Automatic sources dropoff on 2020-06-10 18:32:38.095721 The change is generated with prebuilt drop tool. Change-Id: I24cbf6ba6db262a1ae1445db1427a08fee35b3b4
diff --git a/java/net/AbstractPlainDatagramSocketImpl.java b/java/net/AbstractPlainDatagramSocketImpl.java new file mode 100644 index 0000000..a3c738a --- /dev/null +++ b/java/net/AbstractPlainDatagramSocketImpl.java
@@ -0,0 +1,433 @@ +/* + * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.net; + +import libcore.io.IoBridge; +import libcore.io.IoUtils; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.util.Enumeration; +import java.security.AccessController; + +import dalvik.system.BlockGuard; +import dalvik.system.CloseGuard; +import sun.net.ResourceManager; + +/** + * Abstract datagram and multicast socket implementation base class. + * Note: This is not a public class, so that applets cannot call + * into the implementation directly and hence cannot bypass the + * security checks present in the DatagramSocket and MulticastSocket + * classes. + * + * @author Pavani Diwanji + */ + +abstract class AbstractPlainDatagramSocketImpl extends DatagramSocketImpl +{ + /* timeout value for receive() */ + int timeout = 0; + boolean connected = false; + private int trafficClass = 0; + protected InetAddress connectedAddress = null; + private int connectedPort = -1; + + // Android-added: CloseGuard + private final CloseGuard guard = CloseGuard.get(); + + private static final String os = AccessController.doPrivileged( + new sun.security.action.GetPropertyAction("os.name") + ); + + /** + * flag set if the native connect() call not to be used + */ + private final static boolean connectDisabled = os.contains("OS X"); + + // BEGIN Android-removed: Android doesn't need to load native net library + /** + * Load net library into runtime. + * + static { + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction<Void>() { + public Void run() { + System.loadLibrary("net"); + return null; + } + }); + } + */ + // END Android-removed: Android doesn't need to load native net library + + /** + * Creates a datagram socket + */ + protected synchronized void create() throws SocketException { + ResourceManager.beforeUdpCreate(); + fd = new FileDescriptor(); + try { + datagramSocketCreate(); + } catch (SocketException ioe) { + ResourceManager.afterUdpClose(); + fd = null; + throw ioe; + } + + // Android-added: CloseGuard/fdsan + if (fd != null && fd.valid()) { + guard.open("close"); + IoUtils.setFdOwner(fd, this); + } + } + + /** + * Binds a datagram socket to a local port. + */ + protected synchronized void bind(int lport, InetAddress laddr) + throws SocketException { + bind0(lport, laddr); + } + + protected abstract void bind0(int lport, InetAddress laddr) + throws SocketException; + + /** + * Sends a datagram packet. The packet contains the data and the + * destination address to send the packet to. + * @param p the packet to be sent. + */ + protected abstract void send(DatagramPacket p) throws IOException; + + /** + * Connects a datagram socket to a remote destination. This associates the remote + * address with the local socket so that datagrams may only be sent to this destination + * and received from this destination. + * @param address the remote InetAddress to connect to + * @param port the remote port number + */ + protected void connect(InetAddress address, int port) throws SocketException { + // Android-added: BlockGuard + BlockGuard.getThreadPolicy().onNetwork(); + connect0(address, port); + connectedAddress = address; + connectedPort = port; + connected = true; + } + + /** + * Disconnects a previously connected socket. Does nothing if the socket was + * not connected already. + */ + protected void disconnect() { + disconnect0(connectedAddress.holder().getFamily()); + connected = false; + connectedAddress = null; + connectedPort = -1; + } + + /** + * Peek at the packet to see who it is from. + * @param i the address to populate with the sender address + */ + protected abstract int peek(InetAddress i) throws IOException; + protected abstract int peekData(DatagramPacket p) throws IOException; + /** + * Receive the datagram packet. + * @param p the packet to receive into + */ + protected synchronized void receive(DatagramPacket p) + throws IOException { + receive0(p); + } + + protected abstract void receive0(DatagramPacket p) + throws IOException; + + /** + * Set the TTL (time-to-live) option. + * @param ttl TTL to be set. + */ + protected abstract void setTimeToLive(int ttl) throws IOException; + + /** + * Get the TTL (time-to-live) option. + */ + protected abstract int getTimeToLive() throws IOException; + + /** + * Set the TTL (time-to-live) option. + * @param ttl TTL to be set. + */ + @Deprecated + protected abstract void setTTL(byte ttl) throws IOException; + + /** + * Get the TTL (time-to-live) option. + */ + @Deprecated + protected abstract byte getTTL() throws IOException; + + /** + * Join the multicast group. + * @param inetaddr multicast address to join. + */ + protected void join(InetAddress inetaddr) throws IOException { + join(inetaddr, null); + } + + /** + * Leave the multicast group. + * @param inetaddr multicast address to leave. + */ + protected void leave(InetAddress inetaddr) throws IOException { + leave(inetaddr, null); + } + /** + * Join the multicast group. + * @param mcastaddr multicast address to join. + * @param netIf specifies the local interface to receive multicast + * datagram packets + * @throws IllegalArgumentException if mcastaddr is null or is a + * SocketAddress subclass not supported by this socket + * @since 1.4 + */ + + protected void joinGroup(SocketAddress mcastaddr, NetworkInterface netIf) + throws IOException { + if (mcastaddr == null || !(mcastaddr instanceof InetSocketAddress)) + throw new IllegalArgumentException("Unsupported address type"); + join(((InetSocketAddress)mcastaddr).getAddress(), netIf); + } + + protected abstract void join(InetAddress inetaddr, NetworkInterface netIf) + throws IOException; + + /** + * Leave the multicast group. + * @param mcastaddr multicast address to leave. + * @param netIf specified the local interface to leave the group at + * @throws IllegalArgumentException if mcastaddr is null or is a + * SocketAddress subclass not supported by this socket + * @since 1.4 + */ + protected void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf) + throws IOException { + if (mcastaddr == null || !(mcastaddr instanceof InetSocketAddress)) + throw new IllegalArgumentException("Unsupported address type"); + leave(((InetSocketAddress)mcastaddr).getAddress(), netIf); + } + + protected abstract void leave(InetAddress inetaddr, NetworkInterface netIf) + throws IOException; + + /** + * Close the socket. + */ + protected void close() { + // Android-added: CloseGuard + guard.close(); + + if (fd != null) { + datagramSocketClose(); + ResourceManager.afterUdpClose(); + fd = null; + } + } + + protected boolean isClosed() { + return (fd == null) ? true : false; + } + + protected void finalize() { + // Android-added: CloseGuard + if (guard != null) { + guard.warnIfOpen(); + } + + close(); + } + + /** + * set a value - since we only support (setting) binary options + * here, o must be a Boolean + */ + + public void setOption(int optID, Object o) throws SocketException { + if (isClosed()) { + throw new SocketException("Socket Closed"); + } + switch (optID) { + /* check type safety b4 going native. These should never + * fail, since only java.Socket* has access to + * PlainSocketImpl.setOption(). + */ + case SO_TIMEOUT: + if (o == null || !(o instanceof Integer)) { + throw new SocketException("bad argument for SO_TIMEOUT"); + } + int tmp = ((Integer) o).intValue(); + if (tmp < 0) + throw new IllegalArgumentException("timeout < 0"); + timeout = tmp; + return; + case IP_TOS: + if (o == null || !(o instanceof Integer)) { + throw new SocketException("bad argument for IP_TOS"); + } + trafficClass = ((Integer)o).intValue(); + break; + case SO_REUSEADDR: + if (o == null || !(o instanceof Boolean)) { + throw new SocketException("bad argument for SO_REUSEADDR"); + } + break; + case SO_BROADCAST: + if (o == null || !(o instanceof Boolean)) { + throw new SocketException("bad argument for SO_BROADCAST"); + } + break; + case SO_BINDADDR: + throw new SocketException("Cannot re-bind Socket"); + case SO_RCVBUF: + case SO_SNDBUF: + if (o == null || !(o instanceof Integer) || + ((Integer)o).intValue() < 0) { + throw new SocketException("bad argument for SO_SNDBUF or " + + "SO_RCVBUF"); + } + break; + case IP_MULTICAST_IF: + if (o == null || !(o instanceof InetAddress)) + throw new SocketException("bad argument for IP_MULTICAST_IF"); + break; + case IP_MULTICAST_IF2: + // Android-changed: Support Integer IP_MULTICAST_IF2 values for app compat. b/26790580 + // if (o == null || !(o instanceof NetworkInterface)) + if (o == null || !(o instanceof Integer || o instanceof NetworkInterface)) + throw new SocketException("bad argument for IP_MULTICAST_IF2"); + if (o instanceof NetworkInterface) { + o = new Integer(((NetworkInterface)o).getIndex()); + } + break; + case IP_MULTICAST_LOOP: + if (o == null || !(o instanceof Boolean)) + throw new SocketException("bad argument for IP_MULTICAST_LOOP"); + break; + default: + throw new SocketException("invalid option: " + optID); + } + socketSetOption(optID, o); + } + + /* + * get option's state - set or not + */ + + public Object getOption(int optID) throws SocketException { + if (isClosed()) { + throw new SocketException("Socket Closed"); + } + + Object result; + + switch (optID) { + case SO_TIMEOUT: + result = new Integer(timeout); + break; + + case IP_TOS: + result = socketGetOption(optID); + if ( ((Integer)result).intValue() == -1) { + result = new Integer(trafficClass); + } + break; + + case SO_BINDADDR: + case IP_MULTICAST_IF: + case IP_MULTICAST_IF2: + case SO_RCVBUF: + case SO_SNDBUF: + case IP_MULTICAST_LOOP: + case SO_REUSEADDR: + case SO_BROADCAST: + result = socketGetOption(optID); + // Android-added: Added for app compat reason. See methodgetNIFirstAddress + if (optID == IP_MULTICAST_IF) { + return getNIFirstAddress((Integer)result); + } + break; + + default: + throw new SocketException("invalid option: " + optID); + } + + return result; + } + + // BEGIN Android-added: Support Integer IP_MULTICAST_IF2 values for app compat. b/26790580 + // Native code is changed to return the index of network interface when calling + // getOption(IP_MULTICAST_IF2) due to app compat reason. + // + // For getOption(IP_MULTICAST_IF), we should keep returning InetAddress instance. This method + // convert NetworkInterface index into InetAddress instance. + /** Return the first address bound to NetworkInterface with given ID. + * In case of niIndex == 0 or no address return anyLocalAddress + */ + static InetAddress getNIFirstAddress(int niIndex) throws SocketException { + if (niIndex > 0) { + NetworkInterface networkInterface = NetworkInterface.getByIndex(niIndex); + Enumeration<InetAddress> addressesEnum = networkInterface.getInetAddresses(); + if (addressesEnum.hasMoreElements()) { + return addressesEnum.nextElement(); + } + } + return InetAddress.anyLocalAddress(); + } + // END Android-added: Support Integer IP_MULTICAST_IF2 values for app compat. b/26790580 + + protected abstract void datagramSocketCreate() throws SocketException; + protected abstract void datagramSocketClose(); + protected abstract void socketSetOption(int opt, Object val) + throws SocketException; + protected abstract Object socketGetOption(int opt) throws SocketException; + + protected abstract void connect0(InetAddress address, int port) throws SocketException; + protected abstract void disconnect0(int family); + + protected boolean nativeConnectDisabled() { + return connectDisabled; + } + + // Android-changed: rewritten on the top of IoBridge + int dataAvailable() { + try { + return IoBridge.available(fd); + } catch (IOException e) { + return -1; + } + } +}
diff --git a/java/net/AbstractPlainSocketImpl.java b/java/net/AbstractPlainSocketImpl.java new file mode 100644 index 0000000..0500811 --- /dev/null +++ b/java/net/AbstractPlainSocketImpl.java
@@ -0,0 +1,794 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.FileDescriptor; + +import dalvik.annotation.optimization.ReachabilitySensitive; +import dalvik.system.BlockGuard; +import dalvik.system.CloseGuard; +import dalvik.system.SocketTagger; +import sun.net.ConnectionResetException; +import sun.net.NetHooks; +import sun.net.ResourceManager; + +/** + * Default Socket Implementation. This implementation does + * not implement any security checks. + * Note this class should <b>NOT</b> be public. + * + * @author Steven B. Byrne + */ +abstract class AbstractPlainSocketImpl extends SocketImpl +{ + /* instance variable for SO_TIMEOUT */ + int timeout; // timeout in millisec + // Android-removed: traffic class is set through socket + // private int trafficClass; + + private boolean shut_rd = false; + private boolean shut_wr = false; + + private SocketInputStream socketInputStream = null; + private SocketOutputStream socketOutputStream = null; + + /* number of threads using the FileDescriptor */ + protected int fdUseCount = 0; + + /* lock when increment/decrementing fdUseCount */ + // Android-added: @ReachabilitySensitive + // Marked mostly because it's used where fd is, and fd isn't declared here. + // This adds reachabilityFences where we would if fd were annotated. + @ReachabilitySensitive + protected final Object fdLock = new Object(); + + /* indicates a close is pending on the file descriptor */ + protected boolean closePending = false; + + /* indicates connection reset state */ + private int CONNECTION_NOT_RESET = 0; + private int CONNECTION_RESET_PENDING = 1; + private int CONNECTION_RESET = 2; + private int resetState; + private final Object resetLock = new Object(); + + /* whether this Socket is a stream (TCP) socket or not (UDP) + */ + protected boolean stream; + + // BEGIN Android-removed: Android doesn't need to load native net library + /* + /** + * Load net library into runtime. + * + static { + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction<Void>() { + public Void run() { + System.loadLibrary("net"); + return null; + } + }); + } + */ + // END Android-removed: Android doesn't need to load native net library + + // Android-added: logs a warning if socket is not closed + @ReachabilitySensitive + private final CloseGuard guard = CloseGuard.get(); + + /** + * Creates a socket with a boolean that specifies whether this + * is a stream socket (true) or an unconnected UDP socket (false). + */ + protected synchronized void create(boolean stream) throws IOException { + this.stream = stream; + if (!stream) { + ResourceManager.beforeUdpCreate(); + // Android-removed: socketCreate should set fd if it succeeds + // fd = new FileDescriptor(); + try { + socketCreate(false); + } catch (IOException ioe) { + ResourceManager.afterUdpClose(); + // Android-removed: b/26470377 Represent closed sockets with invalid fd, not null. + // fd = null; + throw ioe; + } + } else { + // Android-removed: socketCreate should set fd if it succeeds + // fd = new FileDescriptor(); + socketCreate(true); + } + if (socket != null) + socket.setCreated(); + if (serverSocket != null) + serverSocket.setCreated(); + + // Android-added: CloseGuard + if (fd != null && fd.valid()) { + guard.open("close"); + } + } + + /** + * Creates a socket and connects it to the specified port on + * the specified host. + * @param host the specified host + * @param port the specified port + */ + protected void connect(String host, int port) + throws UnknownHostException, IOException + { + boolean connected = false; + try { + InetAddress address = InetAddress.getByName(host); + this.port = port; + this.address = address; + + connectToAddress(address, port, timeout); + connected = true; + } finally { + if (!connected) { + try { + close(); + } catch (IOException ioe) { + /* Do nothing. If connect threw an exception then + it will be passed up the call stack */ + } + } + } + } + + /** + * Creates a socket and connects it to the specified address on + * the specified port. + * @param address the address + * @param port the specified port + */ + protected void connect(InetAddress address, int port) throws IOException { + this.port = port; + this.address = address; + + try { + connectToAddress(address, port, timeout); + return; + } catch (IOException e) { + // everything failed + close(); + throw e; + } + } + + /** + * Creates a socket and connects it to the specified address on + * the specified port. + * @param address the address + * @param timeout the timeout value in milliseconds, or zero for no timeout. + * @throws IOException if connection fails + * @throws IllegalArgumentException if address is null or is a + * SocketAddress subclass not supported by this socket + * @since 1.4 + */ + protected void connect(SocketAddress address, int timeout) + throws IOException { + boolean connected = false; + try { + if (address == null || !(address instanceof InetSocketAddress)) + throw new IllegalArgumentException("unsupported address type"); + InetSocketAddress addr = (InetSocketAddress) address; + if (addr.isUnresolved()) + throw new UnknownHostException(addr.getHostName()); + this.port = addr.getPort(); + this.address = addr.getAddress(); + + connectToAddress(this.address, port, timeout); + connected = true; + } finally { + if (!connected) { + try { + close(); + } catch (IOException ioe) { + /* Do nothing. If connect threw an exception then + it will be passed up the call stack */ + } + } + } + } + + private void connectToAddress(InetAddress address, int port, int timeout) throws IOException { + if (address.isAnyLocalAddress()) { + doConnect(InetAddress.getLocalHost(), port, timeout); + } else { + doConnect(address, port, timeout); + } + } + + public void setOption(int opt, Object val) throws SocketException { + if (isClosedOrPending()) { + throw new SocketException("Socket Closed"); + } + // BEGIN Android-removed: Logic dealing with value type moved to socketSetOption. + /* + boolean on = true; + switch (opt) { + /* check type safety b4 going native. These should never + * fail, since only java.Socket* has access to + * PlainSocketImpl.setOption(). + * + case SO_LINGER: + if (val == null || (!(val instanceof Integer) && !(val instanceof Boolean))) + throw new SocketException("Bad parameter for option"); + if (val instanceof Boolean) { + /* true only if disabling - enabling should be Integer * + on = false; + } + break; + case SO_TIMEOUT: + if (val == null || (!(val instanceof Integer))) + throw new SocketException("Bad parameter for SO_TIMEOUT"); + int tmp = ((Integer) val).intValue(); + if (tmp < 0) + throw new IllegalArgumentException("timeout < 0"); + timeout = tmp; + break; + case IP_TOS: + if (val == null || !(val instanceof Integer)) { + throw new SocketException("bad argument for IP_TOS"); + } + trafficClass = ((Integer)val).intValue(); + break; + case SO_BINDADDR: + throw new SocketException("Cannot re-bind socket"); + case TCP_NODELAY: + if (val == null || !(val instanceof Boolean)) + throw new SocketException("bad parameter for TCP_NODELAY"); + on = ((Boolean)val).booleanValue(); + break; + case SO_SNDBUF: + case SO_RCVBUF: + if (val == null || !(val instanceof Integer) || + !(((Integer)val).intValue() > 0)) { + throw new SocketException("bad parameter for SO_SNDBUF " + + "or SO_RCVBUF"); + } + break; + case SO_KEEPALIVE: + if (val == null || !(val instanceof Boolean)) + throw new SocketException("bad parameter for SO_KEEPALIVE"); + on = ((Boolean)val).booleanValue(); + break; + case SO_OOBINLINE: + if (val == null || !(val instanceof Boolean)) + throw new SocketException("bad parameter for SO_OOBINLINE"); + on = ((Boolean)val).booleanValue(); + break; + case SO_REUSEADDR: + if (val == null || !(val instanceof Boolean)) + throw new SocketException("bad parameter for SO_REUSEADDR"); + on = ((Boolean)val).booleanValue(); + break; + default: + throw new SocketException("unrecognized TCP option: " + opt); + } + socketSetOption(opt, on, val); + */ + // END Android-removed: Logic dealing with value type moved to socketSetOption. + // Android-added: Keep track of timeout value not handled by socketSetOption + if (opt == SO_TIMEOUT) { + timeout = (Integer) val; + } + socketSetOption(opt, val); + } + public Object getOption(int opt) throws SocketException { + if (isClosedOrPending()) { + throw new SocketException("Socket Closed"); + } + if (opt == SO_TIMEOUT) { + return new Integer(timeout); + } + // BEGIN Android-changed: Logic dealing with value type moved to socketGetOption. + /* + int ret = 0; + /* + * The native socketGetOption() knows about 3 options. + * The 32 bit value it returns will be interpreted according + * to what we're asking. A return of -1 means it understands + * the option but its turned off. It will raise a SocketException + * if "opt" isn't one it understands. + * + + switch (opt) { + case TCP_NODELAY: + ret = socketGetOption(opt, null); + return Boolean.valueOf(ret != -1); + case SO_OOBINLINE: + ret = socketGetOption(opt, null); + return Boolean.valueOf(ret != -1); + case SO_LINGER: + ret = socketGetOption(opt, null); + return (ret == -1) ? Boolean.FALSE: (Object)(new Integer(ret)); + case SO_REUSEADDR: + ret = socketGetOption(opt, null); + return Boolean.valueOf(ret != -1); + case SO_BINDADDR: + InetAddressContainer in = new InetAddressContainer(); + ret = socketGetOption(opt, in); + return in.addr; + case SO_SNDBUF: + case SO_RCVBUF: + ret = socketGetOption(opt, null); + return new Integer(ret); + case IP_TOS: + try { + ret = socketGetOption(opt, null); + if (ret == -1) { // ipv6 tos + return trafficClass; + } else { + return ret; + } + } catch (SocketException se) { + // TODO - should make better effort to read TOS or TCLASS + return trafficClass; // ipv6 tos + } + case SO_KEEPALIVE: + ret = socketGetOption(opt, null); + return Boolean.valueOf(ret != -1); + // should never get here + default: + return null; + } + */ + return socketGetOption(opt); + // END Android-changed: Logic dealing with value type moved to socketGetOption. + } + + /** + * The workhorse of the connection operation. Tries several times to + * establish a connection to the given <host, port>. If unsuccessful, + * throws an IOException indicating what went wrong. + */ + + synchronized void doConnect(InetAddress address, int port, int timeout) throws IOException { + synchronized (fdLock) { + if (!closePending && (socket == null || !socket.isBound())) { + NetHooks.beforeTcpConnect(fd, address, port); + } + } + try { + acquireFD(); + try { + // Android-added: BlockGuard + BlockGuard.getThreadPolicy().onNetwork(); + socketConnect(address, port, timeout); + /* socket may have been closed during poll/select */ + synchronized (fdLock) { + if (closePending) { + throw new SocketException ("Socket closed"); + } + } + // If we have a ref. to the Socket, then sets the flags + // created, bound & connected to true. + // This is normally done in Socket.connect() but some + // subclasses of Socket may call impl.connect() directly! + if (socket != null) { + socket.setBound(); + socket.setConnected(); + } + } finally { + releaseFD(); + } + } catch (IOException e) { + close(); + throw e; + } + } + + /** + * Binds the socket to the specified address of the specified local port. + * @param address the address + * @param lport the port + */ + protected synchronized void bind(InetAddress address, int lport) + throws IOException + { + synchronized (fdLock) { + if (!closePending && (socket == null || !socket.isBound())) { + NetHooks.beforeTcpBind(fd, address, lport); + } + } + socketBind(address, lport); + if (socket != null) + socket.setBound(); + if (serverSocket != null) + serverSocket.setBound(); + } + + /** + * Listens, for a specified amount of time, for connections. + * @param count the amount of time to listen for connections + */ + protected synchronized void listen(int count) throws IOException { + socketListen(count); + } + + /** + * Accepts connections. + * @param s the connection + */ + protected void accept(SocketImpl s) throws IOException { + acquireFD(); + try { + // Android-added: BlockGuard + BlockGuard.getThreadPolicy().onNetwork(); + socketAccept(s); + } finally { + releaseFD(); + } + } + + /** + * Gets an InputStream for this socket. + */ + protected synchronized InputStream getInputStream() throws IOException { + synchronized (fdLock) { + if (isClosedOrPending()) + throw new IOException("Socket Closed"); + if (shut_rd) + throw new IOException("Socket input is shutdown"); + if (socketInputStream == null) + socketInputStream = new SocketInputStream(this); + } + return socketInputStream; + } + + void setInputStream(SocketInputStream in) { + socketInputStream = in; + } + + /** + * Gets an OutputStream for this socket. + */ + protected synchronized OutputStream getOutputStream() throws IOException { + synchronized (fdLock) { + if (isClosedOrPending()) + throw new IOException("Socket Closed"); + if (shut_wr) + throw new IOException("Socket output is shutdown"); + if (socketOutputStream == null) + socketOutputStream = new SocketOutputStream(this); + } + return socketOutputStream; + } + + // Android-removed: this.fd is maintained by the concrete implementation. + /* + void setFileDescriptor(FileDescriptor fd) { + this.fd = fd; + } + */ + + void setAddress(InetAddress address) { + this.address = address; + } + + void setPort(int port) { + this.port = port; + } + + void setLocalPort(int localport) { + this.localport = localport; + } + + /** + * Returns the number of bytes that can be read without blocking. + */ + protected synchronized int available() throws IOException { + if (isClosedOrPending()) { + throw new IOException("Stream closed."); + } + + /* + * If connection has been reset or shut down for input, then return 0 + * to indicate there are no buffered bytes. + */ + if (isConnectionReset() || shut_rd) { + return 0; + } + + /* + * If no bytes available and we were previously notified + * of a connection reset then we move to the reset state. + * + * If are notified of a connection reset then check + * again if there are bytes buffered on the socket. + */ + int n = 0; + try { + n = socketAvailable(); + if (n == 0 && isConnectionResetPending()) { + setConnectionReset(); + } + } catch (ConnectionResetException exc1) { + setConnectionResetPending(); + try { + n = socketAvailable(); + if (n == 0) { + setConnectionReset(); + } + } catch (ConnectionResetException exc2) { + } + } + return n; + } + + /** + * Closes the socket. + */ + protected void close() throws IOException { + synchronized(fdLock) { + if (fd != null && fd.valid()) { + if (!stream) { + ResourceManager.afterUdpClose(); + } + // Android-changed: + // Socket should be untagged before the preclose. After preclose, + // socket will dup2-ed to marker_fd, therefore, it won't describe the same file. + // If closingPending is true, then the socket has been preclosed. + // + // Also, close the CloseGuard when the #close is called. + if (!closePending) { + closePending = true; + guard.close(); + + if (fdUseCount == 0) { + /* + * We close the FileDescriptor in two-steps - first the + * "pre-close" which closes the socket but doesn't + * release the underlying file descriptor. This operation + * may be lengthy due to untransmitted data and a long + * linger interval. Once the pre-close is done we do the + * actual socket to release the fd. + */ + try { + socketPreClose(); + } finally { + socketClose(); + } + // Android-changed(http://b/26470377): Some Android code doesn't expect file + // descriptor to be null. socketClose invalidates the fd by closing the fd. + // fd = null; + return; + } else { + /* + * If a thread has acquired the fd and a close + * isn't pending then use a deferred close. + * Also decrement fdUseCount to signal the last + * thread that releases the fd to close it. + */ + fdUseCount--; + socketPreClose(); + } + } + } + } + } + + void reset() throws IOException { + if (fd != null && fd.valid()) { + socketClose(); + // Android-changed: Notified the CloseGuard object as the fd has been released. + guard.close(); + } + // Android-removed: b/26470377 Represent closed sockets with invalid fd, not null. + // fd = null; + super.reset(); + } + + + /** + * Shutdown read-half of the socket connection; + */ + protected void shutdownInput() throws IOException { + // Android-changed: b/26470377 Represent closed sockets with invalid fd, not null. + if (fd != null && fd.valid()) { + socketShutdown(SHUT_RD); + if (socketInputStream != null) { + socketInputStream.setEOF(true); + } + shut_rd = true; + } + } + + /** + * Shutdown write-half of the socket connection; + */ + protected void shutdownOutput() throws IOException { + // Android-changed: b/26470377 Represent closed sockets with invalid fd, not null. + if (fd != null && fd.valid()) { + socketShutdown(SHUT_WR); + shut_wr = true; + } + } + + protected boolean supportsUrgentData () { + return true; + } + + protected void sendUrgentData (int data) throws IOException { + // Android-changed: b/26470377 Represent closed sockets with invalid fd, not null. + if (fd == null || !fd.valid()) { + throw new IOException("Socket Closed"); + } + socketSendUrgentData (data); + } + + /** + * Cleans up if the user forgets to close it. + */ + protected void finalize() throws IOException { + // Android-added: CloseGuard + if (guard != null) { + guard.warnIfOpen(); + } + + close(); + } + + /* + * "Acquires" and returns the FileDescriptor for this impl + * + * A corresponding releaseFD is required to "release" the + * FileDescriptor. + */ + FileDescriptor acquireFD() { + synchronized (fdLock) { + fdUseCount++; + return fd; + } + } + + /* + * "Release" the FileDescriptor for this impl. + * + * If the use count goes to -1 then the socket is closed. + */ + void releaseFD() { + synchronized (fdLock) { + fdUseCount--; + if (fdUseCount == -1) { + if (fd != null) { + try { + socketClose(); + } catch (IOException e) { + // Android-removed: b/26470377 Some Android code doesn't expect file + // descriptor to be null. socketClose invalidates the fd by closing the fd. + // } finally { + // fd = null; + } + } + } + } + } + + public boolean isConnectionReset() { + synchronized (resetLock) { + return (resetState == CONNECTION_RESET); + } + } + + public boolean isConnectionResetPending() { + synchronized (resetLock) { + return (resetState == CONNECTION_RESET_PENDING); + } + } + + public void setConnectionReset() { + synchronized (resetLock) { + resetState = CONNECTION_RESET; + } + } + + public void setConnectionResetPending() { + synchronized (resetLock) { + if (resetState == CONNECTION_NOT_RESET) { + resetState = CONNECTION_RESET_PENDING; + } + } + + } + + /* + * Return true if already closed or close is pending + */ + public boolean isClosedOrPending() { + /* + * Lock on fdLock to ensure that we wait if a + * close is in progress. + */ + synchronized (fdLock) { + // Android-changed: b/26470377 Represent closed sockets with invalid fd, not null. + if (closePending || (fd == null) || !fd.valid()) { + return true; + } else { + return false; + } + } + } + + /* + * Return the current value of SO_TIMEOUT + */ + public int getTimeout() { + return timeout; + } + + /* + * "Pre-close" a socket by dup'ing the file descriptor - this enables + * the socket to be closed without releasing the file descriptor. + */ + private void socketPreClose() throws IOException { + socketClose0(true); + } + + /* + * Close the socket (and release the file descriptor). + */ + protected void socketClose() throws IOException { + socketClose0(false); + } + + abstract void socketCreate(boolean isServer) throws IOException; + abstract void socketConnect(InetAddress address, int port, int timeout) + throws IOException; + abstract void socketBind(InetAddress address, int port) + throws IOException; + abstract void socketListen(int count) + throws IOException; + abstract void socketAccept(SocketImpl s) + throws IOException; + abstract int socketAvailable() + throws IOException; + abstract void socketClose0(boolean useDeferredClose) + throws IOException; + abstract void socketShutdown(int howto) + throws IOException; + + // Android-changed: Method signature changed, socket{Get,Set}Option work directly with Object + // values. + abstract void socketSetOption(int cmd, Object value) throws SocketException; + abstract Object socketGetOption(int opt) throws SocketException; + + abstract void socketSendUrgentData(int data) + throws IOException; + + public final static int SHUT_RD = 0; + public final static int SHUT_WR = 1; +}
diff --git a/java/net/AddressCache.java b/java/net/AddressCache.java new file mode 100644 index 0000000..3026923 --- /dev/null +++ b/java/net/AddressCache.java
@@ -0,0 +1,134 @@ +/* + * Copyright (C) 2010 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 java.net; + +import android.compat.annotation.UnsupportedAppUsage; +import libcore.util.BasicLruCache; + +/** + * Implements caching for {@code InetAddress}. We use a unified cache for both positive and negative + * cache entries. + * + * TODO: benchmark and optimize InetAddress until we get to the point where we can just rely on + * the C library level caching. The main thing caching at this level buys us is avoiding repeated + * conversions from 'struct sockaddr's to InetAddress[]. + */ +class AddressCache { + /** + * When the cache contains more entries than this, we start dropping the oldest ones. + * This should be a power of two to avoid wasted space in our custom map. + */ + private static final int MAX_ENTRIES = 16; + + // The TTL for the Java-level cache is short, just 2s. + private static final long TTL_NANOS = 2 * 1000000000L; + + // The actual cache. + @UnsupportedAppUsage + private final BasicLruCache<AddressCacheKey, AddressCacheEntry> cache + = new BasicLruCache<AddressCacheKey, AddressCacheEntry>(MAX_ENTRIES); + + static class AddressCacheKey { + @UnsupportedAppUsage + private final String mHostname; + private final int mNetId; + + AddressCacheKey(String hostname, int netId) { + mHostname = hostname; + mNetId = netId; + } + + @Override public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof AddressCacheKey)) { + return false; + } + AddressCacheKey lhs = (AddressCacheKey) o; + return mHostname.equals(lhs.mHostname) && mNetId == lhs.mNetId; + } + + @Override public int hashCode() { + int result = 17; + result = 31 * result + mNetId; + result = 31 * result + mHostname.hashCode(); + return result; + } + } + + static class AddressCacheEntry { + // Either an InetAddress[] for a positive entry, + // or a String detail message for a negative entry. + @UnsupportedAppUsage + final Object value; + + /** + * The absolute expiry time in nanoseconds. Nanoseconds from System.nanoTime is ideal + * because -- unlike System.currentTimeMillis -- it can never go backwards. + * + * We don't need to worry about overflow with a TTL_NANOS of 2s. + */ + @UnsupportedAppUsage + final long expiryNanos; + + @UnsupportedAppUsage + AddressCacheEntry(Object value) { + this.value = value; + this.expiryNanos = System.nanoTime() + TTL_NANOS; + } + } + + /** + * Removes all entries from the cache. + */ + public void clear() { + cache.evictAll(); + } + + /** + * Returns the cached InetAddress[] for 'hostname' on network 'netId'. Returns null + * if nothing is known about 'hostname'. Returns a String suitable for use as an + * UnknownHostException detail message if 'hostname' is known not to exist. + */ + public Object get(String hostname, int netId) { + AddressCacheEntry entry = cache.get(new AddressCacheKey(hostname, netId)); + // Do we have a valid cache entry? + if (entry != null && entry.expiryNanos >= System.nanoTime()) { + return entry.value; + } + // Either we didn't find anything, or it had expired. + // No need to remove expired entries: the caller will provide a replacement shortly. + return null; + } + + /** + * Associates the given 'addresses' with 'hostname'. The association will expire after a + * certain length of time. + */ + public void put(String hostname, int netId, InetAddress[] addresses) { + cache.put(new AddressCacheKey(hostname, netId), new AddressCacheEntry(addresses)); + } + + /** + * Records that 'hostname' is known not to have any associated addresses. (I.e. insert a + * negative cache entry.) + */ + public void putUnknownHost(String hostname, int netId, String detailMessage) { + cache.put(new AddressCacheKey(hostname, netId), new AddressCacheEntry(detailMessage)); + } +}
diff --git a/java/net/Authenticator.java b/java/net/Authenticator.java new file mode 100644 index 0000000..c88a600 --- /dev/null +++ b/java/net/Authenticator.java
@@ -0,0 +1,425 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +/** + * The class Authenticator represents an object that knows how to obtain + * authentication for a network connection. Usually, it will do this + * by prompting the user for information. + * <p> + * Applications use this class by overriding {@link + * #getPasswordAuthentication()} in a sub-class. This method will + * typically use the various getXXX() accessor methods to get information + * about the entity requesting authentication. It must then acquire a + * username and password either by interacting with the user or through + * some other non-interactive means. The credentials are then returned + * as a {@link PasswordAuthentication} return value. + * <p> + * An instance of this concrete sub-class is then registered + * with the system by calling {@link #setDefault(Authenticator)}. + * When authentication is required, the system will invoke one of the + * requestPasswordAuthentication() methods which in turn will call the + * getPasswordAuthentication() method of the registered object. + * <p> + * All methods that request authentication have a default implementation + * that fails. + * + * @see java.net.Authenticator#setDefault(java.net.Authenticator) + * @see java.net.Authenticator#getPasswordAuthentication() + * + * @author Bill Foote + * @since 1.2 + */ + +// There are no abstract methods, but to be useful the user must +// subclass. +public abstract +class Authenticator { + + // The system-wide authenticator object. See setDefault(). + private static Authenticator theAuthenticator; + + private String requestingHost; + private InetAddress requestingSite; + private int requestingPort; + private String requestingProtocol; + private String requestingPrompt; + private String requestingScheme; + private URL requestingURL; + private RequestorType requestingAuthType; + + /** + * The type of the entity requesting authentication. + * + * @since 1.5 + */ + public enum RequestorType { + /** + * Entity requesting authentication is a HTTP proxy server. + */ + PROXY, + /** + * Entity requesting authentication is a HTTP origin server. + */ + SERVER + } + + private void reset() { + requestingHost = null; + requestingSite = null; + requestingPort = -1; + requestingProtocol = null; + requestingPrompt = null; + requestingScheme = null; + requestingURL = null; + requestingAuthType = RequestorType.SERVER; + } + + + /** + * Sets the authenticator that will be used by the networking code + * when a proxy or an HTTP server asks for authentication. + * <p> + * First, if there is a security manager, its {@code checkPermission} + * method is called with a + * {@code NetPermission("setDefaultAuthenticator")} permission. + * This may result in a java.lang.SecurityException. + * + * @param a The authenticator to be set. If a is {@code null} then + * any previously set authenticator is removed. + * + * @throws SecurityException + * if a security manager exists and its + * {@code checkPermission} method doesn't allow + * setting the default authenticator. + * + * @see SecurityManager#checkPermission + * @see java.net.NetPermission + */ + public synchronized static void setDefault(Authenticator a) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + NetPermission setDefaultPermission + = new NetPermission("setDefaultAuthenticator"); + sm.checkPermission(setDefaultPermission); + } + + theAuthenticator = a; + } + + /** + * Ask the authenticator that has been registered with the system + * for a password. + * <p> + * First, if there is a security manager, its {@code checkPermission} + * method is called with a + * {@code NetPermission("requestPasswordAuthentication")} permission. + * This may result in a java.lang.SecurityException. + * + * @param addr The InetAddress of the site requesting authorization, + * or null if not known. + * @param port the port for the requested connection + * @param protocol The protocol that's requesting the connection + * ({@link java.net.Authenticator#getRequestingProtocol()}) + * @param prompt A prompt string for the user + * @param scheme The authentication scheme + * + * @return The username/password, or null if one can't be gotten. + * + * @throws SecurityException + * if a security manager exists and its + * {@code checkPermission} method doesn't allow + * the password authentication request. + * + * @see SecurityManager#checkPermission + * @see java.net.NetPermission + */ + public static PasswordAuthentication requestPasswordAuthentication( + InetAddress addr, + int port, + String protocol, + String prompt, + String scheme) { + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + NetPermission requestPermission + = new NetPermission("requestPasswordAuthentication"); + sm.checkPermission(requestPermission); + } + + Authenticator a = theAuthenticator; + if (a == null) { + return null; + } else { + synchronized(a) { + a.reset(); + a.requestingSite = addr; + a.requestingPort = port; + a.requestingProtocol = protocol; + a.requestingPrompt = prompt; + a.requestingScheme = scheme; + return a.getPasswordAuthentication(); + } + } + } + + /** + * Ask the authenticator that has been registered with the system + * for a password. This is the preferred method for requesting a password + * because the hostname can be provided in cases where the InetAddress + * is not available. + * <p> + * First, if there is a security manager, its {@code checkPermission} + * method is called with a + * {@code NetPermission("requestPasswordAuthentication")} permission. + * This may result in a java.lang.SecurityException. + * + * @param host The hostname of the site requesting authentication. + * @param addr The InetAddress of the site requesting authentication, + * or null if not known. + * @param port the port for the requested connection. + * @param protocol The protocol that's requesting the connection + * ({@link java.net.Authenticator#getRequestingProtocol()}) + * @param prompt A prompt string for the user which identifies the authentication realm. + * @param scheme The authentication scheme + * + * @return The username/password, or null if one can't be gotten. + * + * @throws SecurityException + * if a security manager exists and its + * {@code checkPermission} method doesn't allow + * the password authentication request. + * + * @see SecurityManager#checkPermission + * @see java.net.NetPermission + * @since 1.4 + */ + public static PasswordAuthentication requestPasswordAuthentication( + String host, + InetAddress addr, + int port, + String protocol, + String prompt, + String scheme) { + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + NetPermission requestPermission + = new NetPermission("requestPasswordAuthentication"); + sm.checkPermission(requestPermission); + } + + Authenticator a = theAuthenticator; + if (a == null) { + return null; + } else { + synchronized(a) { + a.reset(); + a.requestingHost = host; + a.requestingSite = addr; + a.requestingPort = port; + a.requestingProtocol = protocol; + a.requestingPrompt = prompt; + a.requestingScheme = scheme; + return a.getPasswordAuthentication(); + } + } + } + + /** + * Ask the authenticator that has been registered with the system + * for a password. + * <p> + * First, if there is a security manager, its {@code checkPermission} + * method is called with a + * {@code NetPermission("requestPasswordAuthentication")} permission. + * This may result in a java.lang.SecurityException. + * + * @param host The hostname of the site requesting authentication. + * @param addr The InetAddress of the site requesting authorization, + * or null if not known. + * @param port the port for the requested connection + * @param protocol The protocol that's requesting the connection + * ({@link java.net.Authenticator#getRequestingProtocol()}) + * @param prompt A prompt string for the user + * @param scheme The authentication scheme + * @param url The requesting URL that caused the authentication + * @param reqType The type (server or proxy) of the entity requesting + * authentication. + * + * @return The username/password, or null if one can't be gotten. + * + * @throws SecurityException + * if a security manager exists and its + * {@code checkPermission} method doesn't allow + * the password authentication request. + * + * @see SecurityManager#checkPermission + * @see java.net.NetPermission + * + * @since 1.5 + */ + public static PasswordAuthentication requestPasswordAuthentication( + String host, + InetAddress addr, + int port, + String protocol, + String prompt, + String scheme, + URL url, + RequestorType reqType) { + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + NetPermission requestPermission + = new NetPermission("requestPasswordAuthentication"); + sm.checkPermission(requestPermission); + } + + Authenticator a = theAuthenticator; + if (a == null) { + return null; + } else { + synchronized(a) { + a.reset(); + a.requestingHost = host; + a.requestingSite = addr; + a.requestingPort = port; + a.requestingProtocol = protocol; + a.requestingPrompt = prompt; + a.requestingScheme = scheme; + a.requestingURL = url; + a.requestingAuthType = reqType; + return a.getPasswordAuthentication(); + } + } + } + + /** + * Gets the {@code hostname} of the + * site or proxy requesting authentication, or {@code null} + * if not available. + * + * @return the hostname of the connection requiring authentication, or null + * if it's not available. + * @since 1.4 + */ + protected final String getRequestingHost() { + return requestingHost; + } + + /** + * Gets the {@code InetAddress} of the + * site requesting authorization, or {@code null} + * if not available. + * + * @return the InetAddress of the site requesting authorization, or null + * if it's not available. + */ + protected final InetAddress getRequestingSite() { + return requestingSite; + } + + /** + * Gets the port number for the requested connection. + * @return an {@code int} indicating the + * port for the requested connection. + */ + protected final int getRequestingPort() { + return requestingPort; + } + + /** + * Give the protocol that's requesting the connection. Often this + * will be based on a URL, but in a future JDK it could be, for + * example, "SOCKS" for a password-protected SOCKS5 firewall. + * + * @return the protocol, optionally followed by "/version", where + * version is a version number. + * + * @see java.net.URL#getProtocol() + */ + protected final String getRequestingProtocol() { + return requestingProtocol; + } + + /** + * Gets the prompt string given by the requestor. + * + * @return the prompt string given by the requestor (realm for + * http requests) + */ + protected final String getRequestingPrompt() { + return requestingPrompt; + } + + /** + * Gets the scheme of the requestor (the HTTP scheme + * for an HTTP firewall, for example). + * + * @return the scheme of the requestor + * + */ + protected final String getRequestingScheme() { + return requestingScheme; + } + + /** + * Called when password authorization is needed. Subclasses should + * override the default implementation, which returns null. + * @return The PasswordAuthentication collected from the + * user, or null if none is provided. + */ + protected PasswordAuthentication getPasswordAuthentication() { + return null; + } + + /** + * Returns the URL that resulted in this + * request for authentication. + * + * @since 1.5 + * + * @return the requesting URL + * + */ + protected URL getRequestingURL () { + return requestingURL; + } + + /** + * Returns whether the requestor is a Proxy or a Server. + * + * @since 1.5 + * + * @return the authentication type of the requestor + * + */ + protected RequestorType getRequestorType () { + return requestingAuthType; + } +}
diff --git a/java/net/BindException.java b/java/net/BindException.java new file mode 100644 index 0000000..9c2bb48 --- /dev/null +++ b/java/net/BindException.java
@@ -0,0 +1,61 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 1996, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +/** + * Signals that an error occurred while attempting to bind a + * socket to a local address and port. Typically, the port is + * in use, or the requested local address could not be assigned. + * + * @since JDK1.1 + */ + +public class BindException extends SocketException { + private static final long serialVersionUID = -5945005768251722951L; + + /** + * Constructs a new BindException with the specified detail + * message as to why the bind error occurred. + * A detail message is a String that gives a specific + * description of this error. + * @param msg the detail message + */ + public BindException(String msg) { + super(msg); + } + + /** + * Construct a new BindException with no detailed message. + */ + public BindException() {} + + // Android-added: Constructor called by IoBridge + /** @hide */ + public BindException(String msg, Throwable cause) { + super(msg, cause); + } +}
diff --git a/java/net/CacheRequest.java b/java/net/CacheRequest.java new file mode 100644 index 0000000..a6fbbc3 --- /dev/null +++ b/java/net/CacheRequest.java
@@ -0,0 +1,63 @@ +/* + * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.OutputStream; +import java.io.IOException; + +/** + * Represents channels for storing resources in the + * ResponseCache. Instances of such a class provide an + * OutputStream object which is called by protocol handlers to + * store the resource data into the cache, and also an abort() method + * which allows a cache store operation to be interrupted and + * abandoned. If an IOException is encountered while reading the + * response or writing to the cache, the current cache store operation + * will be aborted. + * + * @author Yingxian Wang + * @since 1.5 + */ +public abstract class CacheRequest { + + /** + * Returns an OutputStream to which the response body can be + * written. + * + * @return an OutputStream to which the response body can + * be written + * @throws IOException if an I/O error occurs while + * writing the response body + */ + public abstract OutputStream getBody() throws IOException; + + /** + * Aborts the attempt to cache the response. If an IOException is + * encountered while reading the response or writing to the cache, + * the current cache store operation will be abandoned. + */ + public abstract void abort(); +}
diff --git a/java/net/CacheResponse.java b/java/net/CacheResponse.java new file mode 100644 index 0000000..272b3d9 --- /dev/null +++ b/java/net/CacheResponse.java
@@ -0,0 +1,64 @@ +/* + * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.InputStream; +import java.util.Map; +import java.util.List; +import java.io.IOException; + +/** + * Represent channels for retrieving resources from the + * ResponseCache. Instances of such a class provide an + * InputStream that returns the entity body, and also a + * getHeaders() method which returns the associated response headers. + * + * @author Yingxian Wang + * @since 1.5 + */ +public abstract class CacheResponse { + + /** + * Returns the response headers as a Map. + * + * @return An immutable Map from response header field names to + * lists of field values. The status line has null as its + * field name. + * @throws IOException if an I/O error occurs + * while getting the response headers + */ + public abstract Map<String, List<String>> getHeaders() throws IOException; + + /** + * Returns the response body as an InputStream. + * + * @return an InputStream from which the response body can + * be accessed + * @throws IOException if an I/O error occurs while + * getting the response body + */ + public abstract InputStream getBody() throws IOException; +}
diff --git a/java/net/ConnectException.java b/java/net/ConnectException.java new file mode 100644 index 0000000..ff58643 --- /dev/null +++ b/java/net/ConnectException.java
@@ -0,0 +1,61 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 1996, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +/** + * Signals that an error occurred while attempting to connect a + * socket to a remote address and port. Typically, the connection + * was refused remotely (e.g., no process is listening on the + * remote address/port). + * + * @since JDK1.1 + */ +public class ConnectException extends SocketException { + private static final long serialVersionUID = 3831404271622369215L; + + /** + * Constructs a new ConnectException with the specified detail + * message as to why the connect error occurred. + * A detail message is a String that gives a specific + * description of this error. + * @param msg the detail message + */ + public ConnectException(String msg) { + super(msg); + } + + /** + * Construct a new ConnectException with no detailed message. + */ + public ConnectException() {} + + // Android-added: Constructor called by IoBridge + /** @hide */ + public ConnectException(String msg, Throwable cause) { + super(msg, cause); + } +}
diff --git a/java/net/ContentHandler.java b/java/net/ContentHandler.java new file mode 100644 index 0000000..c658585 --- /dev/null +++ b/java/net/ContentHandler.java
@@ -0,0 +1,111 @@ +/* + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.IOException; + +/** + * The abstract class {@code ContentHandler} is the superclass + * of all classes that read an {@code Object} from a + * {@code URLConnection}. + * <p> + * An application does not generally call the + * {@code getContent} method in this class directly. Instead, an + * application calls the {@code getContent} method in class + * {@code URL} or in {@code URLConnection}. + * The application's content handler factory (an instance of a class that + * implements the interface {@code ContentHandlerFactory} set + * up by a call to {@code setContentHandler}) is + * called with a {@code String} giving the MIME type of the + * object being received on the socket. The factory returns an + * instance of a subclass of {@code ContentHandler}, and its + * {@code getContent} method is called to create the object. + * <p> + * If no content handler could be found, URLConnection will + * look for a content handler in a user-defineable set of places. + * By default it looks in sun.net.www.content, but users can define a + * vertical-bar delimited set of class prefixes to search through in + * addition by defining the java.content.handler.pkgs property. + * The class name must be of the form: + * <pre> + * {package-prefix}.{major}.{minor} + * e.g. + * YoyoDyne.experimental.text.plain + * </pre> + * If the loading of the content handler class would be performed by + * a classloader that is outside of the delegation chain of the caller, + * the JVM will need the RuntimePermission "getClassLoader". + * + * @author James Gosling + * @see java.net.ContentHandler#getContent(java.net.URLConnection) + * @see java.net.ContentHandlerFactory + * @see java.net.URL#getContent() + * @see java.net.URLConnection + * @see java.net.URLConnection#getContent() + * @see java.net.URLConnection#setContentHandlerFactory(java.net.ContentHandlerFactory) + * @since JDK1.0 + */ +abstract public class ContentHandler { + /** + * Given a URL connect stream positioned at the beginning of the + * representation of an object, this method reads that stream and + * creates an object from it. + * + * @param urlc a URL connection. + * @return the object read by the {@code ContentHandler}. + * @exception IOException if an I/O error occurs while reading the object. + */ + abstract public Object getContent(URLConnection urlc) throws IOException; + + /** + * Given a URL connect stream positioned at the beginning of the + * representation of an object, this method reads that stream and + * creates an object that matches one of the types specified. + * + * The default implementation of this method should call getContent() + * and screen the return type for a match of the suggested types. + * + * @param urlc a URL connection. + * @param classes an array of types requested + * @return the object read by the {@code ContentHandler} that is + * the first match of the suggested types. + * null if none of the requested are supported. + * @exception IOException if an I/O error occurs while reading the object. + * @since 1.3 + */ + @SuppressWarnings("rawtypes") + public Object getContent(URLConnection urlc, Class[] classes) throws IOException { + Object obj = getContent(urlc); + + for (int i = 0; i < classes.length; i++) { + if (classes[i].isInstance(obj)) { + return obj; + } + } + return null; + } + +}
diff --git a/java/net/ContentHandlerFactory.java b/java/net/ContentHandlerFactory.java new file mode 100644 index 0000000..64112e3 --- /dev/null +++ b/java/net/ContentHandlerFactory.java
@@ -0,0 +1,54 @@ +/* + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +/** + * This interface defines a factory for content handlers. An + * implementation of this interface should map a MIME type into an + * instance of {@code ContentHandler}. + * <p> + * This interface is used by the {@code URLStreamHandler} class + * to create a {@code ContentHandler} for a MIME type. + * + * @author James Gosling + * @see java.net.ContentHandler + * @see java.net.URLStreamHandler + * @since JDK1.0 + */ +public interface ContentHandlerFactory { + /** + * Creates a new {@code ContentHandler} to read an object from + * a {@code URLStreamHandler}. + * + * @param mimetype the MIME type for which a content handler is desired. + + * @return a new {@code ContentHandler} to read an object from a + * {@code URLStreamHandler}. + * @see java.net.ContentHandler + * @see java.net.URLStreamHandler + */ + ContentHandler createContentHandler(String mimetype); +}
diff --git a/java/net/CookieHandler.java b/java/net/CookieHandler.java new file mode 100644 index 0000000..ef91d00 --- /dev/null +++ b/java/net/CookieHandler.java
@@ -0,0 +1,150 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.util.Map; +import java.util.List; +import java.io.IOException; +import sun.security.util.SecurityConstants; + +/** + * A CookieHandler object provides a callback mechanism to hook up a + * HTTP state management policy implementation into the HTTP protocol + * handler. The HTTP state management mechanism specifies a way to + * create a stateful session with HTTP requests and responses. + * + * <p>A system-wide CookieHandler that to used by the HTTP protocol + * handler can be registered by doing a + * CookieHandler.setDefault(CookieHandler). The currently registered + * CookieHandler can be retrieved by calling + * CookieHandler.getDefault(). + * + * For more information on HTTP state management, see <a + * href="http://www.ietf.org/rfc/rfc2965.txt"><i>RFC 2965: HTTP + * State Management Mechanism</i></a> + * + * @author Yingxian Wang + * @since 1.5 + */ +public abstract class CookieHandler { + /** + * The system-wide cookie handler that will apply cookies to the + * request headers and manage cookies from the response headers. + * + * @see setDefault(CookieHandler) + * @see getDefault() + */ + private static CookieHandler cookieHandler; + + /** + * Gets the system-wide cookie handler. + * + * @return the system-wide cookie handler; A null return means + * there is no system-wide cookie handler currently set. + * @throws SecurityException + * If a security manager has been installed and it denies + * {@link NetPermission}{@code ("getCookieHandler")} + * @see #setDefault(CookieHandler) + */ + public synchronized static CookieHandler getDefault() { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(SecurityConstants.GET_COOKIEHANDLER_PERMISSION); + } + return cookieHandler; + } + + /** + * Sets (or unsets) the system-wide cookie handler. + * + * Note: non-standard http protocol handlers may ignore this setting. + * + * @param cHandler The HTTP cookie handler, or + * {@code null} to unset. + * @throws SecurityException + * If a security manager has been installed and it denies + * {@link NetPermission}{@code ("setCookieHandler")} + * @see #getDefault() + */ + public synchronized static void setDefault(CookieHandler cHandler) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(SecurityConstants.SET_COOKIEHANDLER_PERMISSION); + } + cookieHandler = cHandler; + } + + /** + * Gets all the applicable cookies from a cookie cache for the + * specified uri in the request header. + * + * <P>The {@code URI} passed as an argument specifies the intended use for + * the cookies. In particular the scheme should reflect whether the cookies + * will be sent over http, https or used in another context like javascript. + * The host part should reflect either the destination of the cookies or + * their origin in the case of javascript.</P> + * <P>It is up to the implementation to take into account the {@code URI} and + * the cookies attributes and security settings to determine which ones + * should be returned.</P> + * + * <P>HTTP protocol implementers should make sure that this method is + * called after all request headers related to choosing cookies + * are added, and before the request is sent.</P> + * + * @param uri a {@code URI} representing the intended use for the + * cookies + * @param requestHeaders - a Map from request header + * field names to lists of field values representing + * the current request headers + * @return an immutable map from state management headers, with + * field names "Cookie" or "Cookie2" to a list of + * cookies containing state information + * + * @throws IOException if an I/O error occurs + * @throws IllegalArgumentException if either argument is null + * @see #put(URI, Map) + */ + public abstract Map<String, List<String>> + get(URI uri, Map<String, List<String>> requestHeaders) + throws IOException; + + /** + * Sets all the applicable cookies, examples are response header + * fields that are named Set-Cookie2, present in the response + * headers into a cookie cache. + * + * @param uri a {@code URI} where the cookies come from + * @param responseHeaders an immutable map from field names to + * lists of field values representing the response + * header fields returned + * @throws IOException if an I/O error occurs + * @throws IllegalArgumentException if either argument is null + * @see #get(URI, Map) + */ + public abstract void + put(URI uri, Map<String, List<String>> responseHeaders) + throws IOException; +}
diff --git a/java/net/CookieManager.java b/java/net/CookieManager.java new file mode 100644 index 0000000..dfd3b86 --- /dev/null +++ b/java/net/CookieManager.java
@@ -0,0 +1,491 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.util.Map; +import java.util.List; +import java.util.Collections; +import java.util.Comparator; +import java.io.IOException; +import sun.util.logging.PlatformLogger; + +/** + * CookieManager provides a concrete implementation of {@link CookieHandler}, + * which separates the storage of cookies from the policy surrounding accepting + * and rejecting cookies. A CookieManager is initialized with a {@link CookieStore} + * which manages storage, and a {@link CookiePolicy} object, which makes + * policy decisions on cookie acceptance/rejection. + * + * <p> The HTTP cookie management in java.net package looks like: + * <blockquote> + * <pre>{@code + * use + * CookieHandler <------- HttpURLConnection + * ^ + * | impl + * | use + * CookieManager -------> CookiePolicy + * | use + * |--------> HttpCookie + * | ^ + * | | use + * | use | + * |--------> CookieStore + * ^ + * | impl + * | + * Internal in-memory implementation + * }</pre> + * <ul> + * <li> + * CookieHandler is at the core of cookie management. User can call + * CookieHandler.setDefault to set a concrete CookieHanlder implementation + * to be used. + * </li> + * <li> + * CookiePolicy.shouldAccept will be called by CookieManager.put to see whether + * or not one cookie should be accepted and put into cookie store. User can use + * any of three pre-defined CookiePolicy, namely ACCEPT_ALL, ACCEPT_NONE and + * ACCEPT_ORIGINAL_SERVER, or user can define his own CookiePolicy implementation + * and tell CookieManager to use it. + * </li> + * <li> + * CookieStore is the place where any accepted HTTP cookie is stored in. + * If not specified when created, a CookieManager instance will use an internal + * in-memory implementation. Or user can implements one and tell CookieManager + * to use it. + * </li> + * <li> + * Currently, only CookieStore.add(URI, HttpCookie) and CookieStore.get(URI) + * are used by CookieManager. Others are for completeness and might be needed + * by a more sophisticated CookieStore implementation, e.g. a NetscapeCookieSotre. + * </li> + * </ul> + * </blockquote> + * + * <p>There're various ways user can hook up his own HTTP cookie management behavior, e.g. + * <blockquote> + * <ul> + * <li>Use CookieHandler.setDefault to set a brand new {@link CookieHandler} implementation + * <li>Let CookieManager be the default {@link CookieHandler} implementation, + * but implement user's own {@link CookieStore} and {@link CookiePolicy} + * and tell default CookieManager to use them: + * <blockquote><pre> + * // this should be done at the beginning of an HTTP session + * CookieHandler.setDefault(new CookieManager(new MyCookieStore(), new MyCookiePolicy())); + * </pre></blockquote> + * <li>Let CookieManager be the default {@link CookieHandler} implementation, but + * use customized {@link CookiePolicy}: + * <blockquote><pre> + * // this should be done at the beginning of an HTTP session + * CookieHandler.setDefault(new CookieManager()); + * // this can be done at any point of an HTTP session + * ((CookieManager)CookieHandler.getDefault()).setCookiePolicy(new MyCookiePolicy()); + * </pre></blockquote> + * </ul> + * </blockquote> + * + * <p>The implementation conforms to <a href="http://www.ietf.org/rfc/rfc2965.txt">RFC 2965</a>, section 3.3. + * + * @see CookiePolicy + * @author Edward Wang + * @since 1.6 + */ +public class CookieManager extends CookieHandler +{ + /* ---------------- Fields -------------- */ + + private CookiePolicy policyCallback; + + + private CookieStore cookieJar = null; + + + /* ---------------- Ctors -------------- */ + + /** + * Create a new cookie manager. + * + * <p>This constructor will create new cookie manager with default + * cookie store and accept policy. The effect is same as + * {@code CookieManager(null, null)}. + */ + public CookieManager() { + this(null, null); + } + + + /** + * Create a new cookie manager with specified cookie store and cookie policy. + * + * @param store a {@code CookieStore} to be used by cookie manager. + * if {@code null}, cookie manager will use a default one, + * which is an in-memory CookieStore implementation. + * @param cookiePolicy a {@code CookiePolicy} instance + * to be used by cookie manager as policy callback. + * if {@code null}, ACCEPT_ORIGINAL_SERVER will + * be used. + */ + public CookieManager(CookieStore store, + CookiePolicy cookiePolicy) + { + // use default cookie policy if not specify one + policyCallback = (cookiePolicy == null) ? CookiePolicy.ACCEPT_ORIGINAL_SERVER + : cookiePolicy; + + // if not specify CookieStore to use, use default one + if (store == null) { + cookieJar = new InMemoryCookieStore(); + } else { + cookieJar = store; + } + } + + + /* ---------------- Public operations -------------- */ + + /** + * To set the cookie policy of this cookie manager. + * + * <p> A instance of {@code CookieManager} will have + * cookie policy ACCEPT_ORIGINAL_SERVER by default. Users always + * can call this method to set another cookie policy. + * + * @param cookiePolicy the cookie policy. Can be {@code null}, which + * has no effects on current cookie policy. + */ + public void setCookiePolicy(CookiePolicy cookiePolicy) { + if (cookiePolicy != null) policyCallback = cookiePolicy; + } + + + /** + * To retrieve current cookie store. + * + * @return the cookie store currently used by cookie manager. + */ + public CookieStore getCookieStore() { + return cookieJar; + } + + + public Map<String, List<String>> + get(URI uri, Map<String, List<String>> requestHeaders) + throws IOException + { + // pre-condition check + if (uri == null || requestHeaders == null) { + throw new IllegalArgumentException("Argument is null"); + } + + Map<String, List<String>> cookieMap = + new java.util.HashMap<String, List<String>>(); + // if there's no default CookieStore, no way for us to get any cookie + if (cookieJar == null) + return Collections.unmodifiableMap(cookieMap); + + boolean secureLink = "https".equalsIgnoreCase(uri.getScheme()); + List<HttpCookie> cookies = new java.util.ArrayList<HttpCookie>(); + // BEGIN Android-removed: The logic of converting null path is moved into pathMatches. + /* + String path = uri.getPath(); + if (path == null || path.isEmpty()) { + path = "/"; + } + */ + // END Android-removed: The logic of converting null path is moved into pathMatches. + for (HttpCookie cookie : cookieJar.get(uri)) { + // apply path-matches rule (RFC 2965 sec. 3.3.4) + // and check for the possible "secure" tag (i.e. don't send + // 'secure' cookies over unsecure links) + if (pathMatches(uri, cookie) && + (secureLink || !cookie.getSecure())) { + // BEGIN Android-removed: App compat: b/25897688 InMemoryCookieStore ignores scheme. + /* + if (cookie.isHttpOnly()) { + String s = uri.getScheme(); + if (!"http".equalsIgnoreCase(s) && !"https".equalsIgnoreCase(s)) { + continue; + } + } + */ + // END Android-removed: App compat: b/25897688 InMemoryCookieStore ignores scheme. + + // Let's check the authorize port list if it exists + String ports = cookie.getPortlist(); + if (ports != null && !ports.isEmpty()) { + int port = uri.getPort(); + if (port == -1) { + port = "https".equals(uri.getScheme()) ? 443 : 80; + } + if (isInPortList(ports, port)) { + cookies.add(cookie); + } + } else { + cookies.add(cookie); + } + } + } + // Android-added: b/25897688 A fix to return empty map if cookies list is empty + if (cookies.isEmpty()) { + return Collections.emptyMap(); + } + + // apply sort rule (RFC 2965 sec. 3.3.4) + List<String> cookieHeader = sortByPath(cookies); + + cookieMap.put("Cookie", cookieHeader); + return Collections.unmodifiableMap(cookieMap); + } + + public void + put(URI uri, Map<String, List<String>> responseHeaders) + throws IOException + { + // pre-condition check + if (uri == null || responseHeaders == null) { + throw new IllegalArgumentException("Argument is null"); + } + + + // if there's no default CookieStore, no need to remember any cookie + if (cookieJar == null) + return; + + PlatformLogger logger = PlatformLogger.getLogger("java.net.CookieManager"); + for (String headerKey : responseHeaders.keySet()) { + // RFC 2965 3.2.2, key must be 'Set-Cookie2' + // we also accept 'Set-Cookie' here for backward compatibility + if (headerKey == null + || !(headerKey.equalsIgnoreCase("Set-Cookie2") + || headerKey.equalsIgnoreCase("Set-Cookie") + ) + ) + { + continue; + } + + for (String headerValue : responseHeaders.get(headerKey)) { + try { + List<HttpCookie> cookies; + try { + cookies = HttpCookie.parse(headerValue); + } catch (IllegalArgumentException e) { + // Bogus header, make an empty list and log the error + cookies = java.util.Collections.emptyList(); + if (logger.isLoggable(PlatformLogger.Level.SEVERE)) { + logger.severe("Invalid cookie for " + uri + ": " + headerValue); + } + } + for (HttpCookie cookie : cookies) { + if (cookie.getPath() == null) { + // If no path is specified, then by default + // the path is the directory of the page/doc + String path = uri.getPath(); + if (!path.endsWith("/")) { + int i = path.lastIndexOf("/"); + if (i > 0) { + path = path.substring(0, i + 1); + } else { + path = "/"; + } + } + cookie.setPath(path); + // Android-added: b/25763487 A fix to verify cookie URI before removal + } else { + // Validate existing path + if (!pathMatches(uri, cookie)) { + continue; + } + } + + // As per RFC 2965, section 3.3.1: + // Domain Defaults to the effective request-host. (Note that because + // there is no dot at the beginning of effective request-host, + // the default Domain can only domain-match itself.) + if (cookie.getDomain() == null) { + String host = uri.getHost(); + if (host != null && !host.contains(".")) + host += ".local"; + cookie.setDomain(host); + } + String ports = cookie.getPortlist(); + if (ports != null) { + int port = uri.getPort(); + if (port == -1) { + port = "https".equals(uri.getScheme()) ? 443 : 80; + } + if (ports.isEmpty()) { + // Empty port list means this should be restricted + // to the incoming URI port + cookie.setPortlist("" + port ); + if (shouldAcceptInternal(uri, cookie)) { + cookieJar.add(uri, cookie); + } + } else { + // Only store cookies with a port list + // IF the URI port is in that list, as per + // RFC 2965 section 3.3.2 + if (isInPortList(ports, port) && + shouldAcceptInternal(uri, cookie)) { + cookieJar.add(uri, cookie); + } + } + } else { + if (shouldAcceptInternal(uri, cookie)) { + cookieJar.add(uri, cookie); + } + } + } + } catch (IllegalArgumentException e) { + // invalid set-cookie header string + // no-op + } + } + } + } + + + /* ---------------- Private operations -------------- */ + + // to determine whether or not accept this cookie + private boolean shouldAcceptInternal(URI uri, HttpCookie cookie) { + try { + return policyCallback.shouldAccept(uri, cookie); + } catch (Exception ignored) { // pretect against malicious callback + return false; + } + } + + + static private boolean isInPortList(String lst, int port) { + int i = lst.indexOf(","); + int val = -1; + while (i > 0) { + try { + val = Integer.parseInt(lst.substring(0, i)); + if (val == port) { + return true; + } + } catch (NumberFormatException numberFormatException) { + } + lst = lst.substring(i+1); + i = lst.indexOf(","); + } + if (!lst.isEmpty()) { + try { + val = Integer.parseInt(lst); + if (val == port) { + return true; + } + } catch (NumberFormatException numberFormatException) { + } + } + return false; + } + + // Android-changed: b/25763487 Cookie path matching logic in OpenJDK was wrong + /** + * Return true iff. the path from {@code cookie} matches the path from {@code uri}. + */ + private static boolean pathMatches(URI uri, HttpCookie cookie) { + return normalizePath(uri.getPath()).startsWith(normalizePath(cookie.getPath())); + } + + private static String normalizePath(String path) { + if (path == null) { + path = ""; + } + + if (!path.endsWith("/")) { + path = path + "/"; + } + + return path; + } + + + /* + * sort cookies with respect to their path: those with more specific Path attributes + * precede those with less specific, as defined in RFC 2965 sec. 3.3.4 + */ + private List<String> sortByPath(List<HttpCookie> cookies) { + Collections.sort(cookies, new CookiePathComparator()); + + // BEGIN Android-changed: Netscape cookie spec and RFC 2965 have different format + // of Cookie header; RFC 2965 requires a leading $Version="1" string while Netscape does not + // The workaround here is to add a $Version="1" string in advance + final StringBuilder result = new StringBuilder(); + int minVersion = 1; + for (HttpCookie cookie : cookies) { + if (cookie.getVersion() < minVersion) { + minVersion = cookie.getVersion(); + } + } + + if (minVersion == 1) { + result.append("$Version=\"1\"; "); + } + + for (int i = 0; i < cookies.size(); ++i) { + if (i != 0) { + result.append("; "); + } + + result.append(cookies.get(i).toString()); + } + + List<String> cookieHeader = new java.util.ArrayList<String>(); + cookieHeader.add(result.toString()); + // END Android-changed: Netscape cookie spec and RFC 2965 have different format + return cookieHeader; + } + + + static class CookiePathComparator implements Comparator<HttpCookie> { + public int compare(HttpCookie c1, HttpCookie c2) { + if (c1 == c2) return 0; + if (c1 == null) return -1; + if (c2 == null) return 1; + + // path rule only applies to the cookies with same name + if (!c1.getName().equals(c2.getName())) return 0; + + // Android-changed: normalize before comparison + final String c1Path = normalizePath(c1.getPath()); + final String c2Path = normalizePath(c2.getPath()); + + // those with more specific Path attributes precede those with less specific + if (c1Path.startsWith(c2Path)) + return -1; + else if (c2Path.startsWith(c1Path)) + return 1; + else + return 0; + } + } +}
diff --git a/java/net/CookiePolicy.java b/java/net/CookiePolicy.java new file mode 100644 index 0000000..8094835 --- /dev/null +++ b/java/net/CookiePolicy.java
@@ -0,0 +1,78 @@ +/* + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +/** + * CookiePolicy implementations decide which cookies should be accepted + * and which should be rejected. Three pre-defined policy implementations + * are provided, namely ACCEPT_ALL, ACCEPT_NONE and ACCEPT_ORIGINAL_SERVER. + * + * <p>See RFC 2965 sec. 3.3 and 7 for more detail. + * + * @author Edward Wang + * @since 1.6 + */ +public interface CookiePolicy { + /** + * One pre-defined policy which accepts all cookies. + */ + public static final CookiePolicy ACCEPT_ALL = new CookiePolicy(){ + public boolean shouldAccept(URI uri, HttpCookie cookie) { + return true; + } + }; + + /** + * One pre-defined policy which accepts no cookies. + */ + public static final CookiePolicy ACCEPT_NONE = new CookiePolicy(){ + public boolean shouldAccept(URI uri, HttpCookie cookie) { + return false; + } + }; + + /** + * One pre-defined policy which only accepts cookies from original server. + */ + public static final CookiePolicy ACCEPT_ORIGINAL_SERVER = new CookiePolicy(){ + public boolean shouldAccept(URI uri, HttpCookie cookie) { + if (uri == null || cookie == null) + return false; + return HttpCookie.domainMatches(cookie.getDomain(), uri.getHost()); + } + }; + + + /** + * Will be called to see whether or not this cookie should be accepted. + * + * @param uri the URI to consult accept policy with + * @param cookie the HttpCookie object in question + * @return {@code true} if this cookie should be accepted; + * otherwise, {@code false} + */ + public boolean shouldAccept(URI uri, HttpCookie cookie); +}
diff --git a/java/net/CookieStore.java b/java/net/CookieStore.java new file mode 100644 index 0000000..105cc66 --- /dev/null +++ b/java/net/CookieStore.java
@@ -0,0 +1,129 @@ +/* + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.util.List; +import java.util.Map; + +/** + * A CookieStore object represents a storage for cookie. Can store and retrieve + * cookies. + * + * <p>{@link CookieManager} will call {@code CookieStore.add} to save cookies + * for every incoming HTTP response, and call {@code CookieStore.get} to + * retrieve cookie for every outgoing HTTP request. A CookieStore + * is responsible for removing HttpCookie instances which have expired. + * + * @author Edward Wang + * @since 1.6 + */ +public interface CookieStore { + /** + * Adds one HTTP cookie to the store. This is called for every + * incoming HTTP response. + * + * <p>A cookie to store may or may not be associated with an URI. If it + * is not associated with an URI, the cookie's domain and path attribute + * will indicate where it comes from. If it is associated with an URI and + * its domain and path attribute are not specified, given URI will indicate + * where this cookie comes from. + * + * <p>If a cookie corresponding to the given URI already exists, + * then it is replaced with the new one. + * + * @param uri the uri this cookie associated with. + * if {@code null}, this cookie will not be associated + * with an URI + * @param cookie the cookie to store + * + * @throws NullPointerException if {@code cookie} is {@code null} + * + * @see #get + * + */ + public void add(URI uri, HttpCookie cookie); + + + /** + * Retrieve cookies associated with given URI, or whose domain matches the + * given URI. Only cookies that have not expired are returned. + * This is called for every outgoing HTTP request. + * + * @return an immutable list of HttpCookie, + * return empty list if no cookies match the given URI + * + * @param uri the uri associated with the cookies to be returned + * + * @throws NullPointerException if {@code uri} is {@code null} + * + * @see #add + * + */ + public List<HttpCookie> get(URI uri); + + + /** + * Get all not-expired cookies in cookie store. + * + * @return an immutable list of http cookies; + * return empty list if there's no http cookie in store + */ + public List<HttpCookie> getCookies(); + + + /** + * Get all URIs which identify the cookies in this cookie store. + * + * @return an immutable list of URIs; + * return empty list if no cookie in this cookie store + * is associated with an URI + */ + public List<URI> getURIs(); + + + /** + * Remove a cookie from store. + * + * @param uri the uri this cookie associated with. + * if {@code null}, the cookie to be removed is not associated + * with an URI when added; if not {@code null}, the cookie + * to be removed is associated with the given URI when added. + * @param cookie the cookie to remove + * + * @return {@code true} if this store contained the specified cookie + * + * @throws NullPointerException if {@code cookie} is {@code null} + */ + public boolean remove(URI uri, HttpCookie cookie); + + + /** + * Remove all cookies in this cookie store. + * + * @return {@code true} if this store changed as a result of the call + */ + public boolean removeAll(); +}
diff --git a/java/net/DatagramPacket.java b/java/net/DatagramPacket.java new file mode 100644 index 0000000..c1bd692 --- /dev/null +++ b/java/net/DatagramPacket.java
@@ -0,0 +1,415 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +/** + * This class represents a datagram packet. + * <p> + * Datagram packets are used to implement a connectionless packet + * delivery service. Each message is routed from one machine to + * another based solely on information contained within that packet. + * Multiple packets sent from one machine to another might be routed + * differently, and might arrive in any order. Packet delivery is + * not guaranteed. + * + * @author Pavani Diwanji + * @author Benjamin Renaud + * @since JDK1.0 + */ +public final +class DatagramPacket { + + // BEGIN Android-removed: Android doesn't need to load native net library. + /** + * Perform class initialization + * + static { + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction<Void>() { + public Void run() { + System.loadLibrary("net"); + return null; + } + }); + init(); + } + */ + // END Android-removed: Android doesn't need to load native net library. + + /* + * The fields of this class are package-private since DatagramSocketImpl + * classes needs to access them. + */ + byte[] buf; + int offset; + int length; + int bufLength; + InetAddress address; + int port; + + /** + * Constructs a {@code DatagramPacket} for receiving packets of + * length {@code length}, specifying an offset into the buffer. + * <p> + * The {@code length} argument must be less than or equal to + * {@code buf.length}. + * + * @param buf buffer for holding the incoming datagram. + * @param offset the offset for the buffer + * @param length the number of bytes to read. + * + * @since 1.2 + */ + public DatagramPacket(byte buf[], int offset, int length) { + setData(buf, offset, length); + this.address = null; + this.port = -1; + } + + /** + * Constructs a {@code DatagramPacket} for receiving packets of + * length {@code length}. + * <p> + * The {@code length} argument must be less than or equal to + * {@code buf.length}. + * + * @param buf buffer for holding the incoming datagram. + * @param length the number of bytes to read. + */ + public DatagramPacket(byte buf[], int length) { + this (buf, 0, length); + } + + /** + * Constructs a datagram packet for sending packets of length + * {@code length} with offset {@code ioffset}to the + * specified port number on the specified host. The + * {@code length} argument must be less than or equal to + * {@code buf.length}. + * + * @param buf the packet data. + * @param offset the packet data offset. + * @param length the packet data length. + * @param address the destination address. + * @param port the destination port number. + * @see java.net.InetAddress + * + * @since 1.2 + */ + public DatagramPacket(byte buf[], int offset, int length, + InetAddress address, int port) { + setData(buf, offset, length); + setAddress(address); + setPort(port); + } + + // Android-changed: Added Android-specific notes regarding the exception signature change. + /** + * Constructs a datagram packet for sending packets of length + * {@code length} with offset {@code ioffset}to the + * specified port number on the specified host. The + * {@code length} argument must be less than or equal to + * {@code buf.length}. + * + * <p> + * <em>Android note</em>: Up to and including API 25 this method declared that a SocketException + * can be thrown, although the exception is never thrown. Code compiled against a newer SDK does + * not need to catch the exception and will be binary compatible with older versions of Android. + * + * @param buf the packet data. + * @param offset the packet data offset. + * @param length the packet data length. + * @param address the destination socket address. + * @throws IllegalArgumentException if address type is not supported + * @see java.net.InetAddress + * + * @since 1.4 + */ + public DatagramPacket(byte buf[], int offset, int length, SocketAddress address) { + setData(buf, offset, length); + setSocketAddress(address); + } + + // Android-changed: Added Android-specific notes regarding the exception signature change. + /** + * Constructs a datagram packet for sending packets of length + * {@code length} to the specified port number on the specified + * host. The {@code length} argument must be less than or equal + * to {@code buf.length}. + * + * <p> + * <em>Android note</em>: Up to and including API 25 this method declared that a SocketException + * can be thrown, although the exception is never thrown. Code compiled against a newer SDK does + * not need to catch the exception and will be binary compatible with older versions of Android. + * + * @param buf the packet data. + * @param length the packet length. + * @param address the destination address. + * @param port the destination port number. + * @see java.net.InetAddress + */ + public DatagramPacket(byte buf[], int length, + InetAddress address, int port) { + this(buf, 0, length, address, port); + } + + /** + * Constructs a datagram packet for sending packets of length + * {@code length} to the specified port number on the specified + * host. The {@code length} argument must be less than or equal + * to {@code buf.length}. + * + * @param buf the packet data. + * @param length the packet length. + * @param address the destination address. + * @throws IllegalArgumentException if address type is not supported + * @since 1.4 + * @see java.net.InetAddress + */ + public DatagramPacket(byte buf[], int length, SocketAddress address) { + this(buf, 0, length, address); + } + + /** + * Returns the IP address of the machine to which this datagram is being + * sent or from which the datagram was received. + * + * @return the IP address of the machine to which this datagram is being + * sent or from which the datagram was received. + * @see java.net.InetAddress + * @see #setAddress(java.net.InetAddress) + */ + public synchronized InetAddress getAddress() { + return address; + } + + /** + * Returns the port number on the remote host to which this datagram is + * being sent or from which the datagram was received. + * + * @return the port number on the remote host to which this datagram is + * being sent or from which the datagram was received. + * @see #setPort(int) + */ + public synchronized int getPort() { + return port; + } + + /** + * Returns the data buffer. The data received or the data to be sent + * starts from the {@code offset} in the buffer, + * and runs for {@code length} long. + * + * @return the buffer used to receive or send data + * @see #setData(byte[], int, int) + */ + public synchronized byte[] getData() { + return buf; + } + + /** + * Returns the offset of the data to be sent or the offset of the + * data received. + * + * @return the offset of the data to be sent or the offset of the + * data received. + * + * @since 1.2 + */ + public synchronized int getOffset() { + return offset; + } + + /** + * Returns the length of the data to be sent or the length of the + * data received. + * + * @return the length of the data to be sent or the length of the + * data received. + * @see #setLength(int) + */ + public synchronized int getLength() { + return length; + } + + /** + * Set the data buffer for this packet. This sets the + * data, length and offset of the packet. + * + * @param buf the buffer to set for this packet + * + * @param offset the offset into the data + * + * @param length the length of the data + * and/or the length of the buffer used to receive data + * + * @exception NullPointerException if the argument is null + * + * @see #getData + * @see #getOffset + * @see #getLength + * + * @since 1.2 + */ + public synchronized void setData(byte[] buf, int offset, int length) { + /* this will check to see if buf is null */ + if (length < 0 || offset < 0 || + (length + offset) < 0 || + ((length + offset) > buf.length)) { + throw new IllegalArgumentException("illegal length or offset"); + } + this.buf = buf; + this.length = length; + this.bufLength = length; + this.offset = offset; + } + + /** + * Sets the IP address of the machine to which this datagram + * is being sent. + * @param iaddr the {@code InetAddress} + * @since JDK1.1 + * @see #getAddress() + */ + public synchronized void setAddress(InetAddress iaddr) { + address = iaddr; + } + + // BEGIN Android-changed + /** + * Sets 'length' without changing 'userSuppliedLength', after receiving a packet. + * @hide for IoBridge + */ + public void setReceivedLength(int length) { + this.length = length; + } + // END Android-changed + + /** + * Sets the port number on the remote host to which this datagram + * is being sent. + * @param iport the port number + * @since JDK1.1 + * @see #getPort() + */ + public synchronized void setPort(int iport) { + if (iport < 0 || iport > 0xFFFF) { + throw new IllegalArgumentException("Port out of range:"+ iport); + } + port = iport; + } + + /** + * Sets the SocketAddress (usually IP address + port number) of the remote + * host to which this datagram is being sent. + * + * @param address the {@code SocketAddress} + * @throws IllegalArgumentException if address is null or is a + * SocketAddress subclass not supported by this socket + * + * @since 1.4 + * @see #getSocketAddress + */ + public synchronized void setSocketAddress(SocketAddress address) { + if (address == null || !(address instanceof InetSocketAddress)) + throw new IllegalArgumentException("unsupported address type"); + InetSocketAddress addr = (InetSocketAddress) address; + if (addr.isUnresolved()) + throw new IllegalArgumentException("unresolved address"); + setAddress(addr.getAddress()); + setPort(addr.getPort()); + } + + /** + * Gets the SocketAddress (usually IP address + port number) of the remote + * host that this packet is being sent to or is coming from. + * + * @return the {@code SocketAddress} + * @since 1.4 + * @see #setSocketAddress + */ + public synchronized SocketAddress getSocketAddress() { + return new InetSocketAddress(getAddress(), getPort()); + } + + /** + * Set the data buffer for this packet. With the offset of + * this DatagramPacket set to 0, and the length set to + * the length of {@code buf}. + * + * @param buf the buffer to set for this packet. + * + * @exception NullPointerException if the argument is null. + * + * @see #getLength + * @see #getData + * + * @since JDK1.1 + */ + public synchronized void setData(byte[] buf) { + if (buf == null) { + throw new NullPointerException("null packet buffer"); + } + this.buf = buf; + this.offset = 0; + this.length = buf.length; + this.bufLength = buf.length; + } + + /** + * Set the length for this packet. The length of the packet is + * the number of bytes from the packet's data buffer that will be + * sent, or the number of bytes of the packet's data buffer that + * will be used for receiving data. The length must be lesser or + * equal to the offset plus the length of the packet's buffer. + * + * @param length the length to set for this packet. + * + * @exception IllegalArgumentException if the length is negative + * of if the length is greater than the packet's data buffer + * length. + * + * @see #getLength + * @see #setData + * + * @since JDK1.1 + */ + public synchronized void setLength(int length) { + if ((length + offset) > buf.length || length < 0 || + (length + offset) < 0) { + throw new IllegalArgumentException("illegal length"); + } + this.length = length; + this.bufLength = this.length; + } + + // Android-removed: JNI has been removed + // /** + // * Perform class load-time initializations. + // */ + // private native static void init(); +}
diff --git a/java/net/DatagramSocket.annotated.java b/java/net/DatagramSocket.annotated.java new file mode 100644 index 0000000..b462769 --- /dev/null +++ b/java/net/DatagramSocket.annotated.java
@@ -0,0 +1,109 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package java.net; + +import java.nio.channels.DatagramChannel; +import java.io.IOException; + +@SuppressWarnings({"unchecked", "deprecation", "all"}) +public class DatagramSocket implements java.io.Closeable { + +public DatagramSocket() throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +protected DatagramSocket(java.net.DatagramSocketImpl impl) { throw new RuntimeException("Stub!"); } + +public DatagramSocket(java.net.SocketAddress bindaddr) throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public DatagramSocket(int port) throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public DatagramSocket(int port, java.net.InetAddress laddr) throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public synchronized void bind(java.net.SocketAddress addr) throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public void connect(java.net.InetAddress address, int port) { throw new RuntimeException("Stub!"); } + +public void connect(java.net.SocketAddress addr) throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public void disconnect() { throw new RuntimeException("Stub!"); } + +public boolean isBound() { throw new RuntimeException("Stub!"); } + +public boolean isConnected() { throw new RuntimeException("Stub!"); } + +public java.net.InetAddress getInetAddress() { throw new RuntimeException("Stub!"); } + +public int getPort() { throw new RuntimeException("Stub!"); } + +public java.net.SocketAddress getRemoteSocketAddress() { throw new RuntimeException("Stub!"); } + +public java.net.SocketAddress getLocalSocketAddress() { throw new RuntimeException("Stub!"); } + +public void send(java.net.DatagramPacket p) throws java.io.IOException { throw new RuntimeException("Stub!"); } + +public synchronized void receive(java.net.DatagramPacket p) throws java.io.IOException { throw new RuntimeException("Stub!"); } + +public java.net.InetAddress getLocalAddress() { throw new RuntimeException("Stub!"); } + +public int getLocalPort() { throw new RuntimeException("Stub!"); } + +public synchronized void setSoTimeout(int timeout) throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public synchronized int getSoTimeout() throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public synchronized void setSendBufferSize(int size) throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public synchronized int getSendBufferSize() throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public synchronized void setReceiveBufferSize(int size) throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public synchronized int getReceiveBufferSize() throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public synchronized void setReuseAddress(boolean on) throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public synchronized boolean getReuseAddress() throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public synchronized void setBroadcast(boolean on) throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public synchronized boolean getBroadcast() throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public synchronized void setTrafficClass(int tc) throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public synchronized int getTrafficClass() throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public void close() { throw new RuntimeException("Stub!"); } + +public boolean isClosed() { throw new RuntimeException("Stub!"); } + +public java.nio.channels.DatagramChannel getChannel() { throw new RuntimeException("Stub!"); } + +public static synchronized void setDatagramSocketImplFactory(java.net.DatagramSocketImplFactory fac) throws java.io.IOException { throw new RuntimeException("Stub!"); } + [email protected] +public java.io.FileDescriptor getFileDescriptor$() { throw new RuntimeException("Stub!"); } +} +
diff --git a/java/net/DatagramSocket.java b/java/net/DatagramSocket.java new file mode 100644 index 0000000..3e3faf7 --- /dev/null +++ b/java/net/DatagramSocket.java
@@ -0,0 +1,1358 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import static android.system.OsConstants.SOL_SOCKET; +import static android.system.OsConstants.SO_BINDTODEVICE; + +import android.system.ErrnoException; +import java.io.FileDescriptor; +import java.io.IOException; +import java.nio.channels.DatagramChannel; +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; +import libcore.io.Libcore; + +/** + * This class represents a socket for sending and receiving datagram packets. + * + * <p>A datagram socket is the sending or receiving point for a packet + * delivery service. Each packet sent or received on a datagram socket + * is individually addressed and routed. Multiple packets sent from + * one machine to another may be routed differently, and may arrive in + * any order. + * + * <p> Where possible, a newly constructed {@code DatagramSocket} has the + * {@link SocketOptions#SO_BROADCAST SO_BROADCAST} socket option enabled so as + * to allow the transmission of broadcast datagrams. In order to receive + * broadcast packets a DatagramSocket should be bound to the wildcard address. + * In some implementations, broadcast packets may also be received when + * a DatagramSocket is bound to a more specific address. + * <p> + * Example: + * {@code + * DatagramSocket s = new DatagramSocket(null); + * s.bind(new InetSocketAddress(8888)); + * } + * Which is equivalent to: + * {@code + * DatagramSocket s = new DatagramSocket(8888); + * } + * Both cases will create a DatagramSocket able to receive broadcasts on + * UDP port 8888. + * + * @author Pavani Diwanji + * @see java.net.DatagramPacket + * @see java.nio.channels.DatagramChannel + * @since JDK1.0 + */ +public +class DatagramSocket implements java.io.Closeable { + /** + * Various states of this socket. + */ + private boolean created = false; + private boolean bound = false; + private boolean closed = false; + private Object closeLock = new Object(); + + /* + * The implementation of this DatagramSocket. + */ + DatagramSocketImpl impl; + + /** + * Are we using an older DatagramSocketImpl? + */ + boolean oldImpl = false; + + /** + * Set when a socket is ST_CONNECTED until we are certain + * that any packets which might have been received prior + * to calling connect() but not read by the application + * have been read. During this time we check the source + * address of all packets received to be sure they are from + * the connected destination. Other packets are read but + * silently dropped. + */ + private boolean explicitFilter = false; + private int bytesLeftToFilter; + /* + * Connection state: + * ST_NOT_CONNECTED = socket not connected + * ST_CONNECTED = socket connected + * ST_CONNECTED_NO_IMPL = socket connected but not at impl level + */ + static final int ST_NOT_CONNECTED = 0; + static final int ST_CONNECTED = 1; + static final int ST_CONNECTED_NO_IMPL = 2; + + int connectState = ST_NOT_CONNECTED; + + /* + * Connected address & port + */ + InetAddress connectedAddress = null; + int connectedPort = -1; + + // Android-added: Store pending exception from connect + private SocketException pendingConnectException; + + /** + * Connects this socket to a remote socket address (IP address + port number). + * Binds socket if not already bound. + * <p> + * @param address The remote address. + * @param port The remote port + * @throws SocketException if binding the socket fails. + */ + private synchronized void connectInternal(InetAddress address, int port) throws SocketException { + if (port < 0 || port > 0xFFFF) { + throw new IllegalArgumentException("connect: " + port); + } + if (address == null) { + throw new IllegalArgumentException("connect: null address"); + } + checkAddress (address, "connect"); + if (isClosed()) + return; + SecurityManager security = System.getSecurityManager(); + if (security != null) { + if (address.isMulticastAddress()) { + security.checkMulticast(address); + } else { + security.checkConnect(address.getHostAddress(), port); + security.checkAccept(address.getHostAddress(), port); + } + } + + if (!isBound()) + bind(new InetSocketAddress(0)); + + // Android-changed: This section now throws any SocketException generated by connect() + // to enable it to be recorded as the pendingConnectException. It has been enclosed in a + // try-finally to ensure connectedAddress and connectedPort are set when the exception + // is thrown. + try { + // old impls do not support connect/disconnect + // Android-changed: Added special handling for AbstractPlainDatagramSocketImpl in + // the condition below. + if (oldImpl || (impl instanceof AbstractPlainDatagramSocketImpl && + ((AbstractPlainDatagramSocketImpl)impl).nativeConnectDisabled())) { + connectState = ST_CONNECTED_NO_IMPL; + } else { + try { + getImpl().connect(address, port); + + // socket is now connected by the impl + connectState = ST_CONNECTED; + + // Do we need to filter some packets? + int avail = getImpl().dataAvailable(); + if (avail == -1) { + throw new SocketException(); + } + explicitFilter = avail > 0; + if (explicitFilter) { + bytesLeftToFilter = getReceiveBufferSize(); + } + } catch (SocketException se) { + // connection will be emulated by DatagramSocket + connectState = ST_CONNECTED_NO_IMPL; + // Android-changed: Propagate the SocketException so connect() can store it. + throw se; + } + } + } finally { + connectedAddress = address; + connectedPort = port; + } + } + + + /** + * Constructs a datagram socket and binds it to any available port + * on the local host machine. The socket will be bound to the + * {@link InetAddress#isAnyLocalAddress wildcard} address, + * an IP address chosen by the kernel. + * + * <p>If there is a security manager, + * its {@code checkListen} method is first called + * with 0 as its argument to ensure the operation is allowed. + * This could result in a SecurityException. + * + * @exception SocketException if the socket could not be opened, + * or the socket could not bind to the specified local port. + * @exception SecurityException if a security manager exists and its + * {@code checkListen} method doesn't allow the operation. + * + * @see SecurityManager#checkListen + */ + public DatagramSocket() throws SocketException { + this(new InetSocketAddress(0)); + } + + /** + * Creates an unbound datagram socket with the specified + * DatagramSocketImpl. + * + * @param impl an instance of a <B>DatagramSocketImpl</B> + * the subclass wishes to use on the DatagramSocket. + * @since 1.4 + */ + protected DatagramSocket(DatagramSocketImpl impl) { + if (impl == null) + throw new NullPointerException(); + this.impl = impl; + checkOldImpl(); + } + + /** + * Creates a datagram socket, bound to the specified local + * socket address. + * <p> + * If, if the address is {@code null}, creates an unbound socket. + * + * <p>If there is a security manager, + * its {@code checkListen} method is first called + * with the port from the socket address + * as its argument to ensure the operation is allowed. + * This could result in a SecurityException. + * + * @param bindaddr local socket address to bind, or {@code null} + * for an unbound socket. + * + * @exception SocketException if the socket could not be opened, + * or the socket could not bind to the specified local port. + * @exception SecurityException if a security manager exists and its + * {@code checkListen} method doesn't allow the operation. + * + * @see SecurityManager#checkListen + * @since 1.4 + */ + public DatagramSocket(SocketAddress bindaddr) throws SocketException { + // create a datagram socket. + createImpl(); + if (bindaddr != null) { + try { + bind(bindaddr); + } finally { + if (!isBound()) + close(); + } + } + } + + /** + * Constructs a datagram socket and binds it to the specified port + * on the local host machine. The socket will be bound to the + * {@link InetAddress#isAnyLocalAddress wildcard} address, + * an IP address chosen by the kernel. + * + * <p>If there is a security manager, + * its {@code checkListen} method is first called + * with the {@code port} argument + * as its argument to ensure the operation is allowed. + * This could result in a SecurityException. + * + * @param port port to use. + * @exception SocketException if the socket could not be opened, + * or the socket could not bind to the specified local port. + * @exception SecurityException if a security manager exists and its + * {@code checkListen} method doesn't allow the operation. + * + * @see SecurityManager#checkListen + */ + public DatagramSocket(int port) throws SocketException { + this(port, null); + } + + /** + * Creates a datagram socket, bound to the specified local + * address. The local port must be between 0 and 65535 inclusive. + * If the IP address is 0.0.0.0, the socket will be bound to the + * {@link InetAddress#isAnyLocalAddress wildcard} address, + * an IP address chosen by the kernel. + * + * <p>If there is a security manager, + * its {@code checkListen} method is first called + * with the {@code port} argument + * as its argument to ensure the operation is allowed. + * This could result in a SecurityException. + * + * @param port local port to use + * @param laddr local address to bind + * + * @exception SocketException if the socket could not be opened, + * or the socket could not bind to the specified local port. + * @exception SecurityException if a security manager exists and its + * {@code checkListen} method doesn't allow the operation. + * + * @see SecurityManager#checkListen + * @since JDK1.1 + */ + public DatagramSocket(int port, InetAddress laddr) throws SocketException { + this(new InetSocketAddress(laddr, port)); + } + + private void checkOldImpl() { + if (impl == null) + return; + // DatagramSocketImpl.peekdata() is a protected method, therefore we need to use + // getDeclaredMethod, therefore we need permission to access the member + try { + AccessController.doPrivileged( + new PrivilegedExceptionAction<Void>() { + public Void run() throws NoSuchMethodException { + Class<?>[] cl = new Class<?>[1]; + cl[0] = DatagramPacket.class; + impl.getClass().getDeclaredMethod("peekData", cl); + return null; + } + }); + } catch (java.security.PrivilegedActionException e) { + oldImpl = true; + } + } + + static Class<?> implClass = null; + + void createImpl() throws SocketException { + if (impl == null) { + if (factory != null) { + impl = factory.createDatagramSocketImpl(); + checkOldImpl(); + } else { + boolean isMulticast = (this instanceof MulticastSocket) ? true : false; + impl = DefaultDatagramSocketImplFactory.createDatagramSocketImpl(isMulticast); + + checkOldImpl(); + } + } + // creates a udp socket + impl.create(); + impl.setDatagramSocket(this); + created = true; + } + + /** + * Get the {@code DatagramSocketImpl} attached to this socket, + * creating it if necessary. + * + * @return the {@code DatagramSocketImpl} attached to that + * DatagramSocket + * @throws SocketException if creation fails. + * @since 1.4 + */ + DatagramSocketImpl getImpl() throws SocketException { + if (!created) + createImpl(); + return impl; + } + + /** + * Binds this DatagramSocket to a specific address and port. + * <p> + * If the address is {@code null}, then the system will pick up + * an ephemeral port and a valid local address to bind the socket. + *<p> + * @param addr The address and port to bind to. + * @throws SocketException if any error happens during the bind, or if the + * socket is already bound. + * @throws SecurityException if a security manager exists and its + * {@code checkListen} method doesn't allow the operation. + * @throws IllegalArgumentException if addr is a SocketAddress subclass + * not supported by this socket. + * @since 1.4 + */ + public synchronized void bind(SocketAddress addr) throws SocketException { + if (isClosed()) + throw new SocketException("Socket is closed"); + if (isBound()) + throw new SocketException("already bound"); + if (addr == null) + addr = new InetSocketAddress(0); + if (!(addr instanceof InetSocketAddress)) + throw new IllegalArgumentException("Unsupported address type!"); + InetSocketAddress epoint = (InetSocketAddress) addr; + if (epoint.isUnresolved()) + throw new SocketException("Unresolved address"); + InetAddress iaddr = epoint.getAddress(); + int port = epoint.getPort(); + checkAddress(iaddr, "bind"); + SecurityManager sec = System.getSecurityManager(); + if (sec != null) { + sec.checkListen(port); + } + try { + getImpl().bind(port, iaddr); + } catch (SocketException e) { + getImpl().close(); + throw e; + } + bound = true; + } + + void checkAddress (InetAddress addr, String op) { + if (addr == null) { + return; + } + if (!(addr instanceof Inet4Address || addr instanceof Inet6Address)) { + throw new IllegalArgumentException(op + ": invalid address type"); + } + } + + /** + * Connects the socket to a remote address for this socket. When a + * socket is connected to a remote address, packets may only be + * sent to or received from that address. By default a datagram + * socket is not connected. + * + * <p>If the remote destination to which the socket is connected does not + * exist, or is otherwise unreachable, and if an ICMP destination unreachable + * packet has been received for that address, then a subsequent call to + * send or receive may throw a PortUnreachableException. Note, there is no + * guarantee that the exception will be thrown. + * + * <p> If a security manager has been installed then it is invoked to check + * access to the remote address. Specifically, if the given {@code address} + * is a {@link InetAddress#isMulticastAddress multicast address}, + * the security manager's {@link + * java.lang.SecurityManager#checkMulticast(InetAddress) + * checkMulticast} method is invoked with the given {@code address}. + * Otherwise, the security manager's {@link + * java.lang.SecurityManager#checkConnect(String,int) checkConnect} + * and {@link java.lang.SecurityManager#checkAccept checkAccept} methods + * are invoked, with the given {@code address} and {@code port}, to + * verify that datagrams are permitted to be sent and received + * respectively. + * + * <p> When a socket is connected, {@link #receive receive} and + * {@link #send send} <b>will not perform any security checks</b> + * on incoming and outgoing packets, other than matching the packet's + * and the socket's address and port. On a send operation, if the + * packet's address is set and the packet's address and the socket's + * address do not match, an {@code IllegalArgumentException} will be + * thrown. A socket connected to a multicast address may only be used + * to send packets. + * + * @param address the remote address for the socket + * + * @param port the remote port for the socket. + * + * @throws IllegalArgumentException + * if the address is null, or the port is out of range. + * + * @throws SecurityException + * if a security manager has been installed and it does + * not permit access to the given remote address + * + * @see #disconnect + */ + public void connect(InetAddress address, int port) { + try { + connectInternal(address, port); + } catch (SocketException se) { + // Android-changed: this method can't throw checked SocketException. Throw it later + // throw new Error("connect failed", se); + // TODO: or just use SneakyThrow? There's a clear API bug here. + pendingConnectException = se; + } + } + + /** + * Connects this socket to a remote socket address (IP address + port number). + * + * <p> If given an {@link InetSocketAddress InetSocketAddress}, this method + * behaves as if invoking {@link #connect(InetAddress,int) connect(InetAddress,int)} + * with the the given socket addresses IP address and port number. + * + * @param addr The remote address. + * + * @throws SocketException + * if the connect fails + * + * @throws IllegalArgumentException + * if {@code addr} is {@code null}, or {@code addr} is a SocketAddress + * subclass not supported by this socket + * + * @throws SecurityException + * if a security manager has been installed and it does + * not permit access to the given remote address + * + * @since 1.4 + */ + public void connect(SocketAddress addr) throws SocketException { + if (addr == null) + throw new IllegalArgumentException("Address can't be null"); + if (!(addr instanceof InetSocketAddress)) + throw new IllegalArgumentException("Unsupported address type"); + InetSocketAddress epoint = (InetSocketAddress) addr; + if (epoint.isUnresolved()) + throw new SocketException("Unresolved address"); + connectInternal(epoint.getAddress(), epoint.getPort()); + } + + /** + * Disconnects the socket. If the socket is closed or not connected, + * then this method has no effect. + * + * @see #connect + */ + public void disconnect() { + synchronized (this) { + if (isClosed()) + return; + if (connectState == ST_CONNECTED) { + impl.disconnect (); + } + connectedAddress = null; + connectedPort = -1; + connectState = ST_NOT_CONNECTED; + explicitFilter = false; + } + } + + /** + * Returns the binding state of the socket. + * <p> + * If the socket was bound prior to being {@link #close closed}, + * then this method will continue to return {@code true} + * after the socket is closed. + * + * @return true if the socket successfully bound to an address + * @since 1.4 + */ + public boolean isBound() { + return bound; + } + + /** + * Returns the connection state of the socket. + * <p> + * If the socket was connected prior to being {@link #close closed}, + * then this method will continue to return {@code true} + * after the socket is closed. + * + * @return true if the socket successfully connected to a server + * @since 1.4 + */ + public boolean isConnected() { + return connectState != ST_NOT_CONNECTED; + } + + /** + * Returns the address to which this socket is connected. Returns + * {@code null} if the socket is not connected. + * <p> + * If the socket was connected prior to being {@link #close closed}, + * then this method will continue to return the connected address + * after the socket is closed. + * + * @return the address to which this socket is connected. + */ + public InetAddress getInetAddress() { + return connectedAddress; + } + + /** + * Returns the port number to which this socket is connected. + * Returns {@code -1} if the socket is not connected. + * <p> + * If the socket was connected prior to being {@link #close closed}, + * then this method will continue to return the connected port number + * after the socket is closed. + * + * @return the port number to which this socket is connected. + */ + public int getPort() { + return connectedPort; + } + + /** + * Returns the address of the endpoint this socket is connected to, or + * {@code null} if it is unconnected. + * <p> + * If the socket was connected prior to being {@link #close closed}, + * then this method will continue to return the connected address + * after the socket is closed. + * + * @return a {@code SocketAddress} representing the remote + * endpoint of this socket, or {@code null} if it is + * not connected yet. + * @see #getInetAddress() + * @see #getPort() + * @see #connect(SocketAddress) + * @since 1.4 + */ + public SocketAddress getRemoteSocketAddress() { + if (!isConnected()) + return null; + return new InetSocketAddress(getInetAddress(), getPort()); + } + + /** + * Returns the address of the endpoint this socket is bound to. + * + * @return a {@code SocketAddress} representing the local endpoint of this + * socket, or {@code null} if it is closed or not bound yet. + * @see #getLocalAddress() + * @see #getLocalPort() + * @see #bind(SocketAddress) + * @since 1.4 + */ + + public SocketAddress getLocalSocketAddress() { + if (isClosed()) + return null; + if (!isBound()) + return null; + return new InetSocketAddress(getLocalAddress(), getLocalPort()); + } + + /** + * Sends a datagram packet from this socket. The + * {@code DatagramPacket} includes information indicating the + * data to be sent, its length, the IP address of the remote host, + * and the port number on the remote host. + * + * <p>If there is a security manager, and the socket is not currently + * connected to a remote address, this method first performs some + * security checks. First, if {@code p.getAddress().isMulticastAddress()} + * is true, this method calls the + * security manager's {@code checkMulticast} method + * with {@code p.getAddress()} as its argument. + * If the evaluation of that expression is false, + * this method instead calls the security manager's + * {@code checkConnect} method with arguments + * {@code p.getAddress().getHostAddress()} and + * {@code p.getPort()}. Each call to a security manager method + * could result in a SecurityException if the operation is not allowed. + * + * @param p the {@code DatagramPacket} to be sent. + * + * @exception IOException if an I/O error occurs. + * @exception SecurityException if a security manager exists and its + * {@code checkMulticast} or {@code checkConnect} + * method doesn't allow the send. + * @exception PortUnreachableException may be thrown if the socket is connected + * to a currently unreachable destination. Note, there is no + * guarantee that the exception will be thrown. + * @exception java.nio.channels.IllegalBlockingModeException + * if this socket has an associated channel, + * and the channel is in non-blocking mode. + * @exception IllegalArgumentException if the socket is connected, + * and connected address and packet address differ. + * + * @see java.net.DatagramPacket + * @see SecurityManager#checkMulticast(InetAddress) + * @see SecurityManager#checkConnect + * @revised 1.4 + * @spec JSR-51 + */ + public void send(DatagramPacket p) throws IOException { + InetAddress packetAddress = null; + synchronized (p) { + // BEGIN Android-changed + if (pendingConnectException != null) { + throw new SocketException("Pending connect failure", pendingConnectException); + } + // END Android-changed + if (isClosed()) + throw new SocketException("Socket is closed"); + checkAddress (p.getAddress(), "send"); + if (connectState == ST_NOT_CONNECTED) { + // check the address is ok wiht the security manager on every send. + SecurityManager security = System.getSecurityManager(); + + // The reason you want to synchronize on datagram packet + // is because you don't want an applet to change the address + // while you are trying to send the packet for example + // after the security check but before the send. + if (security != null) { + if (p.getAddress().isMulticastAddress()) { + security.checkMulticast(p.getAddress()); + } else { + security.checkConnect(p.getAddress().getHostAddress(), + p.getPort()); + } + } + } else { + // we're connected + packetAddress = p.getAddress(); + if (packetAddress == null) { + p.setAddress(connectedAddress); + p.setPort(connectedPort); + } else if ((!packetAddress.equals(connectedAddress)) || + p.getPort() != connectedPort) { + throw new IllegalArgumentException("connected address " + + "and packet address" + + " differ"); + } + } + // Check whether the socket is bound + if (!isBound()) + bind(new InetSocketAddress(0)); + // call the method to send + getImpl().send(p); + } + } + + /** + * Receives a datagram packet from this socket. When this method + * returns, the {@code DatagramPacket}'s buffer is filled with + * the data received. The datagram packet also contains the sender's + * IP address, and the port number on the sender's machine. + * <p> + * This method blocks until a datagram is received. The + * {@code length} field of the datagram packet object contains + * the length of the received message. If the message is longer than + * the packet's length, the message is truncated. + * <p> + * If there is a security manager, a packet cannot be received if the + * security manager's {@code checkAccept} method + * does not allow it. + * + * @param p the {@code DatagramPacket} into which to place + * the incoming data. + * @exception IOException if an I/O error occurs. + * @exception SocketTimeoutException if setSoTimeout was previously called + * and the timeout has expired. + * @exception PortUnreachableException may be thrown if the socket is connected + * to a currently unreachable destination. Note, there is no guarantee that the + * exception will be thrown. + * @exception java.nio.channels.IllegalBlockingModeException + * if this socket has an associated channel, + * and the channel is in non-blocking mode. + * @see java.net.DatagramPacket + * @see java.net.DatagramSocket + * @revised 1.4 + * @spec JSR-51 + */ + public synchronized void receive(DatagramPacket p) throws IOException { + synchronized (p) { + if (!isBound()) + bind(new InetSocketAddress(0)); + + // BEGIN Android-changed + if (pendingConnectException != null) { + throw new SocketException("Pending connect failure", pendingConnectException); + } + // END Android-changed + + if (connectState == ST_NOT_CONNECTED) { + // check the address is ok with the security manager before every recv. + SecurityManager security = System.getSecurityManager(); + if (security != null) { + while(true) { + String peekAd = null; + int peekPort = 0; + // peek at the packet to see who it is from. + if (!oldImpl) { + // We can use the new peekData() API + DatagramPacket peekPacket = new DatagramPacket(new byte[1], 1); + peekPort = getImpl().peekData(peekPacket); + peekAd = peekPacket.getAddress().getHostAddress(); + } else { + InetAddress adr = new InetAddress(); + peekPort = getImpl().peek(adr); + peekAd = adr.getHostAddress(); + } + try { + security.checkAccept(peekAd, peekPort); + // security check succeeded - so now break + // and recv the packet. + break; + } catch (SecurityException se) { + // Throw away the offending packet by consuming + // it in a tmp buffer. + DatagramPacket tmp = new DatagramPacket(new byte[1], 1); + getImpl().receive(tmp); + + // silently discard the offending packet + // and continue: unknown/malicious + // entities on nets should not make + // runtime throw security exception and + // disrupt the applet by sending random + // datagram packets. + continue; + } + } // end of while + } + } + DatagramPacket tmp = null; + if ((connectState == ST_CONNECTED_NO_IMPL) || explicitFilter) { + // We have to do the filtering the old fashioned way since + // the native impl doesn't support connect or the connect + // via the impl failed, or .. "explicitFilter" may be set when + // a socket is connected via the impl, for a period of time + // when packets from other sources might be queued on socket. + boolean stop = false; + while (!stop) { + InetAddress peekAddress = null; + int peekPort = -1; + // peek at the packet to see who it is from. + if (!oldImpl) { + // We can use the new peekData() API + DatagramPacket peekPacket = new DatagramPacket(new byte[1], 1); + peekPort = getImpl().peekData(peekPacket); + peekAddress = peekPacket.getAddress(); + } else { + // this api only works for IPv4 + peekAddress = new InetAddress(); + peekPort = getImpl().peek(peekAddress); + } + if ((!connectedAddress.equals(peekAddress)) || + (connectedPort != peekPort)) { + // throw the packet away and silently continue + tmp = new DatagramPacket( + new byte[1024], 1024); + getImpl().receive(tmp); + if (explicitFilter) { + if (checkFiltering(tmp)) { + stop = true; + } + } + } else { + stop = true; + } + } + } + // If the security check succeeds, or the datagram is + // connected then receive the packet + getImpl().receive(p); + if (explicitFilter && tmp == null) { + // packet was not filtered, account for it here + checkFiltering(p); + } + } + } + + private boolean checkFiltering(DatagramPacket p) throws SocketException { + bytesLeftToFilter -= p.getLength(); + if (bytesLeftToFilter <= 0 || getImpl().dataAvailable() <= 0) { + explicitFilter = false; + return true; + } + return false; + } + + /** + * Gets the local address to which the socket is bound. + * + * <p>If there is a security manager, its + * {@code checkConnect} method is first called + * with the host address and {@code -1} + * as its arguments to see if the operation is allowed. + * + * @see SecurityManager#checkConnect + * @return the local address to which the socket is bound, + * {@code null} if the socket is closed, or + * an {@code InetAddress} representing + * {@link InetAddress#isAnyLocalAddress wildcard} + * address if either the socket is not bound, or + * the security manager {@code checkConnect} + * method does not allow the operation + * @since 1.1 + */ + public InetAddress getLocalAddress() { + if (isClosed()) + return null; + InetAddress in = null; + try { + in = (InetAddress) getImpl().getOption(SocketOptions.SO_BINDADDR); + if (in.isAnyLocalAddress()) { + in = InetAddress.anyLocalAddress(); + } + SecurityManager s = System.getSecurityManager(); + if (s != null) { + s.checkConnect(in.getHostAddress(), -1); + } + } catch (Exception e) { + in = InetAddress.anyLocalAddress(); // "0.0.0.0" + } + return in; + } + + /** + * Returns the port number on the local host to which this socket + * is bound. + * + * @return the port number on the local host to which this socket is bound, + {@code -1} if the socket is closed, or + {@code 0} if it is not bound yet. + */ + public int getLocalPort() { + if (isClosed()) + return -1; + try { + return getImpl().getLocalPort(); + } catch (Exception e) { + return 0; + } + } + + /** Enable/disable SO_TIMEOUT with the specified timeout, in + * milliseconds. With this option set to a non-zero timeout, + * a call to receive() for this DatagramSocket + * will block for only this amount of time. If the timeout expires, + * a <B>java.net.SocketTimeoutException</B> is raised, though the + * DatagramSocket is still valid. The option <B>must</B> be enabled + * prior to entering the blocking operation to have effect. The + * timeout must be {@code > 0}. + * A timeout of zero is interpreted as an infinite timeout. + * + * @param timeout the specified timeout in milliseconds. + * @throws SocketException if there is an error in the underlying protocol, such as an UDP error. + * @since JDK1.1 + * @see #getSoTimeout() + */ + public synchronized void setSoTimeout(int timeout) throws SocketException { + if (isClosed()) + throw new SocketException("Socket is closed"); + getImpl().setOption(SocketOptions.SO_TIMEOUT, new Integer(timeout)); + } + + /** + * Retrieve setting for SO_TIMEOUT. 0 returns implies that the + * option is disabled (i.e., timeout of infinity). + * + * @return the setting for SO_TIMEOUT + * @throws SocketException if there is an error in the underlying protocol, such as an UDP error. + * @since JDK1.1 + * @see #setSoTimeout(int) + */ + public synchronized int getSoTimeout() throws SocketException { + if (isClosed()) + throw new SocketException("Socket is closed"); + if (getImpl() == null) + return 0; + Object o = getImpl().getOption(SocketOptions.SO_TIMEOUT); + /* extra type safety */ + if (o instanceof Integer) { + return ((Integer) o).intValue(); + } else { + return 0; + } + } + + /** + * Sets the SO_SNDBUF option to the specified value for this + * {@code DatagramSocket}. The SO_SNDBUF option is used by the + * network implementation as a hint to size the underlying + * network I/O buffers. The SO_SNDBUF setting may also be used + * by the network implementation to determine the maximum size + * of the packet that can be sent on this socket. + * <p> + * As SO_SNDBUF is a hint, applications that want to verify + * what size the buffer is should call {@link #getSendBufferSize()}. + * <p> + * Increasing the buffer size may allow multiple outgoing packets + * to be queued by the network implementation when the send rate + * is high. + * <p> + * Note: If {@link #send(DatagramPacket)} is used to send a + * {@code DatagramPacket} that is larger than the setting + * of SO_SNDBUF then it is implementation specific if the + * packet is sent or discarded. + * + * @param size the size to which to set the send buffer + * size. This value must be greater than 0. + * + * @exception SocketException if there is an error + * in the underlying protocol, such as an UDP error. + * @exception IllegalArgumentException if the value is 0 or is + * negative. + * @see #getSendBufferSize() + */ + public synchronized void setSendBufferSize(int size) + throws SocketException{ + if (!(size > 0)) { + throw new IllegalArgumentException("negative send size"); + } + if (isClosed()) + throw new SocketException("Socket is closed"); + getImpl().setOption(SocketOptions.SO_SNDBUF, new Integer(size)); + } + + /** + * Get value of the SO_SNDBUF option for this {@code DatagramSocket}, that is the + * buffer size used by the platform for output on this {@code DatagramSocket}. + * + * @return the value of the SO_SNDBUF option for this {@code DatagramSocket} + * @exception SocketException if there is an error in + * the underlying protocol, such as an UDP error. + * @see #setSendBufferSize + */ + public synchronized int getSendBufferSize() throws SocketException { + if (isClosed()) + throw new SocketException("Socket is closed"); + int result = 0; + Object o = getImpl().getOption(SocketOptions.SO_SNDBUF); + if (o instanceof Integer) { + result = ((Integer)o).intValue(); + } + return result; + } + + /** + * Sets the SO_RCVBUF option to the specified value for this + * {@code DatagramSocket}. The SO_RCVBUF option is used by the + * the network implementation as a hint to size the underlying + * network I/O buffers. The SO_RCVBUF setting may also be used + * by the network implementation to determine the maximum size + * of the packet that can be received on this socket. + * <p> + * Because SO_RCVBUF is a hint, applications that want to + * verify what size the buffers were set to should call + * {@link #getReceiveBufferSize()}. + * <p> + * Increasing SO_RCVBUF may allow the network implementation + * to buffer multiple packets when packets arrive faster than + * are being received using {@link #receive(DatagramPacket)}. + * <p> + * Note: It is implementation specific if a packet larger + * than SO_RCVBUF can be received. + * + * @param size the size to which to set the receive buffer + * size. This value must be greater than 0. + * + * @exception SocketException if there is an error in + * the underlying protocol, such as an UDP error. + * @exception IllegalArgumentException if the value is 0 or is + * negative. + * @see #getReceiveBufferSize() + */ + public synchronized void setReceiveBufferSize(int size) + throws SocketException{ + if (size <= 0) { + throw new IllegalArgumentException("invalid receive size"); + } + if (isClosed()) + throw new SocketException("Socket is closed"); + getImpl().setOption(SocketOptions.SO_RCVBUF, new Integer(size)); + } + + /** + * Get value of the SO_RCVBUF option for this {@code DatagramSocket}, that is the + * buffer size used by the platform for input on this {@code DatagramSocket}. + * + * @return the value of the SO_RCVBUF option for this {@code DatagramSocket} + * @exception SocketException if there is an error in the underlying protocol, such as an UDP error. + * @see #setReceiveBufferSize(int) + */ + public synchronized int getReceiveBufferSize() + throws SocketException{ + if (isClosed()) + throw new SocketException("Socket is closed"); + int result = 0; + Object o = getImpl().getOption(SocketOptions.SO_RCVBUF); + if (o instanceof Integer) { + result = ((Integer)o).intValue(); + } + return result; + } + + /** + * Enable/disable the SO_REUSEADDR socket option. + * <p> + * For UDP sockets it may be necessary to bind more than one + * socket to the same socket address. This is typically for the + * purpose of receiving multicast packets + * (See {@link java.net.MulticastSocket}). The + * {@code SO_REUSEADDR} socket option allows multiple + * sockets to be bound to the same socket address if the + * {@code SO_REUSEADDR} socket option is enabled prior + * to binding the socket using {@link #bind(SocketAddress)}. + * <p> + * Note: This functionality is not supported by all existing platforms, + * so it is implementation specific whether this option will be ignored + * or not. However, if it is not supported then + * {@link #getReuseAddress()} will always return {@code false}. + * <p> + * When a {@code DatagramSocket} is created the initial setting + * of {@code SO_REUSEADDR} is disabled. + * <p> + * The behaviour when {@code SO_REUSEADDR} is enabled or + * disabled after a socket is bound (See {@link #isBound()}) + * is not defined. + * + * @param on whether to enable or disable the + * @exception SocketException if an error occurs enabling or + * disabling the {@code SO_RESUEADDR} socket option, + * or the socket is closed. + * @since 1.4 + * @see #getReuseAddress() + * @see #bind(SocketAddress) + * @see #isBound() + * @see #isClosed() + */ + public synchronized void setReuseAddress(boolean on) throws SocketException { + if (isClosed()) + throw new SocketException("Socket is closed"); + // Integer instead of Boolean for compatibility with older DatagramSocketImpl + if (oldImpl) + getImpl().setOption(SocketOptions.SO_REUSEADDR, new Integer(on?-1:0)); + else + getImpl().setOption(SocketOptions.SO_REUSEADDR, Boolean.valueOf(on)); + } + + /** + * Tests if SO_REUSEADDR is enabled. + * + * @return a {@code boolean} indicating whether or not SO_REUSEADDR is enabled. + * @exception SocketException if there is an error + * in the underlying protocol, such as an UDP error. + * @since 1.4 + * @see #setReuseAddress(boolean) + */ + public synchronized boolean getReuseAddress() throws SocketException { + if (isClosed()) + throw new SocketException("Socket is closed"); + Object o = getImpl().getOption(SocketOptions.SO_REUSEADDR); + return ((Boolean)o).booleanValue(); + } + + /** + * Enable/disable SO_BROADCAST. + * + * <p> Some operating systems may require that the Java virtual machine be + * started with implementation specific privileges to enable this option or + * send broadcast datagrams. + * + * @param on + * whether or not to have broadcast turned on. + * + * @throws SocketException + * if there is an error in the underlying protocol, such as an UDP + * error. + * + * @since 1.4 + * @see #getBroadcast() + */ + public synchronized void setBroadcast(boolean on) throws SocketException { + if (isClosed()) + throw new SocketException("Socket is closed"); + getImpl().setOption(SocketOptions.SO_BROADCAST, Boolean.valueOf(on)); + } + + /** + * Tests if SO_BROADCAST is enabled. + * @return a {@code boolean} indicating whether or not SO_BROADCAST is enabled. + * @exception SocketException if there is an error + * in the underlying protocol, such as an UDP error. + * @since 1.4 + * @see #setBroadcast(boolean) + */ + public synchronized boolean getBroadcast() throws SocketException { + if (isClosed()) + throw new SocketException("Socket is closed"); + return ((Boolean)(getImpl().getOption(SocketOptions.SO_BROADCAST))).booleanValue(); + } + + /** + * Sets traffic class or type-of-service octet in the IP + * datagram header for datagrams sent from this DatagramSocket. + * As the underlying network implementation may ignore this + * value applications should consider it a hint. + * + * <P> The tc <B>must</B> be in the range {@code 0 <= tc <= + * 255} or an IllegalArgumentException will be thrown. + * <p>Notes: + * <p>For Internet Protocol v4 the value consists of an + * {@code integer}, the least significant 8 bits of which + * represent the value of the TOS octet in IP packets sent by + * the socket. + * RFC 1349 defines the TOS values as follows: + * + * <UL> + * <LI><CODE>IPTOS_LOWCOST (0x02)</CODE></LI> + * <LI><CODE>IPTOS_RELIABILITY (0x04)</CODE></LI> + * <LI><CODE>IPTOS_THROUGHPUT (0x08)</CODE></LI> + * <LI><CODE>IPTOS_LOWDELAY (0x10)</CODE></LI> + * </UL> + * The last low order bit is always ignored as this + * corresponds to the MBZ (must be zero) bit. + * <p> + * Setting bits in the precedence field may result in a + * SocketException indicating that the operation is not + * permitted. + * <p> + * for Internet Protocol v6 {@code tc} is the value that + * would be placed into the sin6_flowinfo field of the IP header. + * + * @param tc an {@code int} value for the bitset. + * @throws SocketException if there is an error setting the + * traffic class or type-of-service + * @since 1.4 + * @see #getTrafficClass + */ + public synchronized void setTrafficClass(int tc) throws SocketException { + if (tc < 0 || tc > 255) + throw new IllegalArgumentException("tc is not in range 0 -- 255"); + + if (isClosed()) + throw new SocketException("Socket is closed"); + try { + getImpl().setOption(SocketOptions.IP_TOS, tc); + } catch (SocketException se) { + // not supported if socket already connected + // Solaris returns error in such cases + if(!isConnected()) + throw se; + } + } + + /** + * Gets traffic class or type-of-service in the IP datagram + * header for packets sent from this DatagramSocket. + * <p> + * As the underlying network implementation may ignore the + * traffic class or type-of-service set using {@link #setTrafficClass(int)} + * this method may return a different value than was previously + * set using the {@link #setTrafficClass(int)} method on this + * DatagramSocket. + * + * @return the traffic class or type-of-service already set + * @throws SocketException if there is an error obtaining the + * traffic class or type-of-service value. + * @since 1.4 + * @see #setTrafficClass(int) + */ + public synchronized int getTrafficClass() throws SocketException { + if (isClosed()) + throw new SocketException("Socket is closed"); + return ((Integer)(getImpl().getOption(SocketOptions.IP_TOS))).intValue(); + } + + /** + * Closes this datagram socket. + * <p> + * Any thread currently blocked in {@link #receive} upon this socket + * will throw a {@link SocketException}. + * + * <p> If this socket has an associated channel then the channel is closed + * as well. + * + * @revised 1.4 + * @spec JSR-51 + */ + public void close() { + synchronized(closeLock) { + if (isClosed()) + return; + impl.close(); + closed = true; + } + } + + /** + * Returns whether the socket is closed or not. + * + * @return true if the socket has been closed + * @since 1.4 + */ + public boolean isClosed() { + synchronized(closeLock) { + return closed; + } + } + + /** + * Returns the unique {@link java.nio.channels.DatagramChannel} object + * associated with this datagram socket, if any. + * + * <p> A datagram socket will have a channel if, and only if, the channel + * itself was created via the {@link java.nio.channels.DatagramChannel#open + * DatagramChannel.open} method. + * + * @return the datagram channel associated with this datagram socket, + * or {@code null} if this socket was not created for a channel + * + * @since 1.4 + * @spec JSR-51 + */ + public DatagramChannel getChannel() { + return null; + } + + /** + * User defined factory for all datagram sockets. + */ + static DatagramSocketImplFactory factory; + + /** + * Sets the datagram socket implementation factory for the + * application. The factory can be specified only once. + * <p> + * When an application creates a new datagram socket, the socket + * implementation factory's {@code createDatagramSocketImpl} method is + * called to create the actual datagram socket implementation. + * <p> + * Passing {@code null} to the method is a no-op unless the factory + * was already set. + * + * <p>If there is a security manager, this method first calls + * the security manager's {@code checkSetFactory} method + * to ensure the operation is allowed. + * This could result in a SecurityException. + * + * @param fac the desired factory. + * @exception IOException if an I/O error occurs when setting the + * datagram socket factory. + * @exception SocketException if the factory is already defined. + * @exception SecurityException if a security manager exists and its + * {@code checkSetFactory} method doesn't allow the + operation. + * @see + java.net.DatagramSocketImplFactory#createDatagramSocketImpl() + * @see SecurityManager#checkSetFactory + * @since 1.3 + */ + public static synchronized void + setDatagramSocketImplFactory(DatagramSocketImplFactory fac) + throws IOException + { + if (factory != null) { + throw new SocketException("factory already defined"); + } + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkSetFactory(); + } + factory = fac; + } + + // Android-added: for testing and internal use. + /** + * @hide internal use only + */ + public FileDescriptor getFileDescriptor$() { + return impl.fd; + } + +}
diff --git a/java/net/DatagramSocketImpl.java b/java/net/DatagramSocketImpl.java new file mode 100644 index 0000000..537edfe --- /dev/null +++ b/java/net/DatagramSocketImpl.java
@@ -0,0 +1,314 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.io.InterruptedIOException; + +/** + * Abstract datagram and multicast socket implementation base class. + * @author Pavani Diwanji + * @since JDK1.1 + */ + +public abstract class DatagramSocketImpl implements SocketOptions { + + /** + * The local port number. + */ + protected int localPort; + + /** + * The file descriptor object. + */ + protected FileDescriptor fd; + + int dataAvailable() { + // default impl returns zero, which disables the calling + // functionality + return 0; + } + + /** + * The DatagramSocket or MulticastSocket + * that owns this impl + */ + DatagramSocket socket; + + void setDatagramSocket(DatagramSocket socket) { + this.socket = socket; + } + + DatagramSocket getDatagramSocket() { + return socket; + } + + /** + * Creates a datagram socket. + * @exception SocketException if there is an error in the + * underlying protocol, such as a TCP error. + */ + protected abstract void create() throws SocketException; + + /** + * Binds a datagram socket to a local port and address. + * @param lport the local port + * @param laddr the local address + * @exception SocketException if there is an error in the + * underlying protocol, such as a TCP error. + */ + protected abstract void bind(int lport, InetAddress laddr) throws SocketException; + + /** + * Sends a datagram packet. The packet contains the data and the + * destination address to send the packet to. + * @param p the packet to be sent. + * @exception IOException if an I/O exception occurs while sending the + * datagram packet. + * @exception PortUnreachableException may be thrown if the socket is connected + * to a currently unreachable destination. Note, there is no guarantee that + * the exception will be thrown. + */ + protected abstract void send(DatagramPacket p) throws IOException; + + /** + * Connects a datagram socket to a remote destination. This associates the remote + * address with the local socket so that datagrams may only be sent to this destination + * and received from this destination. This may be overridden to call a native + * system connect. + * + * <p>If the remote destination to which the socket is connected does not + * exist, or is otherwise unreachable, and if an ICMP destination unreachable + * packet has been received for that address, then a subsequent call to + * send or receive may throw a PortUnreachableException. + * Note, there is no guarantee that the exception will be thrown. + * @param address the remote InetAddress to connect to + * @param port the remote port number + * @exception SocketException may be thrown if the socket cannot be + * connected to the remote destination + * @since 1.4 + */ + protected void connect(InetAddress address, int port) throws SocketException {} + + /** + * Disconnects a datagram socket from its remote destination. + * @since 1.4 + */ + protected void disconnect() {} + + /** + * Peek at the packet to see who it is from. Updates the specified {@code InetAddress} + * to the address which the packet came from. + * @param i an InetAddress object + * @return the port number which the packet came from. + * @exception IOException if an I/O exception occurs + * @exception PortUnreachableException may be thrown if the socket is connected + * to a currently unreachable destination. Note, there is no guarantee that the + * exception will be thrown. + */ + protected abstract int peek(InetAddress i) throws IOException; + + /** + * Peek at the packet to see who it is from. The data is copied into the specified + * {@code DatagramPacket}. The data is returned, + * but not consumed, so that a subsequent peekData/receive operation + * will see the same data. + * @param p the Packet Received. + * @return the port number which the packet came from. + * @exception IOException if an I/O exception occurs + * @exception PortUnreachableException may be thrown if the socket is connected + * to a currently unreachable destination. Note, there is no guarantee that the + * exception will be thrown. + * @since 1.4 + */ + protected abstract int peekData(DatagramPacket p) throws IOException; + /** + * Receive the datagram packet. + * @param p the Packet Received. + * @exception IOException if an I/O exception occurs + * while receiving the datagram packet. + * @exception PortUnreachableException may be thrown if the socket is connected + * to a currently unreachable destination. Note, there is no guarantee that the + * exception will be thrown. + */ + protected abstract void receive(DatagramPacket p) throws IOException; + + /** + * Set the TTL (time-to-live) option. + * @param ttl a byte specifying the TTL value + * + * @deprecated use setTimeToLive instead. + * @exception IOException if an I/O exception occurs while setting + * the time-to-live option. + * @see #getTTL() + */ + @Deprecated + protected abstract void setTTL(byte ttl) throws IOException; + + /** + * Retrieve the TTL (time-to-live) option. + * + * @exception IOException if an I/O exception occurs + * while retrieving the time-to-live option + * @deprecated use getTimeToLive instead. + * @return a byte representing the TTL value + * @see #setTTL(byte) + */ + @Deprecated + protected abstract byte getTTL() throws IOException; + + /** + * Set the TTL (time-to-live) option. + * @param ttl an {@code int} specifying the time-to-live value + * @exception IOException if an I/O exception occurs + * while setting the time-to-live option. + * @see #getTimeToLive() + */ + protected abstract void setTimeToLive(int ttl) throws IOException; + + /** + * Retrieve the TTL (time-to-live) option. + * @exception IOException if an I/O exception occurs + * while retrieving the time-to-live option + * @return an {@code int} representing the time-to-live value + * @see #setTimeToLive(int) + */ + protected abstract int getTimeToLive() throws IOException; + + /** + * Join the multicast group. + * @param inetaddr multicast address to join. + * @exception IOException if an I/O exception occurs + * while joining the multicast group. + */ + protected abstract void join(InetAddress inetaddr) throws IOException; + + /** + * Leave the multicast group. + * @param inetaddr multicast address to leave. + * @exception IOException if an I/O exception occurs + * while leaving the multicast group. + */ + protected abstract void leave(InetAddress inetaddr) throws IOException; + + /** + * Join the multicast group. + * @param mcastaddr address to join. + * @param netIf specifies the local interface to receive multicast + * datagram packets + * @throws IOException if an I/O exception occurs while joining + * the multicast group + * @since 1.4 + */ + protected abstract void joinGroup(SocketAddress mcastaddr, + NetworkInterface netIf) + throws IOException; + + /** + * Leave the multicast group. + * @param mcastaddr address to leave. + * @param netIf specified the local interface to leave the group at + * @throws IOException if an I/O exception occurs while leaving + * the multicast group + * @since 1.4 + */ + protected abstract void leaveGroup(SocketAddress mcastaddr, + NetworkInterface netIf) + throws IOException; + + /** + * Close the socket. + */ + protected abstract void close(); + + /** + * Gets the local port. + * @return an {@code int} representing the local port value + */ + protected int getLocalPort() { + return localPort; + } + + <T> void setOption(SocketOption<T> name, T value) throws IOException { + if (name == StandardSocketOptions.SO_SNDBUF) { + setOption(SocketOptions.SO_SNDBUF, value); + } else if (name == StandardSocketOptions.SO_RCVBUF) { + setOption(SocketOptions.SO_RCVBUF, value); + } else if (name == StandardSocketOptions.SO_REUSEADDR) { + setOption(SocketOptions.SO_REUSEADDR, value); + } else if (name == StandardSocketOptions.IP_TOS) { + setOption(SocketOptions.IP_TOS, value); + } else if (name == StandardSocketOptions.IP_MULTICAST_IF && + (getDatagramSocket() instanceof MulticastSocket)) { + setOption(SocketOptions.IP_MULTICAST_IF2, value); + } else if (name == StandardSocketOptions.IP_MULTICAST_TTL && + (getDatagramSocket() instanceof MulticastSocket)) { + if (! (value instanceof Integer)) { + throw new IllegalArgumentException("not an integer"); + } + setTimeToLive((Integer)value); + } else if (name == StandardSocketOptions.IP_MULTICAST_LOOP && + (getDatagramSocket() instanceof MulticastSocket)) { + setOption(SocketOptions.IP_MULTICAST_LOOP, value); + } else { + throw new UnsupportedOperationException("unsupported option"); + } + } + + <T> T getOption(SocketOption<T> name) throws IOException { + if (name == StandardSocketOptions.SO_SNDBUF) { + return (T) getOption(SocketOptions.SO_SNDBUF); + } else if (name == StandardSocketOptions.SO_RCVBUF) { + return (T) getOption(SocketOptions.SO_RCVBUF); + } else if (name == StandardSocketOptions.SO_REUSEADDR) { + return (T) getOption(SocketOptions.SO_REUSEADDR); + } else if (name == StandardSocketOptions.IP_TOS) { + return (T) getOption(SocketOptions.IP_TOS); + } else if (name == StandardSocketOptions.IP_MULTICAST_IF && + (getDatagramSocket() instanceof MulticastSocket)) { + return (T) getOption(SocketOptions.IP_MULTICAST_IF2); + } else if (name == StandardSocketOptions.IP_MULTICAST_TTL && + (getDatagramSocket() instanceof MulticastSocket)) { + Integer ttl = getTimeToLive(); + return (T)ttl; + } else if (name == StandardSocketOptions.IP_MULTICAST_LOOP && + (getDatagramSocket() instanceof MulticastSocket)) { + return (T) getOption(SocketOptions.IP_MULTICAST_LOOP); + } else { + throw new UnsupportedOperationException("unsupported option"); + } + } + + /** + * Gets the datagram socket file descriptor. + * @return a {@code FileDescriptor} object representing the datagram socket + * file descriptor + */ + protected FileDescriptor getFileDescriptor() { + return fd; + } +}
diff --git a/java/net/DatagramSocketImplFactory.java b/java/net/DatagramSocketImplFactory.java new file mode 100644 index 0000000..4d89196 --- /dev/null +++ b/java/net/DatagramSocketImplFactory.java
@@ -0,0 +1,46 @@ +/* + * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +/** + * This interface defines a factory for datagram socket implementations. It + * is used by the classes {@code DatagramSocket} to create actual socket + * implementations. + * + * @author Yingxian Wang + * @see java.net.DatagramSocket + * @since 1.3 + */ +public +interface DatagramSocketImplFactory { + /** + * Creates a new {@code DatagramSocketImpl} instance. + * + * @return a new instance of {@code DatagramSocketImpl}. + * @see java.net.DatagramSocketImpl + */ + DatagramSocketImpl createDatagramSocketImpl(); +}
diff --git a/java/net/DefaultDatagramSocketImplFactory.java b/java/net/DefaultDatagramSocketImplFactory.java new file mode 100644 index 0000000..ad62c58 --- /dev/null +++ b/java/net/DefaultDatagramSocketImplFactory.java
@@ -0,0 +1,73 @@ +/* + * Copyright (c) 2007,2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.net; + +import java.security.AccessController; + +/** + * This class defines a factory for creating DatagramSocketImpls. It defaults + * to creating plain DatagramSocketImpls, but may create other DatagramSocketImpls + * by setting the impl.prefix system property. + * + * @author Chris Hegarty + */ + +class DefaultDatagramSocketImplFactory { + static Class<?> prefixImplClass = null; + + static { + String prefix = null; + try { + prefix = AccessController.doPrivileged( + new sun.security.action.GetPropertyAction("impl.prefix", null)); + if (prefix != null) + prefixImplClass = Class.forName("java.net."+prefix+"DatagramSocketImpl"); + } catch (Exception e) { + System.err.println("Can't find class: java.net." + + prefix + + "DatagramSocketImpl: check impl.prefix property"); + //prefixImplClass = null; + } + } + + /** + * Creates a new <code>DatagramSocketImpl</code> instance. + * + * @param isMulticast true if this impl if for a MutlicastSocket + * @return a new instance of a <code>DatagramSocketImpl</code>. + */ + static DatagramSocketImpl createDatagramSocketImpl(boolean isMulticast /*unused on unix*/) + throws SocketException { + if (prefixImplClass != null) { + try { + return (DatagramSocketImpl)prefixImplClass.newInstance(); + } catch (Exception e) { + throw new SocketException("can't instantiate DatagramSocketImpl"); + } + } else { + return new java.net.PlainDatagramSocketImpl(); + } + } +}
diff --git a/java/net/DefaultFileNameMap.java b/java/net/DefaultFileNameMap.java new file mode 100644 index 0000000..da67d14 --- /dev/null +++ b/java/net/DefaultFileNameMap.java
@@ -0,0 +1,55 @@ +/* + * Copyright (C) 2010 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 java.net; + +import libcore.content.type.MimeMap; + +/** + * Implements {@link FileNameMap} in terms of {@link MimeMap}. + */ +class DefaultFileNameMap implements FileNameMap { + + public String getContentTypeFor(String filename) { + int fragmentIndex = filename.indexOf('#'); + if (fragmentIndex >= 0) { + filename = filename.substring(0, fragmentIndex); + } + if (filename.endsWith("/")) { // a directory + return "text/html"; + } + + int slashIndex = filename.lastIndexOf('/'); + if (slashIndex >= 0) { + filename = filename.substring(slashIndex); + } + + MimeMap mimeMap = MimeMap.getDefault(); + int dotIndex = -1; + do { + String ext = filename.substring(dotIndex + 1); + String result = mimeMap.guessMimeTypeFromExtension(ext); + if ((result != null) && + // Compat behavior: If there's a '/', then extension must not be the + // whole string. http://b/144977800 + (slashIndex < 0 || dotIndex >= 0)) { + return result; + } + dotIndex = filename.indexOf('.', dotIndex + 1); + } while (dotIndex >= 0); + return null; + } +}
diff --git a/java/net/DefaultInterface.java b/java/net/DefaultInterface.java new file mode 100644 index 0000000..0c31cb6 --- /dev/null +++ b/java/net/DefaultInterface.java
@@ -0,0 +1,41 @@ +/* + * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +/** + * Choose a network interface to be the default for + * outgoing IPv6 traffic that does not specify a scope_id (and which needs one). + * + * Platforms that do not require a default interface may return null + * which is what this implementation does. + */ + +class DefaultInterface { + + static NetworkInterface getDefault() { + return null; + } +}
diff --git a/java/net/FileNameMap.java b/java/net/FileNameMap.java new file mode 100644 index 0000000..393b5aa --- /dev/null +++ b/java/net/FileNameMap.java
@@ -0,0 +1,44 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +/** + * A simple interface which provides a mechanism to map + * between a file name and a MIME type string. + * + * @author Steven B. Byrne + * @since JDK1.1 + */ +public interface FileNameMap { + + /** + * Gets the MIME type for the specified file name. + * @param fileName the specified file name + * @return a {@code String} indicating the MIME + * type for the specified file name. + */ + public String getContentTypeFor(String fileName); +}
diff --git a/java/net/HttpCookie.java b/java/net/HttpCookie.java new file mode 100644 index 0000000..3ff33c7 --- /dev/null +++ b/java/net/HttpCookie.java
@@ -0,0 +1,1213 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.TimeZone; +import libcore.net.http.HttpDate; + +/** + * An HttpCookie object represents an HTTP cookie, which carries state + * information between server and user agent. Cookie is widely adopted + * to create stateful sessions. + * + * <p> There are 3 HTTP cookie specifications: + * <blockquote> + * Netscape draft<br> + * RFC 2109 - <a href="http://www.ietf.org/rfc/rfc2109.txt"> + * <i>http://www.ietf.org/rfc/rfc2109.txt</i></a><br> + * RFC 2965 - <a href="http://www.ietf.org/rfc/rfc2965.txt"> + * <i>http://www.ietf.org/rfc/rfc2965.txt</i></a> + * </blockquote> + * + * <p> HttpCookie class can accept all these 3 forms of syntax. + * + * @author Edward Wang + * @since 1.6 + */ +public final class HttpCookie implements Cloneable { + // BEGIN Android-added: Reserved name can't be HttpCookie name + private static final Set<String> RESERVED_NAMES = new HashSet<String>(); + + static { + RESERVED_NAMES.add("comment"); // RFC 2109 RFC 2965 RFC 6265 + RESERVED_NAMES.add("commenturl"); // RFC 2965 RFC 6265 + RESERVED_NAMES.add("discard"); // RFC 2965 RFC 6265 + RESERVED_NAMES.add("domain"); // Netscape RFC 2109 RFC 2965 RFC 6265 + RESERVED_NAMES.add("expires"); // Netscape + RESERVED_NAMES.add("httponly"); // RFC 6265 + RESERVED_NAMES.add("max-age"); // RFC 2109 RFC 2965 RFC 6265 + RESERVED_NAMES.add("path"); // Netscape RFC 2109 RFC 2965 RFC 6265 + RESERVED_NAMES.add("port"); // RFC 2965 RFC 6265 + RESERVED_NAMES.add("secure"); // Netscape RFC 2109 RFC 2965 RFC 6265 + RESERVED_NAMES.add("version"); // RFC 2109 RFC 2965 RFC 6265 + } + // END Android-added: Reserved name can't be HttpCookie name + + // ---------------- Fields -------------- + + // The value of the cookie itself. + private final String name; // NAME= ... "$Name" style is reserved + private String value; // value of NAME + + // Attributes encoded in the header's cookie fields. + private String comment; // Comment=VALUE ... describes cookie's use + private String commentURL; // CommentURL="http URL" ... describes cookie's use + private boolean toDiscard; // Discard ... discard cookie unconditionally + private String domain; // Domain=VALUE ... domain that sees cookie + private long maxAge = MAX_AGE_UNSPECIFIED; // Max-Age=VALUE ... cookies auto-expire + private String path; // Path=VALUE ... URLs that see the cookie + private String portlist; // Port[="portlist"] ... the port cookie may be returned to + private boolean secure; // Secure ... e.g. use SSL + private boolean httpOnly; // HttpOnly ... i.e. not accessible to scripts + private int version = 1; // Version=1 ... RFC 2965 style + + // The original header this cookie was consructed from, if it was + // constructed by parsing a header, otherwise null. + private final String header; + + // + // Android-changed: Fixed units, s/seconds/milliseconds/, in comment below. + // Hold the creation time (in milliseconds) of the http cookie for later + // expiration calculation + private final long whenCreated; + + // Since the positive and zero max-age have their meanings, + // this value serves as a hint as 'not specify max-age' + private final static long MAX_AGE_UNSPECIFIED = -1; + + // BEGIN Android-removed: Use libcore.net.http.HttpDate for parsing. + // date formats used by Netscape's cookie draft + // as well as formats seen on various sites + /* + private final static String[] COOKIE_DATE_FORMATS = { + "EEE',' dd-MMM-yyyy HH:mm:ss 'GMT'", + "EEE',' dd MMM yyyy HH:mm:ss 'GMT'", + "EEE MMM dd yyyy HH:mm:ss 'GMT'Z", + "EEE',' dd-MMM-yy HH:mm:ss 'GMT'", + "EEE',' dd MMM yy HH:mm:ss 'GMT'", + "EEE MMM dd yy HH:mm:ss 'GMT'Z" + }; + */ + // END Android-removed: Use libcore.net.http.HttpDate for parsing. + + // constant strings represent set-cookie header token + private final static String SET_COOKIE = "set-cookie:"; + private final static String SET_COOKIE2 = "set-cookie2:"; + + // ---------------- Ctors -------------- + + /** + * Constructs a cookie with a specified name and value. + * + * <p> The name must conform to RFC 2965. That means it can contain + * only ASCII alphanumeric characters and cannot contain commas, + * semicolons, or white space or begin with a $ character. The cookie's + * name cannot be changed after creation. + * + * <p> The value can be anything the server chooses to send. Its + * value is probably of interest only to the server. The cookie's + * value can be changed after creation with the + * {@code setValue} method. + * + * <p> By default, cookies are created according to the RFC 2965 + * cookie specification. The version can be changed with the + * {@code setVersion} method. + * + * + * @param name + * a {@code String} specifying the name of the cookie + * + * @param value + * a {@code String} specifying the value of the cookie + * + * @throws IllegalArgumentException + * if the cookie name contains illegal characters + * @throws NullPointerException + * if {@code name} is {@code null} + * + * @see #setValue + * @see #setVersion + */ + public HttpCookie(String name, String value) { + this(name, value, null /*header*/); + } + + private HttpCookie(String name, String value, String header) { + name = name.trim(); + if (name.length() == 0 || !isToken(name) || name.charAt(0) == '$') { + throw new IllegalArgumentException("Illegal cookie name"); + } + + this.name = name; + this.value = value; + toDiscard = false; + secure = false; + + whenCreated = System.currentTimeMillis(); + portlist = null; + this.header = header; + } + + /** + * Constructs cookies from set-cookie or set-cookie2 header string. + * RFC 2965 section 3.2.2 set-cookie2 syntax indicates that one header line + * may contain more than one cookie definitions, so this is a static + * utility method instead of another constructor. + * + * @param header + * a {@code String} specifying the set-cookie header. The header + * should start with "set-cookie", or "set-cookie2" token; or it + * should have no leading token at all. + * + * @return a List of cookie parsed from header line string + * + * @throws IllegalArgumentException + * if header string violates the cookie specification's syntax or + * the cookie name contains illegal characters. + * @throws NullPointerException + * if the header string is {@code null} + */ + public static List<HttpCookie> parse(String header) { + return parse(header, false); + } + + // Private version of parse() that will store the original header used to + // create the cookie, in the cookie itself. This can be useful for filtering + // Set-Cookie[2] headers, using the internal parsing logic defined in this + // class. + private static List<HttpCookie> parse(String header, boolean retainHeader) { + + int version = guessCookieVersion(header); + + // if header start with set-cookie or set-cookie2, strip it off + if (startsWithIgnoreCase(header, SET_COOKIE2)) { + header = header.substring(SET_COOKIE2.length()); + } else if (startsWithIgnoreCase(header, SET_COOKIE)) { + header = header.substring(SET_COOKIE.length()); + } + + List<HttpCookie> cookies = new java.util.ArrayList<>(); + // The Netscape cookie may have a comma in its expires attribute, while + // the comma is the delimiter in rfc 2965/2109 cookie header string. + // so the parse logic is slightly different + if (version == 0) { + // Netscape draft cookie + HttpCookie cookie = parseInternal(header, retainHeader); + cookie.setVersion(0); + cookies.add(cookie); + } else { + // rfc2965/2109 cookie + // if header string contains more than one cookie, + // it'll separate them with comma + List<String> cookieStrings = splitMultiCookies(header); + for (String cookieStr : cookieStrings) { + HttpCookie cookie = parseInternal(cookieStr, retainHeader); + cookie.setVersion(1); + cookies.add(cookie); + } + } + + return cookies; + } + + // ---------------- Public operations -------------- + + /** + * Reports whether this HTTP cookie has expired or not. + * + * @return {@code true} to indicate this HTTP cookie has expired; + * otherwise, {@code false} + */ + public boolean hasExpired() { + if (maxAge == 0) return true; + + // if not specify max-age, this cookie should be + // discarded when user agent is to be closed, but + // it is not expired. + if (maxAge == MAX_AGE_UNSPECIFIED) return false; + + long deltaSecond = (System.currentTimeMillis() - whenCreated) / 1000; + if (deltaSecond > maxAge) + return true; + else + return false; + } + + /** + * Specifies a comment that describes a cookie's purpose. + * The comment is useful if the browser presents the cookie + * to the user. Comments are not supported by Netscape Version 0 cookies. + * + * @param purpose + * a {@code String} specifying the comment to display to the user + * + * @see #getComment + */ + public void setComment(String purpose) { + comment = purpose; + } + + /** + * Returns the comment describing the purpose of this cookie, or + * {@code null} if the cookie has no comment. + * + * @return a {@code String} containing the comment, or {@code null} if none + * + * @see #setComment + */ + public String getComment() { + return comment; + } + + /** + * Specifies a comment URL that describes a cookie's purpose. + * The comment URL is useful if the browser presents the cookie + * to the user. Comment URL is RFC 2965 only. + * + * @param purpose + * a {@code String} specifying the comment URL to display to the user + * + * @see #getCommentURL + */ + public void setCommentURL(String purpose) { + commentURL = purpose; + } + + /** + * Returns the comment URL describing the purpose of this cookie, or + * {@code null} if the cookie has no comment URL. + * + * @return a {@code String} containing the comment URL, or {@code null} + * if none + * + * @see #setCommentURL + */ + public String getCommentURL() { + return commentURL; + } + + /** + * Specify whether user agent should discard the cookie unconditionally. + * This is RFC 2965 only attribute. + * + * @param discard + * {@code true} indicates to discard cookie unconditionally + * + * @see #getDiscard + */ + public void setDiscard(boolean discard) { + toDiscard = discard; + } + + /** + * Returns the discard attribute of the cookie + * + * @return a {@code boolean} to represent this cookie's discard attribute + * + * @see #setDiscard + */ + public boolean getDiscard() { + return toDiscard; + } + + /** + * Specify the portlist of the cookie, which restricts the port(s) + * to which a cookie may be sent back in a Cookie header. + * + * @param ports + * a {@code String} specify the port list, which is comma separated + * series of digits + * + * @see #getPortlist + */ + public void setPortlist(String ports) { + portlist = ports; + } + + /** + * Returns the port list attribute of the cookie + * + * @return a {@code String} contains the port list or {@code null} if none + * + * @see #setPortlist + */ + public String getPortlist() { + return portlist; + } + + /** + * Specifies the domain within which this cookie should be presented. + * + * <p> The form of the domain name is specified by RFC 2965. A domain + * name begins with a dot ({@code .foo.com}) and means that + * the cookie is visible to servers in a specified Domain Name System + * (DNS) zone (for example, {@code www.foo.com}, but not + * {@code a.b.foo.com}). By default, cookies are only returned + * to the server that sent them. + * + * @param pattern + * a {@code String} containing the domain name within which this + * cookie is visible; form is according to RFC 2965 + * + * @see #getDomain + */ + public void setDomain(String pattern) { + if (pattern != null) + domain = pattern.toLowerCase(); + else + domain = pattern; + } + + /** + * Returns the domain name set for this cookie. The form of the domain name + * is set by RFC 2965. + * + * @return a {@code String} containing the domain name + * + * @see #setDomain + */ + public String getDomain() { + return domain; + } + + /** + * Sets the maximum age of the cookie in seconds. + * + * <p> A positive value indicates that the cookie will expire + * after that many seconds have passed. Note that the value is + * the <i>maximum</i> age when the cookie will expire, not the cookie's + * current age. + * + * <p> A negative value means that the cookie is not stored persistently + * and will be deleted when the Web browser exits. A zero value causes the + * cookie to be deleted. + * + * @param expiry + * an integer specifying the maximum age of the cookie in seconds; + * if zero, the cookie should be discarded immediately; otherwise, + * the cookie's max age is unspecified. + * + * @see #getMaxAge + */ + public void setMaxAge(long expiry) { + maxAge = expiry; + } + + /** + * Returns the maximum age of the cookie, specified in seconds. By default, + * {@code -1} indicating the cookie will persist until browser shutdown. + * + * @return an integer specifying the maximum age of the cookie in seconds + * + * @see #setMaxAge + */ + public long getMaxAge() { + return maxAge; + } + + /** + * Specifies a path for the cookie to which the client should return + * the cookie. + * + * <p> The cookie is visible to all the pages in the directory + * you specify, and all the pages in that directory's subdirectories. + * A cookie's path must include the servlet that set the cookie, + * for example, <i>/catalog</i>, which makes the cookie + * visible to all directories on the server under <i>/catalog</i>. + * + * <p> Consult RFC 2965 (available on the Internet) for more + * information on setting path names for cookies. + * + * @param uri + * a {@code String} specifying a path + * + * @see #getPath + */ + public void setPath(String uri) { + path = uri; + } + + /** + * Returns the path on the server to which the browser returns this cookie. + * The cookie is visible to all subpaths on the server. + * + * @return a {@code String} specifying a path that contains a servlet name, + * for example, <i>/catalog</i> + * + * @see #setPath + */ + public String getPath() { + return path; + } + + /** + * Indicates whether the cookie should only be sent using a secure protocol, + * such as HTTPS or SSL. + * + * <p> The default value is {@code false}. + * + * @param flag + * If {@code true}, the cookie can only be sent over a secure + * protocol like HTTPS. If {@code false}, it can be sent over + * any protocol. + * + * @see #getSecure + */ + public void setSecure(boolean flag) { + secure = flag; + } + + /** + * Returns {@code true} if sending this cookie should be restricted to a + * secure protocol, or {@code false} if the it can be sent using any + * protocol. + * + * @return {@code false} if the cookie can be sent over any standard + * protocol; otherwise, {@code true} + * + * @see #setSecure + */ + public boolean getSecure() { + return secure; + } + + /** + * Returns the name of the cookie. The name cannot be changed after + * creation. + * + * @return a {@code String} specifying the cookie's name + */ + public String getName() { + return name; + } + + /** + * Assigns a new value to a cookie after the cookie is created. + * If you use a binary value, you may want to use BASE64 encoding. + * + * <p> With Version 0 cookies, values should not contain white space, + * brackets, parentheses, equals signs, commas, double quotes, slashes, + * question marks, at signs, colons, and semicolons. Empty values may not + * behave the same way on all browsers. + * + * @param newValue + * a {@code String} specifying the new value + * + * @see #getValue + */ + public void setValue(String newValue) { + value = newValue; + } + + /** + * Returns the value of the cookie. + * + * @return a {@code String} containing the cookie's present value + * + * @see #setValue + */ + public String getValue() { + return value; + } + + /** + * Returns the version of the protocol this cookie complies with. Version 1 + * complies with RFC 2965/2109, and version 0 complies with the original + * cookie specification drafted by Netscape. Cookies provided by a browser + * use and identify the browser's cookie version. + * + * @return 0 if the cookie complies with the original Netscape + * specification; 1 if the cookie complies with RFC 2965/2109 + * + * @see #setVersion + */ + public int getVersion() { + return version; + } + + /** + * Sets the version of the cookie protocol this cookie complies + * with. Version 0 complies with the original Netscape cookie + * specification. Version 1 complies with RFC 2965/2109. + * + * @param v + * 0 if the cookie should comply with the original Netscape + * specification; 1 if the cookie should comply with RFC 2965/2109 + * + * @throws IllegalArgumentException + * if {@code v} is neither 0 nor 1 + * + * @see #getVersion + */ + public void setVersion(int v) { + if (v != 0 && v != 1) { + throw new IllegalArgumentException("cookie version should be 0 or 1"); + } + + version = v; + } + + /** + * Returns {@code true} if this cookie contains the <i>HttpOnly</i> + * attribute. This means that the cookie should not be accessible to + * scripting engines, like javascript. + * + * @return {@code true} if this cookie should be considered HTTPOnly + * + * @see #setHttpOnly(boolean) + */ + public boolean isHttpOnly() { + return httpOnly; + } + + /** + * Indicates whether the cookie should be considered HTTP Only. If set to + * {@code true} it means the cookie should not be accessible to scripting + * engines like javascript. + * + * @param httpOnly + * if {@code true} make the cookie HTTP only, i.e. only visible as + * part of an HTTP request. + * + * @see #isHttpOnly() + */ + public void setHttpOnly(boolean httpOnly) { + this.httpOnly = httpOnly; + } + + /** + * The utility method to check whether a host name is in a domain or not. + * + * <p> This concept is described in the cookie specification. + * To understand the concept, some terminologies need to be defined first: + * <blockquote> + * effective host name = hostname if host name contains dot<br> + * + * or = hostname.local if not + * </blockquote> + * <p>Host A's name domain-matches host B's if: + * <blockquote><ul> + * <li>their host name strings string-compare equal; or</li> + * <li>A is a HDN string and has the form NB, where N is a non-empty + * name string, B has the form .B', and B' is a HDN string. (So, + * x.y.com domain-matches .Y.com but not Y.com.)</li> + * </ul></blockquote> + * + * <p>A host isn't in a domain (RFC 2965 sec. 3.3.2) if: + * <blockquote><ul> + * <li>The value for the Domain attribute contains no embedded dots, + * and the value is not .local.</li> + * <li>The effective host name that derives from the request-host does + * not domain-match the Domain attribute.</li> + * <li>The request-host is a HDN (not IP address) and has the form HD, + * where D is the value of the Domain attribute, and H is a string + * that contains one or more dots.</li> + * </ul></blockquote> + * + * <p>Examples: + * <blockquote><ul> + * <li>A Set-Cookie2 from request-host y.x.foo.com for Domain=.foo.com + * would be rejected, because H is y.x and contains a dot.</li> + * <li>A Set-Cookie2 from request-host x.foo.com for Domain=.foo.com + * would be accepted.</li> + * <li>A Set-Cookie2 with Domain=.com or Domain=.com., will always be + * rejected, because there is no embedded dot.</li> + * <li>A Set-Cookie2 from request-host example for Domain=.local will + * be accepted, because the effective host name for the request- + * host is example.local, and example.local domain-matches .local.</li> + * </ul></blockquote> + * + * @param domain + * the domain name to check host name with + * + * @param host + * the host name in question + * + * @return {@code true} if they domain-matches; {@code false} if not + */ + public static boolean domainMatches(String domain, String host) { + if (domain == null || host == null) + return false; + + // if there's no embedded dot in domain and domain is not .local + boolean isLocalDomain = ".local".equalsIgnoreCase(domain); + int embeddedDotInDomain = domain.indexOf('.'); + if (embeddedDotInDomain == 0) + embeddedDotInDomain = domain.indexOf('.', 1); + if (!isLocalDomain + && (embeddedDotInDomain == -1 || + embeddedDotInDomain == domain.length() - 1)) + return false; + + // if the host name contains no dot and the domain name + // is .local or host.local + int firstDotInHost = host.indexOf('.'); + if (firstDotInHost == -1 && + (isLocalDomain || + domain.equalsIgnoreCase(host + ".local"))) { + return true; + } + + int domainLength = domain.length(); + int lengthDiff = host.length() - domainLength; + if (lengthDiff == 0) { + // if the host name and the domain name are just string-compare euqal + return host.equalsIgnoreCase(domain); + } + else if (lengthDiff > 0) { + // need to check H & D component + String H = host.substring(0, lengthDiff); + String D = host.substring(lengthDiff); + + // BEGIN Android-changed: App compat reason + // 1) Disregard RFC 2965 sec. 3.3.2, the "The request-host is a HDN..." + // 2) match "foo.local" for domain ".local". + // return (H.indexOf('.') == -1 && D.equalsIgnoreCase(domain)); + return D.equalsIgnoreCase(domain) && ((domain.startsWith(".") && isFullyQualifiedDomainName(domain, 1)) + || isLocalDomain); + // END Android-changed: App compat reason + } + else if (lengthDiff == -1) { + // if domain is actually .host + return (domain.charAt(0) == '.' && + host.equalsIgnoreCase(domain.substring(1))); + } + + return false; + } + + // BEGIN Android-added: App compat reason + private static boolean isFullyQualifiedDomainName(String s, int firstCharacter) { + int dotPosition = s.indexOf('.', firstCharacter + 1); + return dotPosition != -1 && dotPosition < s.length() - 1; + } + // END Android-added: App compat reason + + /** + * Constructs a cookie header string representation of this cookie, + * which is in the format defined by corresponding cookie specification, + * but without the leading "Cookie:" token. + * + * @return a string form of the cookie. The string has the defined format + */ + @Override + public String toString() { + if (getVersion() > 0) { + return toRFC2965HeaderString(); + } else { + return toNetscapeHeaderString(); + } + } + + /** + * Test the equality of two HTTP cookies. + * + * <p> The result is {@code true} only if two cookies come from same domain + * (case-insensitive), have same name (case-insensitive), and have same path + * (case-sensitive). + * + * @return {@code true} if two HTTP cookies equal to each other; + * otherwise, {@code false} + */ + @Override + public boolean equals(Object obj) { + if (obj == this) + return true; + if (!(obj instanceof HttpCookie)) + return false; + HttpCookie other = (HttpCookie)obj; + + // One http cookie equals to another cookie (RFC 2965 sec. 3.3.3) if: + // 1. they come from same domain (case-insensitive), + // 2. have same name (case-insensitive), + // 3. and have same path (case-sensitive). + return equalsIgnoreCase(getName(), other.getName()) && + equalsIgnoreCase(getDomain(), other.getDomain()) && + Objects.equals(getPath(), other.getPath()); + } + + /** + * Returns the hash code of this HTTP cookie. The result is the sum of + * hash code value of three significant components of this cookie: name, + * domain, and path. That is, the hash code is the value of the expression: + * <blockquote> + * getName().toLowerCase().hashCode()<br> + * + getDomain().toLowerCase().hashCode()<br> + * + getPath().hashCode() + * </blockquote> + * + * @return this HTTP cookie's hash code + */ + @Override + public int hashCode() { + int h1 = name.toLowerCase().hashCode(); + int h2 = (domain!=null) ? domain.toLowerCase().hashCode() : 0; + int h3 = (path!=null) ? path.hashCode() : 0; + + return h1 + h2 + h3; + } + + /** + * Create and return a copy of this object. + * + * @return a clone of this HTTP cookie + */ + @Override + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e.getMessage()); + } + } + + // ---------------- Private operations -------------- + + // Note -- disabled for now to allow full Netscape compatibility + // from RFC 2068, token special case characters + // + // private static final String tspecials = "()<>@,;:\\\"/[]?={} \t"; + // Android-changed: App compat reason. Disallow "=\t" as token + // private static final String tspecials = ",; "; // deliberately includes space + private static final String tspecials = ",;= \t"; + + /* + * Tests a string and returns true if the string counts as a token. + * + * @param value + * the {@code String} to be tested + * + * @return {@code true} if the {@code String} is a token; + * {@code false} if it is not + */ + private static boolean isToken(String value) { + // Android-added: Reserved name can't be a token + if (RESERVED_NAMES.contains(value.toLowerCase(Locale.US))) { + return false; + } + + int len = value.length(); + + for (int i = 0; i < len; i++) { + char c = value.charAt(i); + + if (c < 0x20 || c >= 0x7f || tspecials.indexOf(c) != -1) + return false; + } + return true; + } + + /* + * Parse header string to cookie object. + * + * @param header + * header string; should contain only one NAME=VALUE pair + * + * @return an HttpCookie being extracted + * + * @throws IllegalArgumentException + * if header string violates the cookie specification + */ + private static HttpCookie parseInternal(String header, + boolean retainHeader) + { + HttpCookie cookie = null; + String namevaluePair = null; + + StringTokenizer tokenizer = new StringTokenizer(header, ";"); + + // there should always have at least on name-value pair; + // it's cookie's name + try { + namevaluePair = tokenizer.nextToken(); + int index = namevaluePair.indexOf('='); + if (index != -1) { + String name = namevaluePair.substring(0, index).trim(); + String value = namevaluePair.substring(index + 1).trim(); + if (retainHeader) + cookie = new HttpCookie(name, + stripOffSurroundingQuote(value), + header); + else + cookie = new HttpCookie(name, + stripOffSurroundingQuote(value)); + } else { + // no "=" in name-value pair; it's an error + throw new IllegalArgumentException("Invalid cookie name-value pair"); + } + } catch (NoSuchElementException ignored) { + throw new IllegalArgumentException("Empty cookie header string"); + } + + // remaining name-value pairs are cookie's attributes + while (tokenizer.hasMoreTokens()) { + namevaluePair = tokenizer.nextToken(); + int index = namevaluePair.indexOf('='); + String name, value; + if (index != -1) { + name = namevaluePair.substring(0, index).trim(); + value = namevaluePair.substring(index + 1).trim(); + } else { + name = namevaluePair.trim(); + value = null; + } + + // assign attribute to cookie + assignAttribute(cookie, name, value); + } + + return cookie; + } + + /* + * assign cookie attribute value to attribute name; + * use a map to simulate method dispatch + */ + static interface CookieAttributeAssignor { + public void assign(HttpCookie cookie, + String attrName, + String attrValue); + } + static final java.util.Map<String, CookieAttributeAssignor> assignors = + new java.util.HashMap<>(); + static { + assignors.put("comment", new CookieAttributeAssignor() { + public void assign(HttpCookie cookie, + String attrName, + String attrValue) { + if (cookie.getComment() == null) + cookie.setComment(attrValue); + } + }); + assignors.put("commenturl", new CookieAttributeAssignor() { + public void assign(HttpCookie cookie, + String attrName, + String attrValue) { + if (cookie.getCommentURL() == null) + cookie.setCommentURL(attrValue); + } + }); + assignors.put("discard", new CookieAttributeAssignor() { + public void assign(HttpCookie cookie, + String attrName, + String attrValue) { + cookie.setDiscard(true); + } + }); + assignors.put("domain", new CookieAttributeAssignor(){ + public void assign(HttpCookie cookie, + String attrName, + String attrValue) { + if (cookie.getDomain() == null) + cookie.setDomain(attrValue); + } + }); + assignors.put("max-age", new CookieAttributeAssignor(){ + public void assign(HttpCookie cookie, + String attrName, + String attrValue) { + try { + long maxage = Long.parseLong(attrValue); + if (cookie.getMaxAge() == MAX_AGE_UNSPECIFIED) + cookie.setMaxAge(maxage); + } catch (NumberFormatException ignored) { + throw new IllegalArgumentException( + "Illegal cookie max-age attribute"); + } + } + }); + assignors.put("path", new CookieAttributeAssignor(){ + public void assign(HttpCookie cookie, + String attrName, + String attrValue) { + if (cookie.getPath() == null) + cookie.setPath(attrValue); + } + }); + assignors.put("port", new CookieAttributeAssignor(){ + public void assign(HttpCookie cookie, + String attrName, + String attrValue) { + if (cookie.getPortlist() == null) + cookie.setPortlist(attrValue == null ? "" : attrValue); + } + }); + assignors.put("secure", new CookieAttributeAssignor(){ + public void assign(HttpCookie cookie, + String attrName, + String attrValue) { + cookie.setSecure(true); + } + }); + assignors.put("httponly", new CookieAttributeAssignor(){ + public void assign(HttpCookie cookie, + String attrName, + String attrValue) { + cookie.setHttpOnly(true); + } + }); + assignors.put("version", new CookieAttributeAssignor(){ + public void assign(HttpCookie cookie, + String attrName, + String attrValue) { + try { + int version = Integer.parseInt(attrValue); + cookie.setVersion(version); + } catch (NumberFormatException ignored) { + // Just ignore bogus version, it will default to 0 or 1 + } + } + }); + assignors.put("expires", new CookieAttributeAssignor(){ // Netscape only + public void assign(HttpCookie cookie, + String attrName, + String attrValue) { + if (cookie.getMaxAge() == MAX_AGE_UNSPECIFIED) { + // BEGIN Android-changed: Use HttpDate for date parsing. + // it accepts broader set of date formats. + // cookie.setMaxAge(cookie.expiryDate2DeltaSeconds(attrValue)); + // Android-changed: Altered max age calculation to avoid setting + // it to MAX_AGE_UNSPECIFIED (-1) if "expires" is one second in past. + Date date = HttpDate.parse(attrValue); + long maxAgeInSeconds = 0; + if (date != null) { + maxAgeInSeconds = (date.getTime() - cookie.whenCreated) / 1000; + // Avoid MAX_AGE_UNSPECIFIED + if (maxAgeInSeconds == MAX_AGE_UNSPECIFIED) { + maxAgeInSeconds = 0; + } + } + cookie.setMaxAge(maxAgeInSeconds); + // END Android-changed: Use HttpDate for date parsing. + } + } + }); + } + private static void assignAttribute(HttpCookie cookie, + String attrName, + String attrValue) + { + // strip off the surrounding "-sign if there's any + attrValue = stripOffSurroundingQuote(attrValue); + + CookieAttributeAssignor assignor = assignors.get(attrName.toLowerCase()); + if (assignor != null) { + assignor.assign(cookie, attrName, attrValue); + } else { + // Ignore the attribute as per RFC 2965 + } + } + + // BEGIN Android-removed: Android doesn't use JavaNetHttpCookieAccess + /* + static { + sun.misc.SharedSecrets.setJavaNetHttpCookieAccess( + new sun.misc.JavaNetHttpCookieAccess() { + public List<HttpCookie> parse(String header) { + return HttpCookie.parse(header, true); + } + + public String header(HttpCookie cookie) { + return cookie.header; + } + } + ); + } + */ + // END Android-removed: Android doesn't use JavaNetHttpCookieAccess + + /* + * Returns the original header this cookie was consructed from, if it was + * constructed by parsing a header, otherwise null. + */ + private String header() { + return header; + } + + /* + * Constructs a string representation of this cookie. The string format is + * as Netscape spec, but without leading "Cookie:" token. + */ + private String toNetscapeHeaderString() { + return getName() + "=" + getValue(); + } + + /* + * Constructs a string representation of this cookie. The string format is + * as RFC 2965/2109, but without leading "Cookie:" token. + */ + private String toRFC2965HeaderString() { + StringBuilder sb = new StringBuilder(); + + sb.append(getName()).append("=\"").append(getValue()).append('"'); + if (getPath() != null) + sb.append(";$Path=\"").append(getPath()).append('"'); + if (getDomain() != null) + sb.append(";$Domain=\"").append(getDomain()).append('"'); + if (getPortlist() != null) + sb.append(";$Port=\"").append(getPortlist()).append('"'); + + return sb.toString(); + } + + static final TimeZone GMT = TimeZone.getTimeZone("GMT"); + + // BEGIN Android-removed: not used. + /* + * @param dateString + * a date string in one of the formats defined in Netscape cookie spec + * + * @return delta seconds between this cookie's creation time and the time + * specified by dateString + * + private long expiryDate2DeltaSeconds(String dateString) { + Calendar cal = new GregorianCalendar(GMT); + for (int i = 0; i < COOKIE_DATE_FORMATS.length; i++) { + SimpleDateFormat df = new SimpleDateFormat(COOKIE_DATE_FORMATS[i], + Locale.US); + cal.set(1970, 0, 1, 0, 0, 0); + df.setTimeZone(GMT); + df.setLenient(false); + df.set2DigitYearStart(cal.getTime()); + try { + cal.setTime(df.parse(dateString)); + if (!COOKIE_DATE_FORMATS[i].contains("yyyy")) { + // 2-digit years following the standard set + // out it rfc 6265 + int year = cal.get(Calendar.YEAR); + year %= 100; + if (year < 70) { + year += 2000; + } else { + year += 1900; + } + cal.set(Calendar.YEAR, year); + } + return (cal.getTimeInMillis() - whenCreated) / 1000; + } catch (Exception e) { + // Ignore, try the next date format + } + } + return 0; + } + */ + // END Android-removed: not used. + + /* + * try to guess the cookie version through set-cookie header string + */ + private static int guessCookieVersion(String header) { + int version = 0; + + header = header.toLowerCase(); + if (header.indexOf("expires=") != -1) { + // only netscape cookie using 'expires' + version = 0; + } else if (header.indexOf("version=") != -1) { + // version is mandatory for rfc 2965/2109 cookie + version = 1; + } else if (header.indexOf("max-age") != -1) { + // rfc 2965/2109 use 'max-age' + version = 1; + } else if (startsWithIgnoreCase(header, SET_COOKIE2)) { + // only rfc 2965 cookie starts with 'set-cookie2' + version = 1; + } + + return version; + } + + private static String stripOffSurroundingQuote(String str) { + if (str != null && str.length() > 2 && + str.charAt(0) == '"' && str.charAt(str.length() - 1) == '"') { + return str.substring(1, str.length() - 1); + } + if (str != null && str.length() > 2 && + str.charAt(0) == '\'' && str.charAt(str.length() - 1) == '\'') { + return str.substring(1, str.length() - 1); + } + return str; + } + + private static boolean equalsIgnoreCase(String s, String t) { + if (s == t) return true; + if ((s != null) && (t != null)) { + return s.equalsIgnoreCase(t); + } + return false; + } + + private static boolean startsWithIgnoreCase(String s, String start) { + if (s == null || start == null) return false; + + if (s.length() >= start.length() && + start.equalsIgnoreCase(s.substring(0, start.length()))) { + return true; + } + + return false; + } + + /* + * Split cookie header string according to rfc 2965: + * 1) split where it is a comma; + * 2) but not the comma surrounding by double-quotes, which is the comma + * inside port list or embeded URIs. + * + * @param header + * the cookie header string to split + * + * @return list of strings; never null + */ + private static List<String> splitMultiCookies(String header) { + List<String> cookies = new java.util.ArrayList<String>(); + int quoteCount = 0; + int p, q; + + for (p = 0, q = 0; p < header.length(); p++) { + char c = header.charAt(p); + if (c == '"') quoteCount++; + if (c == ',' && (quoteCount % 2 == 0)) { + // it is comma and not surrounding by double-quotes + cookies.add(header.substring(q, p)); + q = p + 1; + } + } + + cookies.add(header.substring(q)); + + return cookies; + } +}
diff --git a/java/net/HttpRetryException.java b/java/net/HttpRetryException.java new file mode 100644 index 0000000..d498a65 --- /dev/null +++ b/java/net/HttpRetryException.java
@@ -0,0 +1,99 @@ +/* + * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.IOException; + +/** + * Thrown to indicate that a HTTP request needs to be retried + * but cannot be retried automatically, due to streaming mode + * being enabled. + * + * @author Michael McMahon + * @since 1.5 + */ +public +class HttpRetryException extends IOException { + private static final long serialVersionUID = -9186022286469111381L; + + private int responseCode; + private String location; + + /** + * Constructs a new {@code HttpRetryException} from the + * specified response code and exception detail message + * + * @param detail the detail message. + * @param code the HTTP response code from server. + */ + public HttpRetryException(String detail, int code) { + super(detail); + responseCode = code; + } + + /** + * Constructs a new {@code HttpRetryException} with detail message + * responseCode and the contents of the Location response header field. + * + * @param detail the detail message. + * @param code the HTTP response code from server. + * @param location the URL to be redirected to + */ + public HttpRetryException(String detail, int code, String location) { + super (detail); + responseCode = code; + this.location = location; + } + + /** + * Returns the http response code + * + * @return The http response code. + */ + public int responseCode() { + return responseCode; + } + + /** + * Returns a string explaining why the http request could + * not be retried. + * + * @return The reason string + */ + public String getReason() { + return super.getMessage(); + } + + /** + * Returns the value of the Location header field if the + * error resulted from redirection. + * + * @return The location string + */ + public String getLocation() { + return location; + } +}
diff --git a/java/net/HttpURLConnection.java b/java/net/HttpURLConnection.java new file mode 100644 index 0000000..b72168b --- /dev/null +++ b/java/net/HttpURLConnection.java
@@ -0,0 +1,1023 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.InputStream; +import java.io.IOException; +import java.security.Permission; +import java.util.Date; + +// Android-changed: top-level documentation substantially changed/rewritten. +/** + * A URLConnection with support for HTTP-specific features. See + * <A HREF="http://www.w3.org/pub/WWW/Protocols/"> the spec </A> for + * details. + * <p> + * + * <p>Uses of this class follow a pattern: + * <ol> + * <li>Obtain a new {@code HttpURLConnection} by calling {@link + * URL#openConnection() URL.openConnection()} and casting the result to + * {@code HttpURLConnection}. + * <li>Prepare the request. The primary property of a request is its URI. + * Request headers may also include metadata such as credentials, preferred + * content types, and session cookies. + * <li>Optionally upload a request body. Instances must be configured with + * {@link #setDoOutput(boolean) setDoOutput(true)} if they include a + * request body. Transmit data by writing to the stream returned by {@link + * #getOutputStream()}. + * <li>Read the response. Response headers typically include metadata such as + * the response body's content type and length, modified dates and session + * cookies. The response body may be read from the stream returned by {@link + * #getInputStream()}. If the response has no body, that method returns an + * empty stream. + * <li>Disconnect. Once the response body has been read, the {@code + * HttpURLConnection} should be closed by calling {@link #disconnect()}. + * Disconnecting releases the resources held by a connection so they may + * be closed or reused. + * </ol> + * + * <p>For example, to retrieve the webpage at {@code http://www.android.com/}: + * <pre> {@code + * URL url = new URL("http://www.android.com/"); + * HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); + * try { + * InputStream in = new BufferedInputStream(urlConnection.getInputStream()); + * readStream(in); + * } finally { + * urlConnection.disconnect(); + * } + * }</pre> + * + * <h3>Secure Communication with HTTPS</h3> + * Calling {@link URL#openConnection()} on a URL with the "https" + * scheme will return an {@code HttpsURLConnection}, which allows for + * overriding the default {@link javax.net.ssl.HostnameVerifier + * HostnameVerifier} and {@link javax.net.ssl.SSLSocketFactory + * SSLSocketFactory}. An application-supplied {@code SSLSocketFactory} + * created from an {@link javax.net.ssl.SSLContext SSLContext} can + * provide a custom {@link javax.net.ssl.X509TrustManager + * X509TrustManager} for verifying certificate chains and a custom + * {@link javax.net.ssl.X509KeyManager X509KeyManager} for supplying + * client certificates. See {@link javax.net.ssl.HttpsURLConnection + * HttpsURLConnection} for more details. + * + * <h3>Response Handling</h3> + * {@code HttpURLConnection} will follow up to five HTTP redirects. It will + * follow redirects from one origin server to another. This implementation + * doesn't follow redirects from HTTPS to HTTP or vice versa. + * + * <p>If the HTTP response indicates that an error occurred, {@link + * #getInputStream()} will throw an {@link IOException}. Use {@link + * #getErrorStream()} to read the error response. The headers can be read in + * the normal way using {@link #getHeaderFields()}, + * + * <h3>Posting Content</h3> + * To upload data to a web server, configure the connection for output using + * {@link #setDoOutput(boolean) setDoOutput(true)}. + * + * <p>For best performance, you should call either {@link + * #setFixedLengthStreamingMode(int)} when the body length is known in advance, + * or {@link #setChunkedStreamingMode(int)} when it is not. Otherwise {@code + * HttpURLConnection} will be forced to buffer the complete request body in + * memory before it is transmitted, wasting (and possibly exhausting) heap and + * increasing latency. + * + * <p>For example, to perform an upload: <pre> {@code + * HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); + * try { + * urlConnection.setDoOutput(true); + * urlConnection.setChunkedStreamingMode(0); + * + * OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream()); + * writeStream(out); + * + * InputStream in = new BufferedInputStream(urlConnection.getInputStream()); + * readStream(in); + * } finally { + * urlConnection.disconnect(); + * } + * }</pre> + * + * <h3>Performance</h3> + * The input and output streams returned by this class are <strong>not + * buffered</strong>. Most callers should wrap the returned streams with {@link + * java.io.BufferedInputStream BufferedInputStream} or {@link + * java.io.BufferedOutputStream BufferedOutputStream}. Callers that do only bulk + * reads or writes may omit buffering. + * + * <p>When transferring large amounts of data to or from a server, use streams + * to limit how much data is in memory at once. Unless you need the entire + * body to be in memory at once, process it as a stream (rather than storing + * the complete body as a single byte array or string). + * + * <p>To reduce latency, this class may reuse the same underlying {@code Socket} + * for multiple request/response pairs. As a result, HTTP connections may be + * held open longer than necessary. Calls to {@link #disconnect()} may return + * the socket to a pool of connected sockets. + * + * <p>By default, this implementation of {@code HttpURLConnection} requests that + * servers use gzip compression and it automatically decompresses the data for + * callers of {@link #getInputStream()}. The Content-Encoding and Content-Length + * response headers are cleared in this case. Gzip compression can be disabled by + * setting the acceptable encodings in the request header: <pre> {@code + * urlConnection.setRequestProperty("Accept-Encoding", "identity"); + * }</pre> + * + * <p>Setting the Accept-Encoding request header explicitly disables automatic + * decompression and leaves the response headers intact; callers must handle + * decompression as needed, according to the Content-Encoding header of the + * response. + * + * <p>{@link #getContentLength()} returns the number of bytes transmitted and + * cannot be used to predict how many bytes can be read from + * {@link #getInputStream()} for compressed streams. Instead, read that stream + * until it is exhausted, i.e. when {@link InputStream#read} returns -1. + * + * <h3>Handling Network Sign-On</h3> + * Some Wi-Fi networks block Internet access until the user clicks through a + * sign-on page. Such sign-on pages are typically presented by using HTTP + * redirects. You can use {@link #getURL()} to test if your connection has been + * unexpectedly redirected. This check is not valid until <strong>after</strong> + * the response headers have been received, which you can trigger by calling + * {@link #getHeaderFields()} or {@link #getInputStream()}. For example, to + * check that a response was not redirected to an unexpected host: + * <pre> {@code + * HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); + * try { + * InputStream in = new BufferedInputStream(urlConnection.getInputStream()); + * if (!url.getHost().equals(urlConnection.getURL().getHost())) { + * // we were redirected! Kick the user out to the browser to sign on? + * } + * ... + * } finally { + * urlConnection.disconnect(); + * } + * }</pre> + * + * <h3>HTTP Authentication</h3> + * {@code HttpURLConnection} supports <a + * href="http://www.ietf.org/rfc/rfc2617">HTTP basic authentication</a>. Use + * {@link Authenticator} to set the VM-wide authentication handler: + * <pre> {@code + * Authenticator.setDefault(new Authenticator() { + * protected PasswordAuthentication getPasswordAuthentication() { + * return new PasswordAuthentication(username, password.toCharArray()); + * } + * }); + * }</pre> + * Unless paired with HTTPS, this is <strong>not</strong> a secure mechanism for + * user authentication. In particular, the username, password, request and + * response are all transmitted over the network without encryption. + * + * <h3>Sessions with Cookies</h3> + * To establish and maintain a potentially long-lived session between client + * and server, {@code HttpURLConnection} includes an extensible cookie manager. + * Enable VM-wide cookie management using {@link CookieHandler} and {@link + * CookieManager}: <pre> {@code + * CookieManager cookieManager = new CookieManager(); + * CookieHandler.setDefault(cookieManager); + * }</pre> + * By default, {@code CookieManager} accepts cookies from the <a + * href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec1.html">origin + * server</a> only. Two other policies are included: {@link + * CookiePolicy#ACCEPT_ALL} and {@link CookiePolicy#ACCEPT_NONE}. Implement + * {@link CookiePolicy} to define a custom policy. + * + * <p>The default {@code CookieManager} keeps all accepted cookies in memory. It + * will forget these cookies when the VM exits. Implement {@link CookieStore} to + * define a custom cookie store. + * + * <p>In addition to the cookies set by HTTP responses, you may set cookies + * programmatically. To be included in HTTP request headers, cookies must have + * the domain and path properties set. + * + * <p>By default, new instances of {@code HttpCookie} work only with servers + * that support <a href="http://www.ietf.org/rfc/rfc2965.txt">RFC 2965</a> + * cookies. Many web servers support only the older specification, <a + * href="http://www.ietf.org/rfc/rfc2109.txt">RFC 2109</a>. For compatibility + * with the most web servers, set the cookie version to 0. + * + * <p>For example, to receive {@code www.twitter.com} in French: <pre> {@code + * HttpCookie cookie = new HttpCookie("lang", "fr"); + * cookie.setDomain("twitter.com"); + * cookie.setPath("/"); + * cookie.setVersion(0); + * cookieManager.getCookieStore().add(new URI("http://twitter.com/"), cookie); + * }</pre> + * + * <h3>HTTP Methods</h3> + * <p>{@code HttpURLConnection} uses the {@code GET} method by default. It will + * use {@code POST} if {@link #setDoOutput setDoOutput(true)} has been called. + * Other HTTP methods ({@code OPTIONS}, {@code HEAD}, {@code PUT}, {@code + * DELETE} and {@code TRACE}) can be used with {@link #setRequestMethod}. + * + * <h3>Proxies</h3> + * By default, this class will connect directly to the <a + * href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec1.html">origin + * server</a>. It can also connect via an {@link Proxy.Type#HTTP HTTP} or {@link + * Proxy.Type#SOCKS SOCKS} proxy. To use a proxy, use {@link + * URL#openConnection(Proxy) URL.openConnection(Proxy)} when creating the + * connection. + * + * <h3>IPv6 Support</h3> + * <p>This class includes transparent support for IPv6. For hosts with both IPv4 + * and IPv6 addresses, it will attempt to connect to each of a host's addresses + * until a connection is established. + * + * <h3>Response Caching</h3> + * Android 4.0 (Ice Cream Sandwich, API level 15) includes a response cache. See + * {@code android.net.http.HttpResponseCache} for instructions on enabling HTTP + * caching in your application. + * + * <h3>Avoiding Bugs In Earlier Releases</h3> + * Prior to Android 2.2 (Froyo), this class had some frustrating bugs. In + * particular, calling {@code close()} on a readable {@code InputStream} could + * <a href="http://code.google.com/p/android/issues/detail?id=2939">poison the + * connection pool</a>. Work around this by disabling connection pooling: + * <pre> {@code + * private void disableConnectionReuseIfNecessary() { + * // Work around pre-Froyo bugs in HTTP connection reuse. + * if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) { + * System.setProperty("http.keepAlive", "false"); + * } + * }}</pre> + * + * <p>Each instance of {@code HttpURLConnection} may be used for one + * request/response pair. Instances of this class are not thread safe. + * + * @see java.net.HttpURLConnection#disconnect() + * @since JDK1.1 + */ +abstract public class HttpURLConnection extends URLConnection { + /* instance variables */ + + /** + * The HTTP method (GET,POST,PUT,etc.). + */ + protected String method = "GET"; + + /** + * The chunk-length when using chunked encoding streaming mode for output. + * A value of {@code -1} means chunked encoding is disabled for output. + * @since 1.5 + */ + protected int chunkLength = -1; + + /** + * The fixed content-length when using fixed-length streaming mode. + * A value of {@code -1} means fixed-length streaming mode is disabled + * for output. + * + * <P> <B>NOTE:</B> {@link #fixedContentLengthLong} is recommended instead + * of this field, as it allows larger content lengths to be set. + * + * @since 1.5 + */ + protected int fixedContentLength = -1; + + /** + * The fixed content-length when using fixed-length streaming mode. + * A value of {@code -1} means fixed-length streaming mode is disabled + * for output. + * + * @since 1.7 + */ + protected long fixedContentLengthLong = -1; + + /** + * Returns the key for the {@code n}<sup>th</sup> header field. + * Some implementations may treat the {@code 0}<sup>th</sup> + * header field as special, i.e. as the status line returned by the HTTP + * server. In this case, {@link #getHeaderField(int) getHeaderField(0)} returns the status + * line, but {@code getHeaderFieldKey(0)} returns null. + * + * @param n an index, where {@code n >=0}. + * @return the key for the {@code n}<sup>th</sup> header field, + * or {@code null} if the key does not exist. + */ + public String getHeaderFieldKey (int n) { + return null; + } + + /** + * This method is used to enable streaming of a HTTP request body + * without internal buffering, when the content length is known in + * advance. + * <p> + * An exception will be thrown if the application + * attempts to write more data than the indicated + * content-length, or if the application closes the OutputStream + * before writing the indicated amount. + * <p> + * When output streaming is enabled, authentication + * and redirection cannot be handled automatically. + * A HttpRetryException will be thrown when reading + * the response if authentication or redirection are required. + * This exception can be queried for the details of the error. + * <p> + * This method must be called before the URLConnection is connected. + * <p> + * <B>NOTE:</B> {@link #setFixedLengthStreamingMode(long)} is recommended + * instead of this method as it allows larger content lengths to be set. + * + * @param contentLength The number of bytes which will be written + * to the OutputStream. + * + * @throws IllegalStateException if URLConnection is already connected + * or if a different streaming mode is already enabled. + * + * @throws IllegalArgumentException if a content length less than + * zero is specified. + * + * @see #setChunkedStreamingMode(int) + * @since 1.5 + */ + public void setFixedLengthStreamingMode (int contentLength) { + if (connected) { + throw new IllegalStateException ("Already connected"); + } + if (chunkLength != -1) { + throw new IllegalStateException ("Chunked encoding streaming mode set"); + } + if (contentLength < 0) { + throw new IllegalArgumentException ("invalid content length"); + } + fixedContentLength = contentLength; + } + + /** + * This method is used to enable streaming of a HTTP request body + * without internal buffering, when the content length is known in + * advance. + * + * <P> An exception will be thrown if the application attempts to write + * more data than the indicated content-length, or if the application + * closes the OutputStream before writing the indicated amount. + * + * <P> When output streaming is enabled, authentication and redirection + * cannot be handled automatically. A {@linkplain HttpRetryException} will + * be thrown when reading the response if authentication or redirection + * are required. This exception can be queried for the details of the + * error. + * + * <P> This method must be called before the URLConnection is connected. + * + * <P> The content length set by invoking this method takes precedence + * over any value set by {@link #setFixedLengthStreamingMode(int)}. + * + * @param contentLength + * The number of bytes which will be written to the OutputStream. + * + * @throws IllegalStateException + * if URLConnection is already connected or if a different + * streaming mode is already enabled. + * + * @throws IllegalArgumentException + * if a content length less than zero is specified. + * + * @since 1.7 + */ + public void setFixedLengthStreamingMode(long contentLength) { + if (connected) { + throw new IllegalStateException("Already connected"); + } + if (chunkLength != -1) { + throw new IllegalStateException( + "Chunked encoding streaming mode set"); + } + if (contentLength < 0) { + throw new IllegalArgumentException("invalid content length"); + } + fixedContentLengthLong = contentLength; + } + + /* Default chunk size (including chunk header) if not specified; + * we want to keep this in sync with the one defined in + * sun.net.www.http.ChunkedOutputStream + */ + private static final int DEFAULT_CHUNK_SIZE = 4096; + + /** + * This method is used to enable streaming of a HTTP request body + * without internal buffering, when the content length is <b>not</b> + * known in advance. In this mode, chunked transfer encoding + * is used to send the request body. Note, not all HTTP servers + * support this mode. + * <p> + * When output streaming is enabled, authentication + * and redirection cannot be handled automatically. + * A HttpRetryException will be thrown when reading + * the response if authentication or redirection are required. + * This exception can be queried for the details of the error. + * <p> + * This method must be called before the URLConnection is connected. + * + * @param chunklen The number of bytes to write in each chunk. + * If chunklen is less than or equal to zero, a default + * value will be used. + * + * @throws IllegalStateException if URLConnection is already connected + * or if a different streaming mode is already enabled. + * + * @see #setFixedLengthStreamingMode(int) + * @since 1.5 + */ + public void setChunkedStreamingMode (int chunklen) { + if (connected) { + throw new IllegalStateException ("Can't set streaming mode: already connected"); + } + if (fixedContentLength != -1 || fixedContentLengthLong != -1) { + throw new IllegalStateException ("Fixed length streaming mode set"); + } + chunkLength = chunklen <=0? DEFAULT_CHUNK_SIZE : chunklen; + } + + /** + * Returns the value for the {@code n}<sup>th</sup> header field. + * Some implementations may treat the {@code 0}<sup>th</sup> + * header field as special, i.e. as the status line returned by the HTTP + * server. + * <p> + * This method can be used in conjunction with the + * {@link #getHeaderFieldKey getHeaderFieldKey} method to iterate through all + * the headers in the message. + * + * @param n an index, where {@code n>=0}. + * @return the value of the {@code n}<sup>th</sup> header field, + * or {@code null} if the value does not exist. + * @see java.net.HttpURLConnection#getHeaderFieldKey(int) + */ + public String getHeaderField(int n) { + return null; + } + + /** + * An {@code int} representing the three digit HTTP Status-Code. + * <ul> + * <li> 1xx: Informational + * <li> 2xx: Success + * <li> 3xx: Redirection + * <li> 4xx: Client Error + * <li> 5xx: Server Error + * </ul> + */ + protected int responseCode = -1; + + /** + * The HTTP response message. + */ + protected String responseMessage = null; + + /* static variables */ + + /* do we automatically follow redirects? The default is true. */ + private static boolean followRedirects = true; + + /** + * If {@code true}, the protocol will automatically follow redirects. + * If {@code false}, the protocol will not automatically follow + * redirects. + * <p> + * This field is set by the {@code setInstanceFollowRedirects} + * method. Its value is returned by the {@code getInstanceFollowRedirects} + * method. + * <p> + * Its default value is based on the value of the static followRedirects + * at HttpURLConnection construction time. + * + * @see java.net.HttpURLConnection#setInstanceFollowRedirects(boolean) + * @see java.net.HttpURLConnection#getInstanceFollowRedirects() + * @see java.net.HttpURLConnection#setFollowRedirects(boolean) + */ + protected boolean instanceFollowRedirects = followRedirects; + + /* valid HTTP methods */ + private static final String[] methods = { + "GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE" + }; + + /** + * Constructor for the HttpURLConnection. + * @param u the URL + */ + protected HttpURLConnection (URL u) { + super(u); + } + + /** + * Sets whether HTTP redirects (requests with response code 3xx) should + * be automatically followed by this class. True by default. Applets + * cannot change this variable. + * <p> + * If there is a security manager, this method first calls + * the security manager's {@code checkSetFactory} method + * to ensure the operation is allowed. + * This could result in a SecurityException. + * + * @param set a {@code boolean} indicating whether or not + * to follow HTTP redirects. + * @exception SecurityException if a security manager exists and its + * {@code checkSetFactory} method doesn't + * allow the operation. + * @see SecurityManager#checkSetFactory + * @see #getFollowRedirects() + */ + public static void setFollowRedirects(boolean set) { + SecurityManager sec = System.getSecurityManager(); + if (sec != null) { + // seems to be the best check here... + sec.checkSetFactory(); + } + followRedirects = set; + } + + /** + * Returns a {@code boolean} indicating + * whether or not HTTP redirects (3xx) should + * be automatically followed. + * + * @return {@code true} if HTTP redirects should + * be automatically followed, {@code false} if not. + * @see #setFollowRedirects(boolean) + */ + public static boolean getFollowRedirects() { + return followRedirects; + } + + /** + * Sets whether HTTP redirects (requests with response code 3xx) should + * be automatically followed by this {@code HttpURLConnection} + * instance. + * <p> + * The default value comes from followRedirects, which defaults to + * true. + * + * @param followRedirects a {@code boolean} indicating + * whether or not to follow HTTP redirects. + * + * @see java.net.HttpURLConnection#instanceFollowRedirects + * @see #getInstanceFollowRedirects + * @since 1.3 + */ + public void setInstanceFollowRedirects(boolean followRedirects) { + instanceFollowRedirects = followRedirects; + } + + /** + * Returns the value of this {@code HttpURLConnection}'s + * {@code instanceFollowRedirects} field. + * + * @return the value of this {@code HttpURLConnection}'s + * {@code instanceFollowRedirects} field. + * @see java.net.HttpURLConnection#instanceFollowRedirects + * @see #setInstanceFollowRedirects(boolean) + * @since 1.3 + */ + public boolean getInstanceFollowRedirects() { + return instanceFollowRedirects; + } + + /** + * Set the method for the URL request, one of: + * <UL> + * <LI>GET + * <LI>POST + * <LI>HEAD + * <LI>OPTIONS + * <LI>PUT + * <LI>DELETE + * <LI>TRACE + * </UL> are legal, subject to protocol restrictions. The default + * method is GET. + * + * @param method the HTTP method + * @exception ProtocolException if the method cannot be reset or if + * the requested method isn't valid for HTTP. + * @exception SecurityException if a security manager is set and the + * method is "TRACE", but the "allowHttpTrace" + * NetPermission is not granted. + * @see #getRequestMethod() + */ + public void setRequestMethod(String method) throws ProtocolException { + if (connected) { + throw new ProtocolException("Can't reset method: already connected"); + } + // This restriction will prevent people from using this class to + // experiment w/ new HTTP methods using java. But it should + // be placed for security - the request String could be + // arbitrarily long. + + for (int i = 0; i < methods.length; i++) { + if (methods[i].equals(method)) { + if (method.equals("TRACE")) { + SecurityManager s = System.getSecurityManager(); + if (s != null) { + s.checkPermission(new NetPermission("allowHttpTrace")); + } + } + this.method = method; + return; + } + } + throw new ProtocolException("Invalid HTTP method: " + method); + } + + /** + * Get the request method. + * @return the HTTP request method + * @see #setRequestMethod(java.lang.String) + */ + public String getRequestMethod() { + return method; + } + + /** + * Gets the status code from an HTTP response message. + * For example, in the case of the following status lines: + * <PRE> + * HTTP/1.0 200 OK + * HTTP/1.0 401 Unauthorized + * </PRE> + * It will return 200 and 401 respectively. + * Returns -1 if no code can be discerned + * from the response (i.e., the response is not valid HTTP). + * @throws IOException if an error occurred connecting to the server. + * @return the HTTP Status-Code, or -1 + */ + public int getResponseCode() throws IOException { + /* + * We're got the response code already + */ + if (responseCode != -1) { + return responseCode; + } + + /* + * Ensure that we have connected to the server. Record + * exception as we need to re-throw it if there isn't + * a status line. + */ + Exception exc = null; + try { + getInputStream(); + } catch (Exception e) { + exc = e; + } + + /* + * If we can't a status-line then re-throw any exception + * that getInputStream threw. + */ + String statusLine = getHeaderField(0); + if (statusLine == null) { + if (exc != null) { + if (exc instanceof RuntimeException) + throw (RuntimeException)exc; + else + throw (IOException)exc; + } + return -1; + } + + /* + * Examine the status-line - should be formatted as per + * section 6.1 of RFC 2616 :- + * + * Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase + * + * If status line can't be parsed return -1. + */ + if (statusLine.startsWith("HTTP/1.")) { + int codePos = statusLine.indexOf(' '); + if (codePos > 0) { + + int phrasePos = statusLine.indexOf(' ', codePos+1); + if (phrasePos > 0 && phrasePos < statusLine.length()) { + responseMessage = statusLine.substring(phrasePos+1); + } + + // deviation from RFC 2616 - don't reject status line + // if SP Reason-Phrase is not included. + if (phrasePos < 0) + phrasePos = statusLine.length(); + + try { + responseCode = Integer.parseInt + (statusLine.substring(codePos+1, phrasePos)); + return responseCode; + } catch (NumberFormatException e) { } + } + } + return -1; + } + + /** + * Gets the HTTP response message, if any, returned along with the + * response code from a server. From responses like: + * <PRE> + * HTTP/1.0 200 OK + * HTTP/1.0 404 Not Found + * </PRE> + * Extracts the Strings "OK" and "Not Found" respectively. + * Returns null if none could be discerned from the responses + * (the result was not valid HTTP). + * @throws IOException if an error occurred connecting to the server. + * @return the HTTP response message, or {@code null} + */ + public String getResponseMessage() throws IOException { + getResponseCode(); + return responseMessage; + } + + @SuppressWarnings("deprecation") + public long getHeaderFieldDate(String name, long Default) { + String dateString = getHeaderField(name); + try { + if (dateString.indexOf("GMT") == -1) { + dateString = dateString+" GMT"; + } + return Date.parse(dateString); + } catch (Exception e) { + } + return Default; + } + + + /** + * Indicates that other requests to the server + * are unlikely in the near future. Calling disconnect() + * should not imply that this HttpURLConnection + * instance can be reused for other requests. + */ + public abstract void disconnect(); + + /** + * Indicates if the connection is going through a proxy. + * @return a boolean indicating if the connection is + * using a proxy. + */ + public abstract boolean usingProxy(); + + /** + * Returns a {@link SocketPermission} object representing the + * permission necessary to connect to the destination host and port. + * + * @exception IOException if an error occurs while computing + * the permission. + * + * @return a {@code SocketPermission} object representing the + * permission necessary to connect to the destination + * host and port. + */ + public Permission getPermission() throws IOException { + int port = url.getPort(); + port = port < 0 ? 80 : port; + String host = url.getHost() + ":" + port; + Permission permission = new SocketPermission(host, "connect"); + return permission; + } + + /** + * Returns the error stream if the connection failed + * but the server sent useful data nonetheless. The + * typical example is when an HTTP server responds + * with a 404, which will cause a FileNotFoundException + * to be thrown in connect, but the server sent an HTML + * help page with suggestions as to what to do. + * + * <p>This method will not cause a connection to be initiated. If + * the connection was not connected, or if the server did not have + * an error while connecting or if the server had an error but + * no error data was sent, this method will return null. This is + * the default. + * + * @return an error stream if any, null if there have been no + * errors, the connection is not connected or the server sent no + * useful data. + */ + public InputStream getErrorStream() { + return null; + } + + /** + * The response codes for HTTP, as of version 1.1. + */ + + // REMIND: do we want all these?? + // Others not here that we do want?? + + /* 2XX: generally "OK" */ + + /** + * HTTP Status-Code 200: OK. + */ + public static final int HTTP_OK = 200; + + /** + * HTTP Status-Code 201: Created. + */ + public static final int HTTP_CREATED = 201; + + /** + * HTTP Status-Code 202: Accepted. + */ + public static final int HTTP_ACCEPTED = 202; + + /** + * HTTP Status-Code 203: Non-Authoritative Information. + */ + public static final int HTTP_NOT_AUTHORITATIVE = 203; + + /** + * HTTP Status-Code 204: No Content. + */ + public static final int HTTP_NO_CONTENT = 204; + + /** + * HTTP Status-Code 205: Reset Content. + */ + public static final int HTTP_RESET = 205; + + /** + * HTTP Status-Code 206: Partial Content. + */ + public static final int HTTP_PARTIAL = 206; + + /* 3XX: relocation/redirect */ + + /** + * HTTP Status-Code 300: Multiple Choices. + */ + public static final int HTTP_MULT_CHOICE = 300; + + /** + * HTTP Status-Code 301: Moved Permanently. + */ + public static final int HTTP_MOVED_PERM = 301; + + /** + * HTTP Status-Code 302: Temporary Redirect. + */ + public static final int HTTP_MOVED_TEMP = 302; + + /** + * HTTP Status-Code 303: See Other. + */ + public static final int HTTP_SEE_OTHER = 303; + + /** + * HTTP Status-Code 304: Not Modified. + */ + public static final int HTTP_NOT_MODIFIED = 304; + + /** + * HTTP Status-Code 305: Use Proxy. + */ + public static final int HTTP_USE_PROXY = 305; + + /* 4XX: client error */ + + /** + * HTTP Status-Code 400: Bad Request. + */ + public static final int HTTP_BAD_REQUEST = 400; + + /** + * HTTP Status-Code 401: Unauthorized. + */ + public static final int HTTP_UNAUTHORIZED = 401; + + /** + * HTTP Status-Code 402: Payment Required. + */ + public static final int HTTP_PAYMENT_REQUIRED = 402; + + /** + * HTTP Status-Code 403: Forbidden. + */ + public static final int HTTP_FORBIDDEN = 403; + + /** + * HTTP Status-Code 404: Not Found. + */ + public static final int HTTP_NOT_FOUND = 404; + + /** + * HTTP Status-Code 405: Method Not Allowed. + */ + public static final int HTTP_BAD_METHOD = 405; + + /** + * HTTP Status-Code 406: Not Acceptable. + */ + public static final int HTTP_NOT_ACCEPTABLE = 406; + + /** + * HTTP Status-Code 407: Proxy Authentication Required. + */ + public static final int HTTP_PROXY_AUTH = 407; + + /** + * HTTP Status-Code 408: Request Time-Out. + */ + public static final int HTTP_CLIENT_TIMEOUT = 408; + + /** + * HTTP Status-Code 409: Conflict. + */ + public static final int HTTP_CONFLICT = 409; + + /** + * HTTP Status-Code 410: Gone. + */ + public static final int HTTP_GONE = 410; + + /** + * HTTP Status-Code 411: Length Required. + */ + public static final int HTTP_LENGTH_REQUIRED = 411; + + /** + * HTTP Status-Code 412: Precondition Failed. + */ + public static final int HTTP_PRECON_FAILED = 412; + + /** + * HTTP Status-Code 413: Request Entity Too Large. + */ + public static final int HTTP_ENTITY_TOO_LARGE = 413; + + /** + * HTTP Status-Code 414: Request-URI Too Large. + */ + public static final int HTTP_REQ_TOO_LONG = 414; + + /** + * HTTP Status-Code 415: Unsupported Media Type. + */ + public static final int HTTP_UNSUPPORTED_TYPE = 415; + + /* 5XX: server error */ + + /** + * HTTP Status-Code 500: Internal Server Error. + * @deprecated it is misplaced and shouldn't have existed. + */ + @Deprecated + public static final int HTTP_SERVER_ERROR = 500; + + /** + * HTTP Status-Code 500: Internal Server Error. + */ + public static final int HTTP_INTERNAL_ERROR = 500; + + /** + * HTTP Status-Code 501: Not Implemented. + */ + public static final int HTTP_NOT_IMPLEMENTED = 501; + + /** + * HTTP Status-Code 502: Bad Gateway. + */ + public static final int HTTP_BAD_GATEWAY = 502; + + /** + * HTTP Status-Code 503: Service Unavailable. + */ + public static final int HTTP_UNAVAILABLE = 503; + + /** + * HTTP Status-Code 504: Gateway Timeout. + */ + public static final int HTTP_GATEWAY_TIMEOUT = 504; + + /** + * HTTP Status-Code 505: HTTP Version Not Supported. + */ + public static final int HTTP_VERSION = 505; + +}
diff --git a/java/net/IDN.java b/java/net/IDN.java new file mode 100644 index 0000000..a18c3a8 --- /dev/null +++ b/java/net/IDN.java
@@ -0,0 +1,498 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.net; + +import android.icu.text.IDNA; + +/** + * Provides methods to convert internationalized domain names (IDNs) between + * a normal Unicode representation and an ASCII Compatible Encoding (ACE) representation. + * Internationalized domain names can use characters from the entire range of + * Unicode, while traditional domain names are restricted to ASCII characters. + * ACE is an encoding of Unicode strings that uses only ASCII characters and + * can be used with software (such as the Domain Name System) that only + * understands traditional domain names. + * + * <p>Internationalized domain names are defined in <a href="http://www.ietf.org/rfc/rfc3490.txt">RFC 3490</a>. + * RFC 3490 defines two operations: ToASCII and ToUnicode. These 2 operations employ + * <a href="http://www.ietf.org/rfc/rfc3491.txt">Nameprep</a> algorithm, which is a + * profile of <a href="http://www.ietf.org/rfc/rfc3454.txt">Stringprep</a>, and + * <a href="http://www.ietf.org/rfc/rfc3492.txt">Punycode</a> algorithm to convert + * domain name string back and forth. + * + * <p>The behavior of aforementioned conversion process can be adjusted by various flags: + * <ul> + * <li>If the ALLOW_UNASSIGNED flag is used, the domain name string to be converted + * can contain code points that are unassigned in Unicode 3.2, which is the + * Unicode version on which IDN conversion is based. If the flag is not used, + * the presence of such unassigned code points is treated as an error. + * <li>If the USE_STD3_ASCII_RULES flag is used, ASCII strings are checked against <a href="http://www.ietf.org/rfc/rfc1122.txt">RFC 1122</a> and <a href="http://www.ietf.org/rfc/rfc1123.txt">RFC 1123</a>. + * It is an error if they don't meet the requirements. + * </ul> + * These flags can be logically OR'ed together. + * + * <p>The security consideration is important with respect to internationalization + * domain name support. For example, English domain names may be <i>homographed</i> + * - maliciously misspelled by substitution of non-Latin letters. + * <a href="http://www.unicode.org/reports/tr36/">Unicode Technical Report #36</a> + * discusses security issues of IDN support as well as possible solutions. + * Applications are responsible for taking adequate security measures when using + * international domain names. + * + * @author Edward Wang + * @since 1.6 + * + */ +public final class IDN { + /** + * Flag to allow processing of unassigned code points + */ + public static final int ALLOW_UNASSIGNED = 0x01; + + /** + * Flag to turn on the check against STD-3 ASCII rules + */ + public static final int USE_STD3_ASCII_RULES = 0x02; + + + /** + * Translates a string from Unicode to ASCII Compatible Encoding (ACE), + * as defined by the ToASCII operation of <a href="http://www.ietf.org/rfc/rfc3490.txt">RFC 3490</a>. + * + * <p>ToASCII operation can fail. ToASCII fails if any step of it fails. + * If ToASCII operation fails, an IllegalArgumentException will be thrown. + * In this case, the input string should not be used in an internationalized domain name. + * + * <p> A label is an individual part of a domain name. The original ToASCII operation, + * as defined in RFC 3490, only operates on a single label. This method can handle + * both label and entire domain name, by assuming that labels in a domain name are + * always separated by dots. The following characters are recognized as dots: + * \u002E (full stop), \u3002 (ideographic full stop), \uFF0E (fullwidth full stop), + * and \uFF61 (halfwidth ideographic full stop). if dots are + * used as label separators, this method also changes all of them to \u002E (full stop) + * in output translated string. + * + * @param input the string to be processed + * @param flag process flag; can be 0 or any logical OR of possible flags + * + * @return the translated {@code String} + * + * @throws IllegalArgumentException if the input string doesn't conform to RFC 3490 specification + */ + public static String toASCII(String input, int flag) { + // BEGIN Android-changed: Use ICU4J implementation + try { + return IDNA.convertIDNToASCII(input, flag).toString(); + } catch (android.icu.text.StringPrepParseException e) { + // b/113787610: "." is a valid IDN but is rejected by ICU. + // Usage is relatively uncommon, so only check for it if ICU throws. + if (".".equals(input)) { + return input; + } + throw new IllegalArgumentException("Invalid input to toASCII: " + input, e); + } + // END Android-changed: Use ICU4J implementation + } + + + /** + * Translates a string from Unicode to ASCII Compatible Encoding (ACE), + * as defined by the ToASCII operation of <a href="http://www.ietf.org/rfc/rfc3490.txt">RFC 3490</a>. + * + * <p> This convenience method works as if by invoking the + * two-argument counterpart as follows: + * <blockquote> + * {@link #toASCII(String, int) toASCII}(input, 0); + * </blockquote> + * + * @param input the string to be processed + * + * @return the translated {@code String} + * + * @throws IllegalArgumentException if the input string doesn't conform to RFC 3490 specification + */ + public static String toASCII(String input) { + return toASCII(input, 0); + } + + + /** + * Translates a string from ASCII Compatible Encoding (ACE) to Unicode, + * as defined by the ToUnicode operation of <a href="http://www.ietf.org/rfc/rfc3490.txt">RFC 3490</a>. + * + * <p>ToUnicode never fails. In case of any error, the input string is returned unmodified. + * + * <p> A label is an individual part of a domain name. The original ToUnicode operation, + * as defined in RFC 3490, only operates on a single label. This method can handle + * both label and entire domain name, by assuming that labels in a domain name are + * always separated by dots. The following characters are recognized as dots: + * \u002E (full stop), \u3002 (ideographic full stop), \uFF0E (fullwidth full stop), + * and \uFF61 (halfwidth ideographic full stop). + * + * @param input the string to be processed + * @param flag process flag; can be 0 or any logical OR of possible flags + * + * @return the translated {@code String} + */ + public static String toUnicode(String input, int flag) { + // BEGIN Android-changed: Use ICU4J implementation + try { + // ICU only translates separators to ASCII for toASCII. + // Java expects the translation for toUnicode too. + return convertFullStop(IDNA.convertIDNToUnicode(input, flag)).toString(); + } catch (android.icu.text.StringPrepParseException e) { + // The RI documentation explicitly states that if the conversion was unsuccessful + // the original string is returned. + return input; + } + // END Android-changed: Use ICU4J implementation + } + + // BEGIN Android-added: Use ICU4J implementation + private static boolean isLabelSeperator(char c) { + return (c == '\u3002' || c == '\uff0e' || c == '\uff61'); + } + + private static StringBuffer convertFullStop(StringBuffer input) { + for (int i = 0; i < input.length(); i++) { + if (isLabelSeperator(input.charAt(i))) { + input.setCharAt(i, '.'); + } + } + return input; + } + // END Android-added: Use ICU4J implementation + + /** + * Translates a string from ASCII Compatible Encoding (ACE) to Unicode, + * as defined by the ToUnicode operation of <a href="http://www.ietf.org/rfc/rfc3490.txt">RFC 3490</a>. + * + * <p> This convenience method works as if by invoking the + * two-argument counterpart as follows: + * <blockquote> + * {@link #toUnicode(String, int) toUnicode}(input, 0); + * </blockquote> + * + * @param input the string to be processed + * + * @return the translated {@code String} + */ + public static String toUnicode(String input) { + return toUnicode(input, 0); + } + + + /* ---------------- Private members -------------- */ + + // Android-removed: Private helper methods, unused because we use ICU. + /* + // ACE Prefix is "xn--" + private static final String ACE_PREFIX = "xn--"; + private static final int ACE_PREFIX_LENGTH = ACE_PREFIX.length(); + + private static final int MAX_LABEL_LENGTH = 63; + + // single instance of nameprep + private static StringPrep namePrep = null; + + static { + InputStream stream = null; + + try { + final String IDN_PROFILE = "uidna.spp"; + if (System.getSecurityManager() != null) { + stream = AccessController.doPrivileged(new PrivilegedAction<InputStream>() { + public InputStream run() { + return StringPrep.class.getResourceAsStream(IDN_PROFILE); + } + }); + } else { + stream = StringPrep.class.getResourceAsStream(IDN_PROFILE); + } + + namePrep = new StringPrep(stream); + stream.close(); + } catch (IOException e) { + // should never reach here + assert false; + } + } + */ + + /* ---------------- Private operations -------------- */ + + + // + // to suppress the default zero-argument constructor + // + private IDN() {} + + // Android-removed: Private helper methods, unused because we use ICU. + /* + // + // toASCII operation; should only apply to a single label + // + private static String toASCIIInternal(String label, int flag) + { + // step 1 + // Check if the string contains code points outside the ASCII range 0..0x7c. + boolean isASCII = isAllASCII(label); + StringBuffer dest; + + // step 2 + // perform the nameprep operation; flag ALLOW_UNASSIGNED is used here + if (!isASCII) { + UCharacterIterator iter = UCharacterIterator.getInstance(label); + try { + dest = namePrep.prepare(iter, flag); + } catch (java.text.ParseException e) { + throw new IllegalArgumentException(e); + } + } else { + dest = new StringBuffer(label); + } + + // step 8, move forward to check the smallest number of the code points + // the length must be inside 1..63 + if (dest.length() == 0) { + throw new IllegalArgumentException( + "Empty label is not a legal name"); + } + + // step 3 + // Verify the absence of non-LDH ASCII code points + // 0..0x2c, 0x2e..0x2f, 0x3a..0x40, 0x5b..0x60, 0x7b..0x7f + // Verify the absence of leading and trailing hyphen + boolean useSTD3ASCIIRules = ((flag & USE_STD3_ASCII_RULES) != 0); + if (useSTD3ASCIIRules) { + for (int i = 0; i < dest.length(); i++) { + int c = dest.charAt(i); + if (isNonLDHAsciiCodePoint(c)) { + throw new IllegalArgumentException( + "Contains non-LDH ASCII characters"); + } + } + + if (dest.charAt(0) == '-' || + dest.charAt(dest.length() - 1) == '-') { + + throw new IllegalArgumentException( + "Has leading or trailing hyphen"); + } + } + + if (!isASCII) { + // step 4 + // If all code points are inside 0..0x7f, skip to step 8 + if (!isAllASCII(dest.toString())) { + // step 5 + // verify the sequence does not begin with ACE prefix + if(!startsWithACEPrefix(dest)){ + + // step 6 + // encode the sequence with punycode + try { + dest = Punycode.encode(dest, null); + } catch (java.text.ParseException e) { + throw new IllegalArgumentException(e); + } + + dest = toASCIILower(dest); + + // step 7 + // prepend the ACE prefix + dest.insert(0, ACE_PREFIX); + } else { + throw new IllegalArgumentException("The input starts with the ACE Prefix"); + } + + } + } + + // step 8 + // the length must be inside 1..63 + if (dest.length() > MAX_LABEL_LENGTH) { + throw new IllegalArgumentException("The label in the input is too long"); + } + + return dest.toString(); + } + + // + // toUnicode operation; should only apply to a single label + // + private static String toUnicodeInternal(String label, int flag) { + boolean[] caseFlags = null; + StringBuffer dest; + + // step 1 + // find out if all the codepoints in input are ASCII + boolean isASCII = isAllASCII(label); + + if(!isASCII){ + // step 2 + // perform the nameprep operation; flag ALLOW_UNASSIGNED is used here + try { + UCharacterIterator iter = UCharacterIterator.getInstance(label); + dest = namePrep.prepare(iter, flag); + } catch (Exception e) { + // toUnicode never fails; if any step fails, return the input string + return label; + } + } else { + dest = new StringBuffer(label); + } + + // step 3 + // verify ACE Prefix + if(startsWithACEPrefix(dest)) { + + // step 4 + // Remove the ACE Prefix + String temp = dest.substring(ACE_PREFIX_LENGTH, dest.length()); + + try { + // step 5 + // Decode using punycode + StringBuffer decodeOut = Punycode.decode(new StringBuffer(temp), null); + + // step 6 + // Apply toASCII + String toASCIIOut = toASCII(decodeOut.toString(), flag); + + // step 7 + // verify + if (toASCIIOut.equalsIgnoreCase(dest.toString())) { + // step 8 + // return output of step 5 + return decodeOut.toString(); + } + } catch (Exception ignored) { + // no-op + } + } + + // just return the input + return label; + } + + + // + // LDH stands for "letter/digit/hyphen", with characters restricted to the + // 26-letter Latin alphabet <A-Z a-z>, the digits <0-9>, and the hyphen + // <->. + // Non LDH refers to characters in the ASCII range, but which are not + // letters, digits or the hypen. + // + // non-LDH = 0..0x2C, 0x2E..0x2F, 0x3A..0x40, 0x5B..0x60, 0x7B..0x7F + // + private static boolean isNonLDHAsciiCodePoint(int ch){ + return (0x0000 <= ch && ch <= 0x002C) || + (0x002E <= ch && ch <= 0x002F) || + (0x003A <= ch && ch <= 0x0040) || + (0x005B <= ch && ch <= 0x0060) || + (0x007B <= ch && ch <= 0x007F); + } + + // + // search dots in a string and return the index of that character; + // or if there is no dots, return the length of input string + // dots might be: \u002E (full stop), \u3002 (ideographic full stop), \uFF0E (fullwidth full stop), + // and \uFF61 (halfwidth ideographic full stop). + // + private static int searchDots(String s, int start) { + int i; + for (i = start; i < s.length(); i++) { + if (isLabelSeparator(s.charAt(i))) { + break; + } + } + + return i; + } + + // + // to check if a string is a root label, ".". + // + private static boolean isRootLabel(String s) { + return (s.length() == 1 && isLabelSeparator(s.charAt(0))); + } + + // + // to check if a character is a label separator, i.e. a dot character. + // + private static boolean isLabelSeparator(char c) { + return (c == '.' || c == '\u3002' || c == '\uFF0E' || c == '\uFF61'); + } + + // + // to check if a string only contains US-ASCII code point + // + private static boolean isAllASCII(String input) { + boolean isASCII = true; + for (int i = 0; i < input.length(); i++) { + int c = input.charAt(i); + if (c > 0x7F) { + isASCII = false; + break; + } + } + return isASCII; + } + + // + // to check if a string starts with ACE-prefix + // + private static boolean startsWithACEPrefix(StringBuffer input){ + boolean startsWithPrefix = true; + + if(input.length() < ACE_PREFIX_LENGTH){ + return false; + } + for(int i = 0; i < ACE_PREFIX_LENGTH; i++){ + if(toASCIILower(input.charAt(i)) != ACE_PREFIX.charAt(i)){ + startsWithPrefix = false; + } + } + return startsWithPrefix; + } + + private static char toASCIILower(char ch){ + if('A' <= ch && ch <= 'Z'){ + return (char)(ch + 'a' - 'A'); + } + return ch; + } + + private static StringBuffer toASCIILower(StringBuffer input){ + StringBuffer dest = new StringBuffer(); + for(int i = 0; i < input.length();i++){ + dest.append(toASCIILower(input.charAt(i))); + } + return dest; + } + */ +}
diff --git a/java/net/InMemoryCookieStore.java b/java/net/InMemoryCookieStore.java new file mode 100644 index 0000000..5df66c0 --- /dev/null +++ b/java/net/InMemoryCookieStore.java
@@ -0,0 +1,433 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import dalvik.system.VMRuntime; + +import java.util.List; +import java.util.Map; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Collections; +import java.util.Iterator; +import java.util.concurrent.locks.ReentrantLock; + +// Android-changed: App compat changes and bug fixes +// b/26456024 Add targetSdkVersion based compatibility for domain matching +// b/33034917 Support clearing cookies by adding it with "max-age=0" +// b/25897688 InMemoryCookieStore ignores scheme (http/https) port and path of the cookie +// Remove cookieJar and domainIndex. Use urlIndex as single Cookie storage +// Fix InMemoryCookieStore#remove to verify cookie URI before removal +// Fix InMemoryCookieStore#removeAll to return false if it's empty. +/** + * A simple in-memory java.net.CookieStore implementation + * + * @author Edward Wang + * @since 1.6 + * @hide Visible for testing only. + */ +public class InMemoryCookieStore implements CookieStore { + // the in-memory representation of cookies + // BEGIN Android-removed: Remove cookieJar and domainIndex + /* + private List<HttpCookie> cookieJar = null; + + // the cookies are indexed by its domain and associated uri (if present) + // CAUTION: when a cookie removed from main data structure (i.e. cookieJar), + // it won't be cleared in domainIndex & uriIndex. Double-check the + // presence of cookie when retrieve one form index store. + private Map<String, List<HttpCookie>> domainIndex = null; + */ + // END Android-removed: Remove cookieJar and domainIndex + private Map<URI, List<HttpCookie>> uriIndex = null; + + // use ReentrantLock instead of syncronized for scalability + private ReentrantLock lock = null; + + // BEGIN Android-changed: Add targetSdkVersion and remove cookieJar and domainIndex + private final boolean applyMCompatibility; + + /** + * The default ctor + */ + public InMemoryCookieStore() { + this(VMRuntime.getRuntime().getTargetSdkVersion()); + } + + public InMemoryCookieStore(int targetSdkVersion) { + uriIndex = new HashMap<>(); + lock = new ReentrantLock(false); + applyMCompatibility = (targetSdkVersion <= 23); + } + // END Android-changed: Add targetSdkVersion and remove cookieJar and domainIndex + + /** + * Add one cookie into cookie store. + */ + public void add(URI uri, HttpCookie cookie) { + // pre-condition : argument can't be null + if (cookie == null) { + throw new NullPointerException("cookie is null"); + } + + lock.lock(); + try { + // Android-changed: http://b/33034917, android supports clearing cookies + // by adding the cookie with max-age: 0. + //if (cookie.getMaxAge() != 0) { + addIndex(uriIndex, getEffectiveURI(uri), cookie); + //} + } finally { + lock.unlock(); + } + } + + + /** + * Get all cookies, which: + * 1) given uri domain-matches with, or, associated with + * given uri when added to the cookie store. + * 3) not expired. + * See RFC 2965 sec. 3.3.4 for more detail. + */ + public List<HttpCookie> get(URI uri) { + // argument can't be null + if (uri == null) { + throw new NullPointerException("uri is null"); + } + + List<HttpCookie> cookies = new ArrayList<HttpCookie>(); + // BEGIN Android-changed: b/25897688 InMemoryCookieStore ignores scheme (http/https) + lock.lock(); + try { + // check domainIndex first + getInternal1(cookies, uriIndex, uri.getHost()); + // check uriIndex then + getInternal2(cookies, uriIndex, getEffectiveURI(uri)); + } finally { + lock.unlock(); + } + // END Android-changed: b/25897688 InMemoryCookieStore ignores scheme (http/https) + return cookies; + } + + /** + * Get all cookies in cookie store, except those have expired + */ + public List<HttpCookie> getCookies() { + // BEGIN Android-changed: Remove cookieJar and domainIndex + List<HttpCookie> rt = new ArrayList<HttpCookie>(); + + lock.lock(); + try { + for (List<HttpCookie> list : uriIndex.values()) { + Iterator<HttpCookie> it = list.iterator(); + while (it.hasNext()) { + HttpCookie cookie = it.next(); + if (cookie.hasExpired()) { + it.remove(); + } else if (!rt.contains(cookie)) { + rt.add(cookie); + } + } + } + } finally { + rt = Collections.unmodifiableList(rt); + lock.unlock(); + } + // END Android-changed: Remove cookieJar and domainIndex + + return rt; + } + + /** + * Get all URIs, which are associated with at least one cookie + * of this cookie store. + */ + public List<URI> getURIs() { + // BEGIN Android-changed: App compat. Return URI with no cookies. http://b/65538736 + /* + List<URI> uris = new ArrayList<URI>(); + + lock.lock(); + try { + Iterator<URI> it = uriIndex.keySet().iterator(); + while (it.hasNext()) { + URI uri = it.next(); + List<HttpCookie> cookies = uriIndex.get(uri); + if (cookies == null || cookies.size() == 0) { + // no cookies list or an empty list associated with + // this uri entry, delete it + it.remove(); + } + } + } finally { + uris.addAll(uriIndex.keySet()); + lock.unlock(); + } + + return uris; + */ + lock.lock(); + try { + List<URI> result = new ArrayList<URI>(uriIndex.keySet()); + result.remove(null); + return Collections.unmodifiableList(result); + } finally { + lock.unlock(); + } + // END Android-changed: App compat. Return URI with no cookies. http://b/65538736 + } + + + /** + * Remove a cookie from store + */ + public boolean remove(URI uri, HttpCookie ck) { + // argument can't be null + if (ck == null) { + throw new NullPointerException("cookie is null"); + } + + // BEGIN Android-changed: Fix uri not being removed from uriIndex + lock.lock(); + try { + uri = getEffectiveURI(uri); + if (uriIndex.get(uri) == null) { + return false; + } else { + List<HttpCookie> cookies = uriIndex.get(uri); + if (cookies != null) { + return cookies.remove(ck); + } else { + return false; + } + } + } finally { + lock.unlock(); + } + // END Android-changed: Fix uri not being removed from uriIndex + } + + + /** + * Remove all cookies in this cookie store. + */ + public boolean removeAll() { + lock.lock(); + // BEGIN Android-changed: Let removeAll() return false when there are no cookies. + boolean result = false; + + try { + result = !uriIndex.isEmpty(); + uriIndex.clear(); + } finally { + lock.unlock(); + } + + return result; + // END Android-changed: Let removeAll() return false when there are no cookies. + } + + + /* ---------------- Private operations -------------- */ + + + /* + * This is almost the same as HttpCookie.domainMatches except for + * one difference: It won't reject cookies when the 'H' part of the + * domain contains a dot ('.'). + * I.E.: RFC 2965 section 3.3.2 says that if host is x.y.domain.com + * and the cookie domain is .domain.com, then it should be rejected. + * However that's not how the real world works. Browsers don't reject and + * some sites, like yahoo.com do actually expect these cookies to be + * passed along. + * And should be used for 'old' style cookies (aka Netscape type of cookies) + */ + private boolean netscapeDomainMatches(String domain, String host) + { + if (domain == null || host == null) { + return false; + } + + // if there's no embedded dot in domain and domain is not .local + boolean isLocalDomain = ".local".equalsIgnoreCase(domain); + int embeddedDotInDomain = domain.indexOf('.'); + if (embeddedDotInDomain == 0) { + embeddedDotInDomain = domain.indexOf('.', 1); + } + if (!isLocalDomain && (embeddedDotInDomain == -1 || embeddedDotInDomain == domain.length() - 1)) { + return false; + } + + // if the host name contains no dot and the domain name is .local + int firstDotInHost = host.indexOf('.'); + if (firstDotInHost == -1 && isLocalDomain) { + return true; + } + + int domainLength = domain.length(); + int lengthDiff = host.length() - domainLength; + if (lengthDiff == 0) { + // if the host name and the domain name are just string-compare euqal + return host.equalsIgnoreCase(domain); + } else if (lengthDiff > 0) { + // need to check H & D component + String D = host.substring(lengthDiff); + + // Android-changed: b/26456024 targetSdkVersion based compatibility for domain matching + // Android M and earlier: Cookies with domain "foo.com" would not match "bar.foo.com". + // The RFC dictates that the user agent must treat those domains as if they had a + // leading period and must therefore match "bar.foo.com". + if (applyMCompatibility && !domain.startsWith(".")) { + return false; + } + + return (D.equalsIgnoreCase(domain)); + } else if (lengthDiff == -1) { + // if domain is actually .host + return (domain.charAt(0) == '.' && + host.equalsIgnoreCase(domain.substring(1))); + } + + return false; + } + + private void getInternal1(List<HttpCookie> cookies, Map<URI, List<HttpCookie>> cookieIndex, + String host) { + // BEGIN Android-changed: b/25897688 InMemoryCookieStore ignores scheme (http/https) + // Use a separate list to handle cookies that need to be removed so + // that there is no conflict with iterators. + ArrayList<HttpCookie> toRemove = new ArrayList<HttpCookie>(); + for (Map.Entry<URI, List<HttpCookie>> entry : cookieIndex.entrySet()) { + List<HttpCookie> lst = entry.getValue(); + for (HttpCookie c : lst) { + String domain = c.getDomain(); + if ((c.getVersion() == 0 && netscapeDomainMatches(domain, host)) || + (c.getVersion() == 1 && HttpCookie.domainMatches(domain, host))) { + + // the cookie still in main cookie store + if (!c.hasExpired()) { + // don't add twice + if (!cookies.contains(c)) { + cookies.add(c); + } + } else { + toRemove.add(c); + } + } + } + // Clear up the cookies that need to be removed + for (HttpCookie c : toRemove) { + lst.remove(c); + + } + toRemove.clear(); + } + // END Android-changed: b/25897688 InMemoryCookieStore ignores scheme (http/https) + } + + // @param cookies [OUT] contains the found cookies + // @param cookieIndex the index + // @param comparator the prediction to decide whether or not + // a cookie in index should be returned + private <T extends Comparable<T>> + void getInternal2(List<HttpCookie> cookies, Map<T, List<HttpCookie>> cookieIndex, + T comparator) + { + // BEGIN Android-changed: b/25897688 InMemoryCookieStore ignores scheme (http/https) + // Removed cookieJar + for (T index : cookieIndex.keySet()) { + if ((index == comparator) || (index != null && comparator.compareTo(index) == 0)) { + List<HttpCookie> indexedCookies = cookieIndex.get(index); + // check the list of cookies associated with this domain + if (indexedCookies != null) { + Iterator<HttpCookie> it = indexedCookies.iterator(); + while (it.hasNext()) { + HttpCookie ck = it.next(); + // the cookie still in main cookie store + if (!ck.hasExpired()) { + // don't add twice + if (!cookies.contains(ck)) + cookies.add(ck); + } else { + it.remove(); + } + } + } // end of indexedCookies != null + } // end of comparator.compareTo(index) == 0 + } // end of cookieIndex iteration + // END Android-changed: b/25897688 InMemoryCookieStore ignores scheme (http/https) + } + + // add 'cookie' indexed by 'index' into 'indexStore' + private <T> void addIndex(Map<T, List<HttpCookie>> indexStore, + T index, + HttpCookie cookie) + { + // Android-changed: "index" can be null. We only use the URI based + // index on Android and we want to support null URIs. The underlying + // store is a HashMap which will support null keys anyway. + // if (index != null) { + List<HttpCookie> cookies = indexStore.get(index); + if (cookies != null) { + // there may already have the same cookie, so remove it first + cookies.remove(cookie); + + cookies.add(cookie); + } else { + cookies = new ArrayList<HttpCookie>(); + cookies.add(cookie); + indexStore.put(index, cookies); + } + } + + + // + // for cookie purpose, the effective uri should only be http://host + // the path will be taken into account when path-match algorithm applied + // + private URI getEffectiveURI(URI uri) { + URI effectiveURI = null; + // Android-added: Fix NullPointerException + if (uri == null) { + return null; + } + try { + effectiveURI = new URI("http", + uri.getHost(), + null, // path component + null, // query component + null // fragment component + ); + } catch (URISyntaxException ignored) { + effectiveURI = uri; + } + + return effectiveURI; + } +}
diff --git a/java/net/Inet4Address.annotated.java b/java/net/Inet4Address.annotated.java new file mode 100644 index 0000000..e10debf --- /dev/null +++ b/java/net/Inet4Address.annotated.java
@@ -0,0 +1,77 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package java.net; + +import java.io.ObjectStreamException; + +@SuppressWarnings({"unchecked", "deprecation", "all"}) +public final class Inet4Address extends java.net.InetAddress { + +Inet4Address() { throw new RuntimeException("Stub!"); } + +public boolean isMulticastAddress() { throw new RuntimeException("Stub!"); } + +public boolean isAnyLocalAddress() { throw new RuntimeException("Stub!"); } + +public boolean isLoopbackAddress() { throw new RuntimeException("Stub!"); } + +public boolean isLinkLocalAddress() { throw new RuntimeException("Stub!"); } + +public boolean isSiteLocalAddress() { throw new RuntimeException("Stub!"); } + +public boolean isMCGlobal() { throw new RuntimeException("Stub!"); } + +public boolean isMCNodeLocal() { throw new RuntimeException("Stub!"); } + +public boolean isMCLinkLocal() { throw new RuntimeException("Stub!"); } + +public boolean isMCSiteLocal() { throw new RuntimeException("Stub!"); } + +public boolean isMCOrgLocal() { throw new RuntimeException("Stub!"); } + +public byte[] getAddress() { throw new RuntimeException("Stub!"); } + +public java.lang.String getHostAddress() { throw new RuntimeException("Stub!"); } + +public int hashCode() { throw new RuntimeException("Stub!"); } + +public boolean equals(java.lang.Object obj) { throw new RuntimeException("Stub!"); } + [email protected] +public static final java.net.InetAddress ALL; +static { ALL = null; } + [email protected] +public static final java.net.InetAddress ANY; +static { ANY = null; } + [email protected] +public static final java.net.InetAddress LOOPBACK; +static { LOOPBACK = null; } +} +
diff --git a/java/net/Inet4Address.java b/java/net/Inet4Address.java new file mode 100644 index 0000000..1fb7b92 --- /dev/null +++ b/java/net/Inet4Address.java
@@ -0,0 +1,401 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.ObjectStreamException; +import static android.system.OsConstants.*; + +/** + * This class represents an Internet Protocol version 4 (IPv4) address. + * Defined by <a href="http://www.ietf.org/rfc/rfc790.txt"> + * <i>RFC 790: Assigned Numbers</i></a>, + * <a href="http://www.ietf.org/rfc/rfc1918.txt"> + * <i>RFC 1918: Address Allocation for Private Internets</i></a>, + * and <a href="http://www.ietf.org/rfc/rfc2365.txt"><i>RFC 2365: + * Administratively Scoped IP Multicast</i></a> + * + * <h3> <A NAME="format">Textual representation of IP addresses</a> </h3> + * + * Textual representation of IPv4 address used as input to methods + * takes one of the following forms: + * + * <blockquote><table cellpadding=0 cellspacing=0 summary="layout"> + * <tr><td>{@code d.d.d.d}</td></tr> + * <tr><td>{@code d.d.d}</td></tr> + * <tr><td>{@code d.d}</td></tr> + * <tr><td>{@code d}</td></tr> + * </table></blockquote> + * + * <p> When four parts are specified, each is interpreted as a byte of + * data and assigned, from left to right, to the four bytes of an IPv4 + * address. + + * <p> When a three part address is specified, the last part is + * interpreted as a 16-bit quantity and placed in the right most two + * bytes of the network address. This makes the three part address + * format convenient for specifying Class B net- work addresses as + * 128.net.host. + * + * <p> When a two part address is supplied, the last part is + * interpreted as a 24-bit quantity and placed in the right most three + * bytes of the network address. This makes the two part address + * format convenient for specifying Class A network addresses as + * net.host. + * + * <p> When only one part is given, the value is stored directly in + * the network address without any byte rearrangement. + * + * <p> For methods that return a textual representation as output + * value, the first form, i.e. a dotted-quad string, is used. + * + * <h4> The Scope of a Multicast Address </h4> + * + * Historically the IPv4 TTL field in the IP header has doubled as a + * multicast scope field: a TTL of 0 means node-local, 1 means + * link-local, up through 32 means site-local, up through 64 means + * region-local, up through 128 means continent-local, and up through + * 255 are global. However, the administrative scoping is preferred. + * Please refer to <a href="http://www.ietf.org/rfc/rfc2365.txt"> + * <i>RFC 2365: Administratively Scoped IP Multicast</i></a> + * @since 1.4 + */ + +public final +class Inet4Address extends InetAddress { + final static int INADDRSZ = 4; + + /** use serialVersionUID from InetAddress, but Inet4Address instance + * is always replaced by an InetAddress instance before being + * serialized */ + private static final long serialVersionUID = 3286316764910316507L; + + // BEGIN Android-added: Define special-purpose IPv4 address + /** @hide */ + public static final InetAddress ANY = new Inet4Address(null, new byte[] { 0, 0, 0, 0 }); + + /** @hide */ + public static final InetAddress ALL = + new Inet4Address(null, new byte[] { (byte) 255, (byte) 255, + (byte) 255, (byte) 255 }); + /** @hide */ + public static final InetAddress LOOPBACK = + new Inet4Address("localhost", new byte[] { 127, 0, 0, 1 }); + // END Android-added: Define special-purpose IPv4 address + + + // BEGIN Android-removed: Android doesn't need to call native init + /* + * Perform initializations. + * + static { + init(); + } + */ + // END Android-removed: Android doesn't need to call native init + Inet4Address() { + super(); + holder().hostName = null; + holder().address = 0; + holder().family = AF_INET; + } + + Inet4Address(String hostName, byte addr[]) { + holder().hostName = hostName; + holder().family = AF_INET; + if (addr != null) { + if (addr.length == INADDRSZ) { + int address = addr[3] & 0xFF; + address |= ((addr[2] << 8) & 0xFF00); + address |= ((addr[1] << 16) & 0xFF0000); + address |= ((addr[0] << 24) & 0xFF000000); + holder().address = address; + } + } + holder().originalHostName = hostName; + } + Inet4Address(String hostName, int address) { + holder().hostName = hostName; + holder().family = AF_INET; + holder().address = address; + holder().originalHostName = hostName; + } + + /** + * Replaces the object to be serialized with an InetAddress object. + * + * @return the alternate object to be serialized. + * + * @throws ObjectStreamException if a new object replacing this + * object could not be created + */ + private Object writeReplace() throws ObjectStreamException { + // will replace the to be serialized 'this' object + InetAddress inet = new InetAddress(); + inet.holder().hostName = holder().getHostName(); + inet.holder().address = holder().getAddress(); + + /** + * Prior to 1.4 an InetAddress was created with a family + * based on the platform AF_INET value (usually 2). + * For compatibility reasons we must therefore write the + * the InetAddress with this family. + */ + inet.holder().family = 2; + + return inet; + } + + /** + * Utility routine to check if the InetAddress is an + * IP multicast address. IP multicast address is a Class D + * address i.e first four bits of the address are 1110. + * @return a {@code boolean} indicating if the InetAddress is + * an IP multicast address + * @since JDK1.1 + */ + public boolean isMulticastAddress() { + return ((holder().getAddress() & 0xf0000000) == 0xe0000000); + } + + /** + * Utility routine to check if the InetAddress in a wildcard address. + * @return a {@code boolean} indicating if the Inetaddress is + * a wildcard address. + * @since 1.4 + */ + public boolean isAnyLocalAddress() { + return holder().getAddress() == 0; + } + + /** + * Utility routine to check if the InetAddress is a loopback address. + * + * @return a {@code boolean} indicating if the InetAddress is + * a loopback address; or false otherwise. + * @since 1.4 + */ + public boolean isLoopbackAddress() { + /* 127.x.x.x */ + byte[] byteAddr = getAddress(); + return byteAddr[0] == 127; + } + + /** + * Utility routine to check if the InetAddress is an link local address. + * + * @return a {@code boolean} indicating if the InetAddress is + * a link local address; or false if address is not a link local unicast address. + * @since 1.4 + */ + public boolean isLinkLocalAddress() { + // link-local unicast in IPv4 (169.254.0.0/16) + // defined in "Documenting Special Use IPv4 Address Blocks + // that have been Registered with IANA" by Bill Manning + // draft-manning-dsua-06.txt + int address = holder().getAddress(); + return (((address >>> 24) & 0xFF) == 169) + && (((address >>> 16) & 0xFF) == 254); + } + + /** + * Utility routine to check if the InetAddress is a site local address. + * + * @return a {@code boolean} indicating if the InetAddress is + * a site local address; or false if address is not a site local unicast address. + * @since 1.4 + */ + public boolean isSiteLocalAddress() { + // refer to RFC 1918 + // 10/8 prefix + // 172.16/12 prefix + // 192.168/16 prefix + int address = holder().getAddress(); + return (((address >>> 24) & 0xFF) == 10) + || ((((address >>> 24) & 0xFF) == 172) + && (((address >>> 16) & 0xF0) == 16)) + || ((((address >>> 24) & 0xFF) == 192) + && (((address >>> 16) & 0xFF) == 168)); + } + + /** + * Utility routine to check if the multicast address has global scope. + * + * @return a {@code boolean} indicating if the address has + * is a multicast address of global scope, false if it is not + * of global scope or it is not a multicast address + * @since 1.4 + */ + public boolean isMCGlobal() { + // 224.0.1.0 to 238.255.255.255 + byte[] byteAddr = getAddress(); + return ((byteAddr[0] & 0xff) >= 224 && (byteAddr[0] & 0xff) <= 238 ) && + !((byteAddr[0] & 0xff) == 224 && byteAddr[1] == 0 && + byteAddr[2] == 0); + } + + /** + * Utility routine to check if the multicast address has node scope. + * + * @return a {@code boolean} indicating if the address has + * is a multicast address of node-local scope, false if it is not + * of node-local scope or it is not a multicast address + * @since 1.4 + */ + public boolean isMCNodeLocal() { + // unless ttl == 0 + return false; + } + + /** + * Utility routine to check if the multicast address has link scope. + * + * @return a {@code boolean} indicating if the address has + * is a multicast address of link-local scope, false if it is not + * of link-local scope or it is not a multicast address + * @since 1.4 + */ + public boolean isMCLinkLocal() { + // 224.0.0/24 prefix and ttl == 1 + int address = holder().getAddress(); + return (((address >>> 24) & 0xFF) == 224) + && (((address >>> 16) & 0xFF) == 0) + && (((address >>> 8) & 0xFF) == 0); + } + + /** + * Utility routine to check if the multicast address has site scope. + * + * @return a {@code boolean} indicating if the address has + * is a multicast address of site-local scope, false if it is not + * of site-local scope or it is not a multicast address + * @since 1.4 + */ + public boolean isMCSiteLocal() { + // 239.255/16 prefix or ttl < 32 + int address = holder().getAddress(); + return (((address >>> 24) & 0xFF) == 239) + && (((address >>> 16) & 0xFF) == 255); + } + + /** + * Utility routine to check if the multicast address has organization scope. + * + * @return a {@code boolean} indicating if the address has + * is a multicast address of organization-local scope, + * false if it is not of organization-local scope + * or it is not a multicast address + * @since 1.4 + */ + public boolean isMCOrgLocal() { + // 239.192 - 239.195 + int address = holder().getAddress(); + return (((address >>> 24) & 0xFF) == 239) + && (((address >>> 16) & 0xFF) >= 192) + && (((address >>> 16) & 0xFF) <= 195); + } + + /** + * Returns the raw IP address of this {@code InetAddress} + * object. The result is in network byte order: the highest order + * byte of the address is in {@code getAddress()[0]}. + * + * @return the raw IP address of this object. + */ + public byte[] getAddress() { + int address = holder().getAddress(); + byte[] addr = new byte[INADDRSZ]; + + addr[0] = (byte) ((address >>> 24) & 0xFF); + addr[1] = (byte) ((address >>> 16) & 0xFF); + addr[2] = (byte) ((address >>> 8) & 0xFF); + addr[3] = (byte) (address & 0xFF); + return addr; + } + + /** + * Returns the IP address string in textual presentation form. + * + * @return the raw IP address in a string format. + * @since JDK1.0.2 + */ + public String getHostAddress() { + return numericToTextFormat(getAddress()); + } + + /** + * Returns a hashcode for this IP address. + * + * @return a hash code value for this IP address. + */ + public int hashCode() { + return holder().getAddress(); + } + + /** + * Compares this object against the specified object. + * The result is {@code true} if and only if the argument is + * not {@code null} and it represents the same IP address as + * this object. + * <p> + * Two instances of {@code InetAddress} represent the same IP + * address if the length of the byte arrays returned by + * {@code getAddress} is the same for both, and each of the + * array components is the same for the byte arrays. + * + * @param obj the object to compare against. + * @return {@code true} if the objects are the same; + * {@code false} otherwise. + * @see java.net.InetAddress#getAddress() + */ + public boolean equals(Object obj) { + return (obj != null) && (obj instanceof Inet4Address) && + (((InetAddress)obj).holder().getAddress() == holder().getAddress()); + } + + // Utilities + /* + * Converts IPv4 binary address into a string suitable for presentation. + * + * @param src a byte array representing an IPv4 numeric address + * @return a String representing the IPv4 address in + * textual representation format + * @since 1.4 + */ + + static String numericToTextFormat(byte[] src) + { + return (src[0] & 0xff) + "." + (src[1] & 0xff) + "." + (src[2] & 0xff) + "." + (src[3] & 0xff); + } + + // BEGIN Android-removed: Android doesn't need to call native init + /* + * Perform class load-time initializations. + * + private static native void init(); + */ + // END Android-removed: Android doesn't need to call native init +}
diff --git a/java/net/Inet6Address.annotated.java b/java/net/Inet6Address.annotated.java new file mode 100644 index 0000000..7888710 --- /dev/null +++ b/java/net/Inet6Address.annotated.java
@@ -0,0 +1,83 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package java.net; + + [email protected] +@SuppressWarnings({"unchecked", "deprecation", "all"}) +public final class Inet6Address extends java.net.InetAddress { + +Inet6Address() { throw new RuntimeException("Stub!"); } + +public static java.net.Inet6Address getByAddress(java.lang.String host, byte[] addr, java.net.NetworkInterface nif) throws java.net.UnknownHostException { throw new RuntimeException("Stub!"); } + +public static java.net.Inet6Address getByAddress(java.lang.String host, byte[] addr, int scope_id) throws java.net.UnknownHostException { throw new RuntimeException("Stub!"); } + +public boolean isMulticastAddress() { throw new RuntimeException("Stub!"); } + +public boolean isAnyLocalAddress() { throw new RuntimeException("Stub!"); } + +public boolean isLoopbackAddress() { throw new RuntimeException("Stub!"); } + +public boolean isLinkLocalAddress() { throw new RuntimeException("Stub!"); } + +public boolean isSiteLocalAddress() { throw new RuntimeException("Stub!"); } + +public boolean isMCGlobal() { throw new RuntimeException("Stub!"); } + +public boolean isMCNodeLocal() { throw new RuntimeException("Stub!"); } + +public boolean isMCLinkLocal() { throw new RuntimeException("Stub!"); } + +public boolean isMCSiteLocal() { throw new RuntimeException("Stub!"); } + +public boolean isMCOrgLocal() { throw new RuntimeException("Stub!"); } + +public byte[] getAddress() { throw new RuntimeException("Stub!"); } + [email protected] +public int getScopeId() { throw new RuntimeException("Stub!"); } + +public java.net.NetworkInterface getScopedInterface() { throw new RuntimeException("Stub!"); } + +public java.lang.String getHostAddress() { throw new RuntimeException("Stub!"); } + +public int hashCode() { throw new RuntimeException("Stub!"); } + +public boolean equals(java.lang.Object obj) { throw new RuntimeException("Stub!"); } + +public boolean isIPv4CompatibleAddress() { throw new RuntimeException("Stub!"); } + [email protected] +public static final java.net.InetAddress ANY; +static { ANY = null; } + [email protected] +public static final java.net.InetAddress LOOPBACK; +static { LOOPBACK = null; } +}
diff --git a/java/net/Inet6Address.java b/java/net/Inet6Address.java new file mode 100644 index 0000000..c0aadb3 --- /dev/null +++ b/java/net/Inet6Address.java
@@ -0,0 +1,981 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamField; +import java.util.Enumeration; +import java.util.Arrays; +import libcore.io.Libcore; +import static android.system.OsConstants.*; + +/** + * This class represents an Internet Protocol version 6 (IPv6) address. + * Defined by <a href="http://www.ietf.org/rfc/rfc2373.txt"> + * <i>RFC 2373: IP Version 6 Addressing Architecture</i></a>. + * + * <h3> <A NAME="format">Textual representation of IP addresses</a> </h3> + * + * Textual representation of IPv6 address used as input to methods + * takes one of the following forms: + * + * <ol> + * <li><p> <A NAME="lform">The preferred form</a> is x:x:x:x:x:x:x:x, + * where the 'x's are + * the hexadecimal values of the eight 16-bit pieces of the + * address. This is the full form. For example, + * + * <blockquote><table cellpadding=0 cellspacing=0 summary="layout"> + * <tr><td>{@code 1080:0:0:0:8:800:200C:417A}<td></tr> + * </table></blockquote> + * + * <p> Note that it is not necessary to write the leading zeros in + * an individual field. However, there must be at least one numeral + * in every field, except as described below.</li> + * + * <li><p> Due to some methods of allocating certain styles of IPv6 + * addresses, it will be common for addresses to contain long + * strings of zero bits. In order to make writing addresses + * containing zero bits easier, a special syntax is available to + * compress the zeros. The use of "::" indicates multiple groups + * of 16-bits of zeros. The "::" can only appear once in an address. + * The "::" can also be used to compress the leading and/or trailing + * zeros in an address. For example, + * + * <blockquote><table cellpadding=0 cellspacing=0 summary="layout"> + * <tr><td>{@code 1080::8:800:200C:417A}<td></tr> + * </table></blockquote> + * + * <li><p> An alternative form that is sometimes more convenient + * when dealing with a mixed environment of IPv4 and IPv6 nodes is + * x:x:x:x:x:x:d.d.d.d, where the 'x's are the hexadecimal values + * of the six high-order 16-bit pieces of the address, and the 'd's + * are the decimal values of the four low-order 8-bit pieces of the + * standard IPv4 representation address, for example, + * + * <blockquote><table cellpadding=0 cellspacing=0 summary="layout"> + * <tr><td>{@code ::FFFF:129.144.52.38}<td></tr> + * <tr><td>{@code ::129.144.52.38}<td></tr> + * </table></blockquote> + * + * <p> where "::FFFF:d.d.d.d" and "::d.d.d.d" are, respectively, the + * general forms of an IPv4-mapped IPv6 address and an + * IPv4-compatible IPv6 address. Note that the IPv4 portion must be + * in the "d.d.d.d" form. The following forms are invalid: + * + * <blockquote><table cellpadding=0 cellspacing=0 summary="layout"> + * <tr><td>{@code ::FFFF:d.d.d}<td></tr> + * <tr><td>{@code ::FFFF:d.d}<td></tr> + * <tr><td>{@code ::d.d.d}<td></tr> + * <tr><td>{@code ::d.d}<td></tr> + * </table></blockquote> + * + * <p> The following form: + * + * <blockquote><table cellpadding=0 cellspacing=0 summary="layout"> + * <tr><td>{@code ::FFFF:d}<td></tr> + * </table></blockquote> + * + * <p> is valid, however it is an unconventional representation of + * the IPv4-compatible IPv6 address, + * + * <blockquote><table cellpadding=0 cellspacing=0 summary="layout"> + * <tr><td>{@code ::255.255.0.d}<td></tr> + * </table></blockquote> + * + * <p> while "::d" corresponds to the general IPv6 address + * "0:0:0:0:0:0:0:d".</li> + * </ol> + * + * <p> For methods that return a textual representation as output + * value, the full form is used. Inet6Address will return the full + * form because it is unambiguous when used in combination with other + * textual data. + * + * <h4> Special IPv6 address </h4> + * + * <blockquote> + * <table cellspacing=2 summary="Description of IPv4-mapped address"> + * <tr><th valign=top><i>IPv4-mapped address</i></th> + * <td>Of the form::ffff:w.x.y.z, this IPv6 address is used to + * represent an IPv4 address. It allows the native program to + * use the same address data structure and also the same + * socket when communicating with both IPv4 and IPv6 nodes. + * + * <p>In InetAddress and Inet6Address, it is used for internal + * representation; it has no functional role. Java will never + * return an IPv4-mapped address. These classes can take an + * IPv4-mapped address as input, both in byte array and text + * representation. However, it will be converted into an IPv4 + * address.</td></tr> + * </table></blockquote> + * + * <h4><A NAME="scoped">Textual representation of IPv6 scoped addresses</a></h4> + * + * <p> The textual representation of IPv6 addresses as described above can be + * extended to specify IPv6 scoped addresses. This extension to the basic + * addressing architecture is described in [draft-ietf-ipngwg-scoping-arch-04.txt]. + * + * <p> Because link-local and site-local addresses are non-global, it is possible + * that different hosts may have the same destination address and may be + * reachable through different interfaces on the same originating system. In + * this case, the originating system is said to be connected to multiple zones + * of the same scope. In order to disambiguate which is the intended destination + * zone, it is possible to append a zone identifier (or <i>scope_id</i>) to an + * IPv6 address. + * + * <p> The general format for specifying the <i>scope_id</i> is the following: + * + * <blockquote><i>IPv6-address</i>%<i>scope_id</i></blockquote> + * <p> The IPv6-address is a literal IPv6 address as described above. + * The <i>scope_id</i> refers to an interface on the local system, and it can be + * specified in two ways. + * <ol><li><i>As a numeric identifier.</i> This must be a positive integer + * that identifies the particular interface and scope as understood by the + * system. Usually, the numeric values can be determined through administration + * tools on the system. Each interface may have multiple values, one for each + * scope. If the scope is unspecified, then the default value used is zero.</li> + * <li><i>As a string.</i> This must be the exact string that is returned by + * {@link java.net.NetworkInterface#getName()} for the particular interface in + * question. When an Inet6Address is created in this way, the numeric scope-id + * is determined at the time the object is created by querying the relevant + * NetworkInterface.</li></ol> + * + * <p> Note also, that the numeric <i>scope_id</i> can be retrieved from + * Inet6Address instances returned from the NetworkInterface class. This can be + * used to find out the current scope ids configured on the system. + * @since 1.4 + */ + +public final +class Inet6Address extends InetAddress { + final static int INADDRSZ = 16; + + // BEGIN Android-removed: Remove special handling for link-local addresses + /* + * cached scope_id - for link-local address use only. + * + private transient int cached_scope_id; // 0 + */ + // END Android-removed: Remove special handling for link-local addresses + + // BEGIN Android-added: Define special-purpose IPv6 address + /** @hide */ + public static final InetAddress ANY = + new Inet6Address("::", new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0); + + /** @hide */ + public static final InetAddress LOOPBACK = new Inet6Address("ip6-localhost", + new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, 0); + // END Android-added: Define special-purpose IPv6 address + + private class Inet6AddressHolder { + + private Inet6AddressHolder() { + ipaddress = new byte[INADDRSZ]; + } + + private Inet6AddressHolder( + byte[] ipaddress, int scope_id, boolean scope_id_set, + NetworkInterface ifname, boolean scope_ifname_set) + { + this.ipaddress = ipaddress; + this.scope_id = scope_id; + this.scope_id_set = scope_id_set; + this.scope_ifname_set = scope_ifname_set; + this.scope_ifname = ifname; + } + + /** + * Holds a 128-bit (16 bytes) IPv6 address. + */ + byte[] ipaddress; + + /** + * scope_id. The scope specified when the object is created. If the object + * is created with an interface name, then the scope_id is not determined + * until the time it is needed. + */ + int scope_id; // 0 + + /** + * This will be set to true when the scope_id field contains a valid + * integer scope_id. + */ + boolean scope_id_set; // false + + /** + * scoped interface. scope_id is derived from this as the scope_id of the first + * address whose scope is the same as this address for the named interface. + */ + NetworkInterface scope_ifname; // null + + /** + * set if the object is constructed with a scoped + * interface instead of a numeric scope id. + */ + boolean scope_ifname_set; // false; + + void setAddr(byte addr[]) { + if (addr.length == INADDRSZ) { // normal IPv6 address + System.arraycopy(addr, 0, ipaddress, 0, INADDRSZ); + } + } + + void init(byte addr[], int scope_id) { + setAddr(addr); + + // Android-changed: was >= 0 + if (scope_id > 0) { + this.scope_id = scope_id; + this.scope_id_set = true; + } + } + + void init(byte addr[], NetworkInterface nif) + throws UnknownHostException + { + setAddr(addr); + + if (nif != null) { + this.scope_id = deriveNumericScope(ipaddress, nif); + this.scope_id_set = true; + this.scope_ifname = nif; + this.scope_ifname_set = true; + } + } + + // Android-removed: getnameinfo returns smarter representations than getHostAddress() + /* + String getHostAddress() { + String s = numericToTextFormat(ipaddress); + if (scope_ifname != null) { // must check this first + s = s + "%" + scope_ifname.getName(); + } else if (scope_id_set) { + s = s + "%" + scope_id; + } + return s; + } + */ + + public boolean equals(Object o) { + if (! (o instanceof Inet6AddressHolder)) { + return false; + } + Inet6AddressHolder that = (Inet6AddressHolder)o; + + return Arrays.equals(this.ipaddress, that.ipaddress); + } + + public int hashCode() { + if (ipaddress != null) { + + int hash = 0; + int i=0; + while (i<INADDRSZ) { + int j=0; + int component=0; + while (j<4 && i<INADDRSZ) { + component = (component << 8) + ipaddress[i]; + j++; + i++; + } + hash += component; + } + return hash; + + } else { + return 0; + } + } + + boolean isIPv4CompatibleAddress() { + if ((ipaddress[0] == 0x00) && (ipaddress[1] == 0x00) && + (ipaddress[2] == 0x00) && (ipaddress[3] == 0x00) && + (ipaddress[4] == 0x00) && (ipaddress[5] == 0x00) && + (ipaddress[6] == 0x00) && (ipaddress[7] == 0x00) && + (ipaddress[8] == 0x00) && (ipaddress[9] == 0x00) && + (ipaddress[10] == 0x00) && (ipaddress[11] == 0x00)) { + return true; + } + return false; + } + + boolean isMulticastAddress() { + return ((ipaddress[0] & 0xff) == 0xff); + } + + boolean isAnyLocalAddress() { + byte test = 0x00; + for (int i = 0; i < INADDRSZ; i++) { + test |= ipaddress[i]; + } + return (test == 0x00); + } + + boolean isLoopbackAddress() { + byte test = 0x00; + for (int i = 0; i < 15; i++) { + test |= ipaddress[i]; + } + return (test == 0x00) && (ipaddress[15] == 0x01); + } + + boolean isLinkLocalAddress() { + return ((ipaddress[0] & 0xff) == 0xfe + && (ipaddress[1] & 0xc0) == 0x80); + } + + + boolean isSiteLocalAddress() { + return ((ipaddress[0] & 0xff) == 0xfe + && (ipaddress[1] & 0xc0) == 0xc0); + } + + boolean isMCGlobal() { + return ((ipaddress[0] & 0xff) == 0xff + && (ipaddress[1] & 0x0f) == 0x0e); + } + + boolean isMCNodeLocal() { + return ((ipaddress[0] & 0xff) == 0xff + && (ipaddress[1] & 0x0f) == 0x01); + } + + boolean isMCLinkLocal() { + return ((ipaddress[0] & 0xff) == 0xff + && (ipaddress[1] & 0x0f) == 0x02); + } + + boolean isMCSiteLocal() { + return ((ipaddress[0] & 0xff) == 0xff + && (ipaddress[1] & 0x0f) == 0x05); + } + + boolean isMCOrgLocal() { + return ((ipaddress[0] & 0xff) == 0xff + && (ipaddress[1] & 0x0f) == 0x08); + } + } + + private final transient Inet6AddressHolder holder6; + + private static final long serialVersionUID = 6880410070516793377L; + + // BEGIN Android-removed: Android doesn't need to call native init + /* + // Perform native initialization + static { init(); } + // END Android-removed: Android doesn't need to call native init + */ + + Inet6Address() { + super(); + holder.init(null, AF_INET6); + holder6 = new Inet6AddressHolder(); + } + + /* checking of value for scope_id should be done by caller + * scope_id must be >= 0, or -1 to indicate not being set + */ + Inet6Address(String hostName, byte addr[], int scope_id) { + holder.init(hostName, AF_INET6); + holder6 = new Inet6AddressHolder(); + holder6.init(addr, scope_id); + } + + Inet6Address(String hostName, byte addr[]) { + holder6 = new Inet6AddressHolder(); + try { + initif (hostName, addr, null); + } catch (UnknownHostException e) {} /* cant happen if ifname is null */ + } + + Inet6Address (String hostName, byte addr[], NetworkInterface nif) + throws UnknownHostException + { + holder6 = new Inet6AddressHolder(); + initif (hostName, addr, nif); + } + + Inet6Address (String hostName, byte addr[], String ifname) + throws UnknownHostException + { + holder6 = new Inet6AddressHolder(); + initstr (hostName, addr, ifname); + } + + /** + * Create an Inet6Address in the exact manner of {@link + * InetAddress#getByAddress(String,byte[])} except that the IPv6 scope_id is + * set to the value corresponding to the given interface for the address + * type specified in {@code addr}. The call will fail with an + * UnknownHostException if the given interface does not have a numeric + * scope_id assigned for the given address type (eg. link-local or site-local). + * See <a href="Inet6Address.html#scoped">here</a> for a description of IPv6 + * scoped addresses. + * + * @param host the specified host + * @param addr the raw IP address in network byte order + * @param nif an interface this address must be associated with. + * @return an Inet6Address object created from the raw IP address. + * @throws UnknownHostException + * if IP address is of illegal length, or if the interface does not + * have a numeric scope_id assigned for the given address type. + * + * @since 1.5 + */ + public static Inet6Address getByAddress(String host, byte[] addr, + NetworkInterface nif) + throws UnknownHostException + { + if (host != null && host.length() > 0 && host.charAt(0) == '[') { + if (host.charAt(host.length()-1) == ']') { + host = host.substring(1, host.length() -1); + } + } + if (addr != null) { + if (addr.length == Inet6Address.INADDRSZ) { + return new Inet6Address(host, addr, nif); + } + } + throw new UnknownHostException("addr is of illegal length"); + } + + /** + * Create an Inet6Address in the exact manner of {@link + * InetAddress#getByAddress(String,byte[])} except that the IPv6 scope_id is + * set to the given numeric value. The scope_id is not checked to determine + * if it corresponds to any interface on the system. + * See <a href="Inet6Address.html#scoped">here</a> for a description of IPv6 + * scoped addresses. + * + * @param host the specified host + * @param addr the raw IP address in network byte order + * @param scope_id the numeric scope_id for the address. + * @return an Inet6Address object created from the raw IP address. + * @throws UnknownHostException if IP address is of illegal length. + * + * @since 1.5 + */ + public static Inet6Address getByAddress(String host, byte[] addr, + int scope_id) + throws UnknownHostException + { + if (host != null && host.length() > 0 && host.charAt(0) == '[') { + if (host.charAt(host.length()-1) == ']') { + host = host.substring(1, host.length() -1); + } + } + if (addr != null) { + if (addr.length == Inet6Address.INADDRSZ) { + return new Inet6Address(host, addr, scope_id); + } + } + throw new UnknownHostException("addr is of illegal length"); + } + + private void initstr(String hostName, byte addr[], String ifname) + throws UnknownHostException + { + try { + NetworkInterface nif = NetworkInterface.getByName (ifname); + if (nif == null) { + throw new UnknownHostException ("no such interface " + ifname); + } + initif (hostName, addr, nif); + } catch (SocketException e) { + throw new UnknownHostException ("SocketException thrown" + ifname); + } + } + + private void initif(String hostName, byte addr[], NetworkInterface nif) + throws UnknownHostException + { + int family = -1; + holder6.init(addr, nif); + + if (addr.length == INADDRSZ) { // normal IPv6 address + family = AF_INET6; + } + holder.init(hostName, family); + } + + /* check the two Ipv6 addresses and return false if they are both + * non global address types, but not the same. + * (ie. one is sitelocal and the other linklocal) + * return true otherwise. + */ + + private static boolean isDifferentLocalAddressType( + byte[] thisAddr, byte[] otherAddr) { + + if (Inet6Address.isLinkLocalAddress(thisAddr) && + !Inet6Address.isLinkLocalAddress(otherAddr)) { + return false; + } + if (Inet6Address.isSiteLocalAddress(thisAddr) && + !Inet6Address.isSiteLocalAddress(otherAddr)) { + return false; + } + return true; + } + + private static int deriveNumericScope (byte[] thisAddr, NetworkInterface ifc) throws UnknownHostException { + Enumeration<InetAddress> addresses = ifc.getInetAddresses(); + while (addresses.hasMoreElements()) { + InetAddress addr = addresses.nextElement(); + if (!(addr instanceof Inet6Address)) { + continue; + } + Inet6Address ia6_addr = (Inet6Address)addr; + /* check if site or link local prefixes match */ + if (!isDifferentLocalAddressType(thisAddr, ia6_addr.getAddress())){ + /* type not the same, so carry on searching */ + continue; + } + /* found a matching address - return its scope_id */ + return ia6_addr.getScopeId(); + } + throw new UnknownHostException ("no scope_id found"); + } + + private int deriveNumericScope (String ifname) throws UnknownHostException { + Enumeration<NetworkInterface> en; + try { + en = NetworkInterface.getNetworkInterfaces(); + } catch (SocketException e) { + throw new UnknownHostException ("could not enumerate local network interfaces"); + } + while (en.hasMoreElements()) { + NetworkInterface ifc = en.nextElement(); + if (ifc.getName().equals (ifname)) { + return deriveNumericScope(holder6.ipaddress, ifc); + } + } + throw new UnknownHostException ("No matching address found for interface : " +ifname); + } + + /** + * @serialField ipaddress byte[] + * @serialField scope_id int + * @serialField scope_id_set boolean + * @serialField scope_ifname_set boolean + * @serialField ifname String + */ + + private static final ObjectStreamField[] serialPersistentFields = { + new ObjectStreamField("ipaddress", byte[].class), + new ObjectStreamField("scope_id", int.class), + new ObjectStreamField("scope_id_set", boolean.class), + new ObjectStreamField("scope_ifname_set", boolean.class), + new ObjectStreamField("ifname", String.class) + }; + + private static final long FIELDS_OFFSET; + private static final sun.misc.Unsafe UNSAFE; + + static { + try { + sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe(); + FIELDS_OFFSET = unsafe.objectFieldOffset( + Inet6Address.class.getDeclaredField("holder6")); + UNSAFE = unsafe; + } catch (ReflectiveOperationException e) { + throw new Error(e); + } + } + + /** + * restore the state of this object from stream + * including the scope information, only if the + * scoped interface name is valid on this system + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException { + NetworkInterface scope_ifname = null; + + // Android-changed: was getClass().getClassLoader() != null + if (getClass().getClassLoader() != Class.class.getClassLoader()) { + throw new SecurityException ("invalid address type"); + } + + ObjectInputStream.GetField gf = s.readFields(); + byte[] ipaddress = (byte[])gf.get("ipaddress", null); + int scope_id = (int)gf.get("scope_id", -1); + boolean scope_id_set = (boolean)gf.get("scope_id_set", false); + boolean scope_ifname_set = (boolean)gf.get("scope_ifname_set", false); + String ifname = (String)gf.get("ifname", null); + + if (ifname != null && !"".equals (ifname)) { + try { + scope_ifname = NetworkInterface.getByName(ifname); + if (scope_ifname == null) { + /* the interface does not exist on this system, so we clear + * the scope information completely */ + scope_id_set = false; + scope_ifname_set = false; + scope_id = 0; + } else { + scope_ifname_set = true; + try { + scope_id = deriveNumericScope (ipaddress, scope_ifname); + } catch (UnknownHostException e) { + // typically should not happen, but it may be that + // the machine being used for deserialization has + // the same interface name but without IPv6 configured. + } + } + } catch (SocketException e) {} + } + + /* if ifname was not supplied, then the numeric info is used */ + + ipaddress = ipaddress.clone(); + + // Check that our invariants are satisfied + if (ipaddress.length != INADDRSZ) { + throw new InvalidObjectException("invalid address length: "+ + ipaddress.length); + } + + if (holder().getFamily() != AF_INET6) { + throw new InvalidObjectException("invalid address family type"); + } + + Inet6AddressHolder h = new Inet6AddressHolder( + ipaddress, scope_id, scope_id_set, scope_ifname, scope_ifname_set + ); + + UNSAFE.putObject(this, FIELDS_OFFSET, h); + } + + /** + * default behavior is overridden in order to write the + * scope_ifname field as a String, rather than a NetworkInterface + * which is not serializable + */ + private synchronized void writeObject(ObjectOutputStream s) + throws IOException + { + String ifname = null; + + if (holder6.scope_ifname != null) { + ifname = holder6.scope_ifname.getName(); + holder6.scope_ifname_set = true; + } + ObjectOutputStream.PutField pfields = s.putFields(); + pfields.put("ipaddress", holder6.ipaddress); + pfields.put("scope_id", holder6.scope_id); + pfields.put("scope_id_set", holder6.scope_id_set); + pfields.put("scope_ifname_set", holder6.scope_ifname_set); + pfields.put("ifname", ifname); + s.writeFields(); + } + + /** + * Utility routine to check if the InetAddress is an IP multicast + * address. 11111111 at the start of the address identifies the + * address as being a multicast address. + * + * @return a {@code boolean} indicating if the InetAddress is an IP + * multicast address + * + * @since JDK1.1 + */ + @Override + public boolean isMulticastAddress() { + return holder6.isMulticastAddress(); + } + + /** + * Utility routine to check if the InetAddress in a wildcard address. + * + * @return a {@code boolean} indicating if the Inetaddress is + * a wildcard address. + * + * @since 1.4 + */ + @Override + public boolean isAnyLocalAddress() { + return holder6.isAnyLocalAddress(); + } + + /** + * Utility routine to check if the InetAddress is a loopback address. + * + * @return a {@code boolean} indicating if the InetAddress is a loopback + * address; or false otherwise. + * + * @since 1.4 + */ + @Override + public boolean isLoopbackAddress() { + return holder6.isLoopbackAddress(); + } + + /** + * Utility routine to check if the InetAddress is an link local address. + * + * @return a {@code boolean} indicating if the InetAddress is a link local + * address; or false if address is not a link local unicast address. + * + * @since 1.4 + */ + @Override + public boolean isLinkLocalAddress() { + return holder6.isLinkLocalAddress(); + } + + /* static version of above */ + static boolean isLinkLocalAddress(byte[] ipaddress) { + return ((ipaddress[0] & 0xff) == 0xfe + && (ipaddress[1] & 0xc0) == 0x80); + } + + /** + * Utility routine to check if the InetAddress is a site local address. + * + * @return a {@code boolean} indicating if the InetAddress is a site local + * address; or false if address is not a site local unicast address. + * + * @since 1.4 + */ + @Override + public boolean isSiteLocalAddress() { + return holder6.isSiteLocalAddress(); + } + + /* static version of above */ + static boolean isSiteLocalAddress(byte[] ipaddress) { + return ((ipaddress[0] & 0xff) == 0xfe + && (ipaddress[1] & 0xc0) == 0xc0); + } + + /** + * Utility routine to check if the multicast address has global scope. + * + * @return a {@code boolean} indicating if the address has is a multicast + * address of global scope, false if it is not of global scope or + * it is not a multicast address + * + * @since 1.4 + */ + @Override + public boolean isMCGlobal() { + return holder6.isMCGlobal(); + } + + /** + * Utility routine to check if the multicast address has node scope. + * + * @return a {@code boolean} indicating if the address has is a multicast + * address of node-local scope, false if it is not of node-local + * scope or it is not a multicast address + * + * @since 1.4 + */ + @Override + public boolean isMCNodeLocal() { + return holder6.isMCNodeLocal(); + } + + /** + * Utility routine to check if the multicast address has link scope. + * + * @return a {@code boolean} indicating if the address has is a multicast + * address of link-local scope, false if it is not of link-local + * scope or it is not a multicast address + * + * @since 1.4 + */ + @Override + public boolean isMCLinkLocal() { + return holder6.isMCLinkLocal(); + } + + /** + * Utility routine to check if the multicast address has site scope. + * + * @return a {@code boolean} indicating if the address has is a multicast + * address of site-local scope, false if it is not of site-local + * scope or it is not a multicast address + * + * @since 1.4 + */ + @Override + public boolean isMCSiteLocal() { + return holder6.isMCSiteLocal(); + } + + /** + * Utility routine to check if the multicast address has organization scope. + * + * @return a {@code boolean} indicating if the address has is a multicast + * address of organization-local scope, false if it is not of + * organization-local scope or it is not a multicast address + * + * @since 1.4 + */ + @Override + public boolean isMCOrgLocal() { + return holder6.isMCOrgLocal(); + } + /** + * Returns the raw IP address of this {@code InetAddress} object. The result + * is in network byte order: the highest order byte of the address is in + * {@code getAddress()[0]}. + * + * @return the raw IP address of this object. + */ + @Override + public byte[] getAddress() { + return holder6.ipaddress.clone(); + } + + /** + * Returns the numeric scopeId, if this instance is associated with + * an interface. If no scoped_id is set, the returned value is zero. + * + * @return the scopeId, or zero if not set. + * + * @since 1.5 + */ + public int getScopeId() { + return holder6.scope_id; + } + + /** + * Returns the scoped interface, if this instance was created with + * with a scoped interface. + * + * @return the scoped interface, or null if not set. + * @since 1.5 + */ + public NetworkInterface getScopedInterface() { + return holder6.scope_ifname; + } + + /** + * Returns the IP address string in textual presentation. If the instance + * was created specifying a scope identifier then the scope id is appended + * to the IP address preceded by a "%" (per-cent) character. This can be + * either a numeric value or a string, depending on which was used to create + * the instance. + * + * @return the raw IP address in a string format. + */ + @Override + public String getHostAddress() { + // Android-changed: getnameinfo returns smarter representations than getHostAddress() + // return holder6.getHostAddress(); + return Libcore.os.getnameinfo(this, NI_NUMERICHOST); // Can't throw. + } + + /** + * Returns a hashcode for this IP address. + * + * @return a hash code value for this IP address. + */ + @Override + public int hashCode() { + return holder6.hashCode(); + } + + /** + * Compares this object against the specified object. The result is {@code + * true} if and only if the argument is not {@code null} and it represents + * the same IP address as this object. + * + * <p> Two instances of {@code InetAddress} represent the same IP address + * if the length of the byte arrays returned by {@code getAddress} is the + * same for both, and each of the array components is the same for the byte + * arrays. + * + * @param obj the object to compare against. + * + * @return {@code true} if the objects are the same; {@code false} otherwise. + * + * @see java.net.InetAddress#getAddress() + */ + @Override + public boolean equals(Object obj) { + if (obj == null || !(obj instanceof Inet6Address)) + return false; + + Inet6Address inetAddr = (Inet6Address)obj; + + return holder6.equals(inetAddr.holder6); + } + + /** + * Utility routine to check if the InetAddress is an + * IPv4 compatible IPv6 address. + * + * @return a {@code boolean} indicating if the InetAddress is an IPv4 + * compatible IPv6 address; or false if address is IPv4 address. + * + * @since 1.4 + */ + public boolean isIPv4CompatibleAddress() { + return holder6.isIPv4CompatibleAddress(); + } + + // Utilities + private final static int INT16SZ = 2; + + /* + * Convert IPv6 binary address into presentation (printable) format. + * + * @param src a byte array representing the IPv6 numeric address + * @return a String representing an IPv6 address in + * textual representation format + * @since 1.4 + */ + static String numericToTextFormat(byte[] src) { + StringBuilder sb = new StringBuilder(39); + for (int i = 0; i < (INADDRSZ / INT16SZ); i++) { + sb.append(Integer.toHexString(((src[i<<1]<<8) & 0xff00) + | (src[(i<<1)+1] & 0xff))); + if (i < (INADDRSZ / INT16SZ) -1 ) { + sb.append(":"); + } + } + return sb.toString(); + } + + // BEGIN Android-removed: Android doesn't need to call native init + /* + * Perform class load-time initializations. + * + private static native void init(); + */ + // END Android-removed: Android doesn't need to call native init +}
diff --git a/java/net/Inet6AddressImpl.java b/java/net/Inet6AddressImpl.java new file mode 100644 index 0000000..84bcf17 --- /dev/null +++ b/java/net/Inet6AddressImpl.java
@@ -0,0 +1,352 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.net; +import android.system.ErrnoException; +import android.system.GaiException; +import android.system.StructAddrinfo; +import android.system.IcmpHeaders; + +import dalvik.system.BlockGuard; + +import libcore.io.IoBridge; +import libcore.io.Libcore; + +import java.io.FileDescriptor; +import java.io.IOException; +import libcore.net.InetAddressUtils; + +import static android.system.OsConstants.AF_INET; +import static android.system.OsConstants.AF_INET6; +import static android.system.OsConstants.AF_UNSPEC; +import static android.system.OsConstants.AI_ADDRCONFIG; +import static android.system.OsConstants.EACCES; +import static android.system.OsConstants.ECONNREFUSED; +import static android.system.OsConstants.EPERM; +import static android.system.OsConstants.NI_NAMEREQD; +import static android.system.OsConstants.ICMP6_ECHO_REPLY; +import static android.system.OsConstants.ICMP_ECHOREPLY; +import static android.system.OsConstants.IPPROTO_ICMP; +import static android.system.OsConstants.IPPROTO_ICMPV6; +import static android.system.OsConstants.SOCK_DGRAM; +import static android.system.OsConstants.SOCK_STREAM; + +// Android-note: Android-specific behavior and Linux-based implementation +// http://b/36933260 Implement root-less ICMP for isReachable() +// http://b/28609551 Rewrite getHostByAddr0 using POSIX library Libcore.os. +// http://b/25861497 Add BlockGuard checks. +// http://b/26700324 Fix odd dependency chains of the static InetAddress. +// anyLocalAddress() Let anyLocalAddress() always return an IPv6 address. +// Let loopbackAddresses() return both Inet4 and Inet6 loopbacks. +// Rewrote hostname lookup methods on top of Libcore.os. Merge implementation from InetAddress +// and remove native methods in this class +/* + * Package private implementation of InetAddressImpl for dual + * IPv4/IPv6 stack. {@code #anyLocalAddress()} will always return an IPv6 address. + * + * @since 1.4 + */ + +class Inet6AddressImpl implements InetAddressImpl { + + // @GuardedBy(Inet6AddressImpl.class) + private static InetAddress anyLocalAddress; + // @GuardedBy(Inet6AddressImpl.class) + private static InetAddress[] loopbackAddresses; + + private static final AddressCache addressCache = new AddressCache(); + + // BEGIN Android-changed: Rewrote hostname lookup methods on top of Libcore.os. + /* + public native String getLocalHostName() throws UnknownHostException; + public native InetAddress[] + lookupAllHostAddr(String hostname) throws UnknownHostException; + public native String getHostByAddr(byte[] addr) throws UnknownHostException; + private native boolean isReachable0(byte[] addr, int scope, int timeout, byte[] inf, int ttl, int if_scope) throws IOException; + */ + @Override + public InetAddress[] lookupAllHostAddr(String host, int netId) throws UnknownHostException { + if (host == null || host.isEmpty()) { + // Android-changed: Return both the Inet4 and Inet6 loopback addresses + // when host == null or empty. + return loopbackAddresses(); + } + + // Is it a numeric address? + InetAddress result = InetAddressUtils.parseNumericAddressNoThrowStripOptionalBrackets(host); + if (result != null) { + return new InetAddress[] { result }; + } + + return lookupHostByName(host, netId); + } + + /** + * Resolves a hostname to its IP addresses using a cache. + * + * @param host the hostname to resolve. + * @param netId the network to perform resolution upon. + * @return the IP addresses of the host. + */ + private static InetAddress[] lookupHostByName(String host, int netId) + throws UnknownHostException { + BlockGuard.getThreadPolicy().onNetwork(); + // Do we have a result cached? + Object cachedResult = addressCache.get(host, netId); + if (cachedResult != null) { + if (cachedResult instanceof InetAddress[]) { + // A cached positive result. + return (InetAddress[]) cachedResult; + } else { + // A cached negative result. + throw new UnknownHostException((String) cachedResult); + } + } + try { + StructAddrinfo hints = new StructAddrinfo(); + hints.ai_flags = AI_ADDRCONFIG; + hints.ai_family = AF_UNSPEC; + // If we don't specify a socket type, every address will appear twice, once + // for SOCK_STREAM and one for SOCK_DGRAM. Since we do not return the family + // anyway, just pick one. + hints.ai_socktype = SOCK_STREAM; + InetAddress[] addresses = Libcore.os.android_getaddrinfo(host, hints, netId); + // TODO: should getaddrinfo set the hostname of the InetAddresses it returns? + for (InetAddress address : addresses) { + address.holder().hostName = host; + address.holder().originalHostName = host; + } + addressCache.put(host, netId, addresses); + return addresses; + } catch (GaiException gaiException) { + // If the failure appears to have been a lack of INTERNET permission, throw a clear + // SecurityException to aid in debugging this common mistake. + // http://code.google.com/p/android/issues/detail?id=15722 + if (gaiException.getCause() instanceof ErrnoException) { + int errno = ((ErrnoException) gaiException.getCause()).errno; + if (errno == EACCES || errno == EPERM) { + throw new SecurityException("Permission denied (missing INTERNET permission?)", gaiException); + } + } + // Otherwise, throw an UnknownHostException. + String detailMessage = "Unable to resolve host \"" + host + "\": " + Libcore.os.gai_strerror(gaiException.error); + addressCache.putUnknownHost(host, netId, detailMessage); + throw gaiException.rethrowAsUnknownHostException(detailMessage); + } + } + + @Override + public String getHostByAddr(byte[] addr) throws UnknownHostException { + BlockGuard.getThreadPolicy().onNetwork(); + + return getHostByAddr0(addr); + } + + @Override + public void clearAddressCache() { + addressCache.clear(); + } + // END Android-changed: Rewrote hostname lookup methods on top of Libcore.os. + + @Override + public boolean isReachable(InetAddress addr, int timeout, NetworkInterface netif, int ttl) throws IOException { + // Android-changed: rewritten on the top of IoBridge and Libcore.os. + InetAddress sourceAddr = null; + if (netif != null) { + /* + * Let's make sure we bind to an address of the proper family. + * Which means same family as addr because at this point it could + * be either an IPv6 address or an IPv4 address (case of a dual + * stack system). + */ + java.util.Enumeration<InetAddress> it = netif.getInetAddresses(); + InetAddress inetaddr = null; + while (it.hasMoreElements()) { + inetaddr = it.nextElement(); + if (inetaddr.getClass().isInstance(addr)) { + sourceAddr = inetaddr; + break; + } + } + + if (sourceAddr == null) { + // Interface doesn't support the address family of + // the destination + return false; + } + } + + // Android-changed: http://b/36933260 Implement root-less ICMP for isReachable(). + /* + if (addr instanceof Inet6Address) + scope = ((Inet6Address) addr).getScopeId(); + return isReachable0(addr.getAddress(), scope, timeout, ifaddr, ttl, netif_scope); + */ + // Try ICMP first + if (icmpEcho(addr, timeout, sourceAddr, ttl)) { + return true; + } + + // No good, let's fall back to TCP + return tcpEcho(addr, timeout, sourceAddr, ttl); + } + + // BEGIN Android-added: http://b/36933260 Implement root-less ICMP for isReachable(). + private boolean tcpEcho(InetAddress addr, int timeout, InetAddress sourceAddr, int ttl) + throws IOException { + FileDescriptor fd = null; + try { + fd = IoBridge.socket(AF_INET6, SOCK_STREAM, 0); + if (ttl > 0) { + IoBridge.setSocketOption(fd, IoBridge.JAVA_IP_TTL, ttl); + } + if (sourceAddr != null) { + IoBridge.bind(fd, sourceAddr, 0); + } + IoBridge.connect(fd, addr, 7 /* Echo-protocol port */, timeout); + return true; + } catch (IOException e) { + // Connection refused by remote (ECONNREFUSED) implies reachable. Otherwise silently + // ignore the exception and return false. + Throwable cause = e.getCause(); + return cause instanceof ErrnoException + && ((ErrnoException) cause).errno == ECONNREFUSED; + } finally { + IoBridge.closeAndSignalBlockedThreads(fd); + } + } + + protected boolean icmpEcho(InetAddress addr, int timeout, InetAddress sourceAddr, int ttl) + throws IOException { + + FileDescriptor fd = null; + try { + boolean isIPv4 = addr instanceof Inet4Address; + int domain = isIPv4 ? AF_INET : AF_INET6; + int icmpProto = isIPv4 ? IPPROTO_ICMP : IPPROTO_ICMPV6; + fd = IoBridge.socket(domain, SOCK_DGRAM, icmpProto); + + if (ttl > 0) { + IoBridge.setSocketOption(fd, IoBridge.JAVA_IP_TTL, ttl); + } + if (sourceAddr != null) { + IoBridge.bind(fd, sourceAddr, 0); + } + + byte[] packet; + + // ICMP is unreliable, try sending requests every second until timeout. + for (int to = timeout, seq = 1; to > 0; ++seq) { + int sockTo = to >= 1000 ? 1000 : to; + + IoBridge.setSocketOption(fd, SocketOptions.SO_TIMEOUT, sockTo); + + packet = IcmpHeaders.createIcmpEchoHdr(isIPv4, seq); + IoBridge.sendto(fd, packet, 0, packet.length, 0, addr, 0); + final int icmpId = IoBridge.getLocalInetSocketAddress(fd).getPort(); + + byte[] received = new byte[packet.length]; + DatagramPacket receivedPacket = new DatagramPacket(received, packet.length); + int size = IoBridge + .recvfrom(true, fd, received, 0, received.length, 0, receivedPacket, false); + if (size == packet.length) { + byte expectedType = isIPv4 ? (byte) ICMP_ECHOREPLY + : (byte) ICMP6_ECHO_REPLY; + if (receivedPacket.getAddress().equals(addr) + && received[0] == expectedType + && received[4] == (byte) (icmpId >> 8) + && received[5] == (byte) icmpId) { + int receivedSequence = ((received[6] & 0xff) << 8) + (received[7] & 0xff); + if (receivedSequence <= seq) { + return true; + } + } + } + to -= sockTo; + } + } catch (IOException e) { + // Silently ignore and fall back. + } finally { + if (fd != null) { + try { + Libcore.os.close(fd); + } catch (ErrnoException e) { } + } + } + + return false; + } + // END Android-added: http://b/36933260 Implement root-less ICMP for isReachable(). + + // BEGIN Android-changed: Let anyLocalAddress() always return an IPv6 address. + @Override + public InetAddress anyLocalAddress() { + synchronized (Inet6AddressImpl.class) { + // We avoid initializing anyLocalAddress during <clinit> to avoid issues + // caused by the dependency chains of these classes. InetAddress depends on + // InetAddressImpl, but Inet6Address & Inet4Address are its subclasses. + // Also see {@code loopbackAddresses). http://b/26700324 + if (anyLocalAddress == null) { + Inet6Address anyAddress = new Inet6Address(); + anyAddress.holder().hostName = "::"; + anyLocalAddress = anyAddress; + } + + return anyLocalAddress; + } + } + // END Android-changed: Let anyLocalAddress() always return an IPv6 address. + + // BEGIN Android-changed: Let loopbackAddresses() return both Inet4 and Inet6 loopbacks. + @Override + public InetAddress[] loopbackAddresses() { + synchronized (Inet6AddressImpl.class) { + // We avoid initializing anyLocalAddress during <clinit> to avoid issues + // caused by the dependency chains of these classes. InetAddress depends on + // InetAddressImpl, but Inet6Address & Inet4Address are its subclasses. + // Also see {@code anyLocalAddress). + if (loopbackAddresses == null) { + loopbackAddresses = new InetAddress[]{Inet6Address.LOOPBACK, Inet4Address.LOOPBACK}; + } + + return loopbackAddresses; + } + } + // END Android-changed: Let loopbackAddresses() return both Inet4 and Inet6 loopbacks. + + // BEGIN Android-changed: b/28609551 Rewrite getHostByAddr0 using POSIX library Libcore.os. + private String getHostByAddr0(byte[] addr) throws UnknownHostException { + // Android-changed: Rewritten on the top of Libcore.os + InetAddress hostaddr = InetAddress.getByAddress(addr); + try { + return Libcore.os.getnameinfo(hostaddr, NI_NAMEREQD); + } catch (GaiException e) { + UnknownHostException uhe = new UnknownHostException(hostaddr.toString()); + uhe.initCause(e); + throw uhe; + } + } + // END Android-changed: b/28609551 Rewrite getHostByAddr0 using POSIX library Libcore.os. +}
diff --git a/java/net/InetAddress.annotated.java b/java/net/InetAddress.annotated.java new file mode 100644 index 0000000..6f4492d --- /dev/null +++ b/java/net/InetAddress.annotated.java
@@ -0,0 +1,105 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package java.net; + +import java.io.ObjectStreamException; +import java.io.IOException; + +@SuppressWarnings({"unchecked", "deprecation", "all"}) +public class InetAddress implements java.io.Serializable { + +InetAddress() { throw new RuntimeException("Stub!"); } + +public boolean isMulticastAddress() { throw new RuntimeException("Stub!"); } + +public boolean isAnyLocalAddress() { throw new RuntimeException("Stub!"); } + +public boolean isLoopbackAddress() { throw new RuntimeException("Stub!"); } + +public boolean isLinkLocalAddress() { throw new RuntimeException("Stub!"); } + +public boolean isSiteLocalAddress() { throw new RuntimeException("Stub!"); } + +public boolean isMCGlobal() { throw new RuntimeException("Stub!"); } + +public boolean isMCNodeLocal() { throw new RuntimeException("Stub!"); } + +public boolean isMCLinkLocal() { throw new RuntimeException("Stub!"); } + +public boolean isMCSiteLocal() { throw new RuntimeException("Stub!"); } + +public boolean isMCOrgLocal() { throw new RuntimeException("Stub!"); } + +public boolean isReachable(int timeout) throws java.io.IOException { throw new RuntimeException("Stub!"); } + +public boolean isReachable(java.net.NetworkInterface netif, int ttl, int timeout) throws java.io.IOException { throw new RuntimeException("Stub!"); } + +public boolean isReachableByICMP(int timeout) throws java.io.IOException { throw new RuntimeException("Stub!"); } + +public java.lang.String getHostName() { throw new RuntimeException("Stub!"); } + +public java.lang.String getCanonicalHostName() { throw new RuntimeException("Stub!"); } + +public byte[] getAddress() { throw new RuntimeException("Stub!"); } + +public java.lang.String getHostAddress() { throw new RuntimeException("Stub!"); } + +public int hashCode() { throw new RuntimeException("Stub!"); } + +public boolean equals(java.lang.Object obj) { throw new RuntimeException("Stub!"); } + +public java.lang.String toString() { throw new RuntimeException("Stub!"); } + +public static java.net.InetAddress getByAddress(java.lang.String host, byte[] addr) throws java.net.UnknownHostException { throw new RuntimeException("Stub!"); } + +public static java.net.InetAddress getByName(java.lang.String host) throws java.net.UnknownHostException { throw new RuntimeException("Stub!"); } + +public static java.net.InetAddress[] getAllByName(java.lang.String host) throws java.net.UnknownHostException { throw new RuntimeException("Stub!"); } + +public static java.net.InetAddress getLoopbackAddress() { throw new RuntimeException("Stub!"); } + +public static java.net.InetAddress getByAddress(byte[] addr) throws java.net.UnknownHostException { throw new RuntimeException("Stub!"); } + +public static java.net.InetAddress getLocalHost() throws java.net.UnknownHostException { throw new RuntimeException("Stub!"); } + [email protected] +public static boolean isNumeric(java.lang.String address) { throw new RuntimeException("Stub!"); } + [email protected] +public static java.net.InetAddress parseNumericAddress(java.lang.String numericAddress) { throw new RuntimeException("Stub!"); } + [email protected] +public static void clearDnsCache() { throw new RuntimeException("Stub!"); } + [email protected] +public static java.net.InetAddress getByNameOnNet(java.lang.String host, int netId) throws java.net.UnknownHostException { throw new RuntimeException("Stub!"); } + [email protected] +public static java.net.InetAddress[] getAllByNameOnNet(java.lang.String host, int netId) throws java.net.UnknownHostException { throw new RuntimeException("Stub!"); } +} +
diff --git a/java/net/InetAddress.java b/java/net/InetAddress.java new file mode 100644 index 0000000..e4f8660 --- /dev/null +++ b/java/net/InetAddress.java
@@ -0,0 +1,1702 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.ObjectStreamException; +import java.io.ObjectStreamField; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectInputStream.GetField; +import java.io.ObjectOutputStream; +import java.io.ObjectOutputStream.PutField; +import libcore.net.InetAddressUtils; +import sun.net.util.IPAddressUtil; +import sun.net.spi.nameservice.*; +import libcore.io.Libcore; + +/** + * This class represents an Internet Protocol (IP) address. + * + * <p> An IP address is either a 32-bit or 128-bit unsigned number + * used by IP, a lower-level protocol on which protocols like UDP and + * TCP are built. The IP address architecture is defined by <a + * href="http://www.ietf.org/rfc/rfc790.txt"><i>RFC 790: + * Assigned Numbers</i></a>, <a + * href="http://www.ietf.org/rfc/rfc1918.txt"> <i>RFC 1918: + * Address Allocation for Private Internets</i></a>, <a + * href="http://www.ietf.org/rfc/rfc2365.txt"><i>RFC 2365: + * Administratively Scoped IP Multicast</i></a>, and <a + * href="http://www.ietf.org/rfc/rfc2373.txt"><i>RFC 2373: IP + * Version 6 Addressing Architecture</i></a>. An instance of an + * InetAddress consists of an IP address and possibly its + * corresponding host name (depending on whether it is constructed + * with a host name or whether it has already done reverse host name + * resolution). + * + * <h3> Address types </h3> + * + * <blockquote><table cellspacing=2 summary="Description of unicast and multicast address types"> + * <tr><th valign=top><i>unicast</i></th> + * <td>An identifier for a single interface. A packet sent to + * a unicast address is delivered to the interface identified by + * that address. + * + * <p> The Unspecified Address -- Also called anylocal or wildcard + * address. It must never be assigned to any node. It indicates the + * absence of an address. One example of its use is as the target of + * bind, which allows a server to accept a client connection on any + * interface, in case the server host has multiple interfaces. + * + * <p> The <i>unspecified</i> address must not be used as + * the destination address of an IP packet. + * + * <p> The <i>Loopback</i> Addresses -- This is the address + * assigned to the loopback interface. Anything sent to this + * IP address loops around and becomes IP input on the local + * host. This address is often used when testing a + * client.</td></tr> + * <tr><th valign=top><i>multicast</i></th> + * <td>An identifier for a set of interfaces (typically belonging + * to different nodes). A packet sent to a multicast address is + * delivered to all interfaces identified by that address.</td></tr> + * </table></blockquote> + * + * <h4> IP address scope </h4> + * + * <p> <i>Link-local</i> addresses are designed to be used for addressing + * on a single link for purposes such as auto-address configuration, + * neighbor discovery, or when no routers are present. + * + * <p> <i>Site-local</i> addresses are designed to be used for addressing + * inside of a site without the need for a global prefix. + * + * <p> <i>Global</i> addresses are unique across the internet. + * + * <h4> Textual representation of IP addresses </h4> + * + * The textual representation of an IP address is address family specific. + * + * <p> + * + * For IPv4 address format, please refer to <A + * HREF="Inet4Address.html#format">Inet4Address#format</A>; For IPv6 + * address format, please refer to <A + * HREF="Inet6Address.html#format">Inet6Address#format</A>. + * + * <P>There is a <a href="doc-files/net-properties.html#Ipv4IPv6">couple of + * System Properties</a> affecting how IPv4 and IPv6 addresses are used.</P> + * + * <h4> Host Name Resolution </h4> + * + * Host name-to-IP address <i>resolution</i> is accomplished through + * the use of a combination of local machine configuration information + * and network naming services such as the Domain Name System (DNS) + * and Network Information Service(NIS). The particular naming + * services(s) being used is by default the local machine configured + * one. For any host name, its corresponding IP address is returned. + * + * <p> <i>Reverse name resolution</i> means that for any IP address, + * the host associated with the IP address is returned. + * + * <p> The InetAddress class provides methods to resolve host names to + * their IP addresses and vice versa. + * + * <h4> InetAddress Caching </h4> + * + * The InetAddress class has a cache to store successful as well as + * unsuccessful host name resolutions. + * + * <p> By default, when a security manager is installed, in order to + * protect against DNS spoofing attacks, + * the result of positive host name resolutions are + * cached forever. When a security manager is not installed, the default + * behavior is to cache entries for a finite (implementation dependent) + * period of time. The result of unsuccessful host + * name resolution is cached for a very short period of time (10 + * seconds) to improve performance. + * + * <p> If the default behavior is not desired, then a Java security property + * can be set to a different Time-to-live (TTL) value for positive + * caching. Likewise, a system admin can configure a different + * negative caching TTL value when needed. + * + * <p> Two Java security properties control the TTL values used for + * positive and negative host name resolution caching: + * + * <blockquote> + * <dl> + * <dt><b>networkaddress.cache.ttl</b></dt> + * <dd>Indicates the caching policy for successful name lookups from + * the name service. The value is specified as as integer to indicate + * the number of seconds to cache the successful lookup. The default + * setting is to cache for an implementation specific period of time. + * <p> + * A value of -1 indicates "cache forever". + * </dd> + * <dt><b>networkaddress.cache.negative.ttl</b> (default: 10)</dt> + * <dd>Indicates the caching policy for un-successful name lookups + * from the name service. The value is specified as as integer to + * indicate the number of seconds to cache the failure for + * un-successful lookups. + * <p> + * A value of 0 indicates "never cache". + * A value of -1 indicates "cache forever". + * </dd> + * </dl> + * </blockquote> + * + * @author Chris Warth + * @see java.net.InetAddress#getByAddress(byte[]) + * @see java.net.InetAddress#getByAddress(java.lang.String, byte[]) + * @see java.net.InetAddress#getAllByName(java.lang.String) + * @see java.net.InetAddress#getByName(java.lang.String) + * @see java.net.InetAddress#getLocalHost() + * @since JDK1.0 + */ +public +class InetAddress implements java.io.Serializable { + // BEGIN Android-removed: Android uses linux-based OsConstants. + /* + * Specify the address family: Internet Protocol, Version 4 + * @since 1.4 + * + static final int IPv4 = 1; + + /** + * Specify the address family: Internet Protocol, Version 6 + * @since 1.4 + * + static final int IPv6 = 2; + */ + // END Android-removed: Android uses linux-based OsConstants. + + // Android-removed: Android doesn't support the preference. + // /* Specify address family preference */ + //static transient boolean preferIPv6Address = false; + + static class InetAddressHolder { + /** + * Reserve the original application specified hostname. + * + * The original hostname is useful for domain-based endpoint + * identification (see RFC 2818 and RFC 6125). If an address + * was created with a raw IP address, a reverse name lookup + * may introduce endpoint identification security issue via + * DNS forging. + * + * Oracle JSSE provider is using this original hostname, via + * sun.misc.JavaNetAccess, for SSL/TLS endpoint identification. + * + * Note: May define a new public method in the future if necessary. + */ + String originalHostName; + + InetAddressHolder() {} + + InetAddressHolder(String hostName, int address, int family) { + this.originalHostName = hostName; + this.hostName = hostName; + this.address = address; + this.family = family; + } + + void init(String hostName, int family) { + this.originalHostName = hostName; + this.hostName = hostName; + if (family != -1) { + this.family = family; + } + } + + String hostName; + + String getHostName() { + return hostName; + } + + String getOriginalHostName() { + return originalHostName; + } + + /** + * Holds a 32-bit IPv4 address. + */ + int address; + + int getAddress() { + return address; + } + + // Android-changed: Documentation: use Linux-based OsConstants. + /** + * Specifies the address family type, for instance, AF_INET for IPv4 + * addresses, and AF_INET6 for IPv6 addresses. + */ + int family; + + int getFamily() { + return family; + } + } + + transient InetAddressHolder holder; + + InetAddressHolder holder() { + return holder; + } + + /* The implementation is always dual stack IPv6/IPv4 on android */ + static final InetAddressImpl impl = new Inet6AddressImpl(); + + /* Used to store the name service provider */ + // Android-changed: Android has only one name service. + // Android doesn't allow user to provide custom name services. + // private static List<NameService> nameServices = null; + private static final NameService nameService = new NameService() { + public InetAddress[] lookupAllHostAddr(String host, int netId) + throws UnknownHostException { + return impl.lookupAllHostAddr(host, netId); + } + public String getHostByAddr(byte[] addr) + throws UnknownHostException { + return impl.getHostByAddr(addr); + } + }; + + /* Used to store the best available hostname */ + private transient String canonicalHostName = null; + + /** use serialVersionUID from JDK 1.0.2 for interoperability */ + private static final long serialVersionUID = 3286316764910316507L; + + + // BEGIN Android-removed: Android doesn't need to load native library. + /* + * Load net library into runtime, and perform initializations. + * + static { + preferIPv6Address = java.security.AccessController.doPrivileged( + new GetBooleanAction("java.net.preferIPv6Addresses")).booleanValue(); + AccessController.doPrivileged( + new java.security.PrivilegedAction<Void>() { + public Void run() { + System.loadLibrary("net"); + return null; + } + }); + init(); + } + */ + // END Android-removed: Android doesn't need to load native library. + + /** + * Constructor for the Socket.accept() method. + * This creates an empty InetAddress, which is filled in by + * the accept() method. This InetAddress, however, is not + * put in the address cache, since it is not created by name. + */ + InetAddress() { + holder = new InetAddressHolder(); + } + + /** + * Replaces the de-serialized object with an Inet4Address object. + * + * @return the alternate object to the de-serialized object. + * + * @throws ObjectStreamException if a new object replacing this + * object could not be created + */ + private Object readResolve() throws ObjectStreamException { + // will replace the deserialized 'this' object + return new Inet4Address(holder().getHostName(), holder().getAddress()); + } + + /** + * Utility routine to check if the InetAddress is an + * IP multicast address. + * @return a {@code boolean} indicating if the InetAddress is + * an IP multicast address + * @since JDK1.1 + */ + public boolean isMulticastAddress() { + return false; + } + + /** + * Utility routine to check if the InetAddress in a wildcard address. + * @return a {@code boolean} indicating if the Inetaddress is + * a wildcard address. + * @since 1.4 + */ + public boolean isAnyLocalAddress() { + return false; + } + + /** + * Utility routine to check if the InetAddress is a loopback address. + * + * @return a {@code boolean} indicating if the InetAddress is + * a loopback address; or false otherwise. + * @since 1.4 + */ + public boolean isLoopbackAddress() { + return false; + } + + /** + * Utility routine to check if the InetAddress is an link local address. + * + * @return a {@code boolean} indicating if the InetAddress is + * a link local address; or false if address is not a link local unicast address. + * @since 1.4 + */ + public boolean isLinkLocalAddress() { + return false; + } + + /** + * Utility routine to check if the InetAddress is a site local address. + * + * @return a {@code boolean} indicating if the InetAddress is + * a site local address; or false if address is not a site local unicast address. + * @since 1.4 + */ + public boolean isSiteLocalAddress() { + return false; + } + + /** + * Utility routine to check if the multicast address has global scope. + * + * @return a {@code boolean} indicating if the address has + * is a multicast address of global scope, false if it is not + * of global scope or it is not a multicast address + * @since 1.4 + */ + public boolean isMCGlobal() { + return false; + } + + /** + * Utility routine to check if the multicast address has node scope. + * + * @return a {@code boolean} indicating if the address has + * is a multicast address of node-local scope, false if it is not + * of node-local scope or it is not a multicast address + * @since 1.4 + */ + public boolean isMCNodeLocal() { + return false; + } + + /** + * Utility routine to check if the multicast address has link scope. + * + * @return a {@code boolean} indicating if the address has + * is a multicast address of link-local scope, false if it is not + * of link-local scope or it is not a multicast address + * @since 1.4 + */ + public boolean isMCLinkLocal() { + return false; + } + + /** + * Utility routine to check if the multicast address has site scope. + * + * @return a {@code boolean} indicating if the address has + * is a multicast address of site-local scope, false if it is not + * of site-local scope or it is not a multicast address + * @since 1.4 + */ + public boolean isMCSiteLocal() { + return false; + } + + /** + * Utility routine to check if the multicast address has organization scope. + * + * @return a {@code boolean} indicating if the address has + * is a multicast address of organization-local scope, + * false if it is not of organization-local scope + * or it is not a multicast address + * @since 1.4 + */ + public boolean isMCOrgLocal() { + return false; + } + + + // Android-changed: Document that impl tries ICMP ECHO REQUESTs first. + // The sole implementation, Inet6AddressImpl.isReachable(), tries ICMP ECHO REQUESTs before + // TCP ECHO REQUESTs on Android. On Android, these are both possible without root access. + /** + * Test whether that address is reachable. Best effort is made by the + * implementation to try to reach the host, but firewalls and server + * configuration may block requests resulting in a unreachable status + * while some specific ports may be accessible. + * <p> + * Android implementation attempts ICMP ECHO REQUESTs first, on failure it + * will fall back to TCP ECHO REQUESTs. Success on either protocol will + * return true. + * <p> + * The timeout value, in milliseconds, indicates the maximum amount of time + * the try should take. If the operation times out before getting an + * answer, the host is deemed unreachable. A negative value will result + * in an IllegalArgumentException being thrown. + * + * @param timeout the time, in milliseconds, before the call aborts + * @return a {@code boolean} indicating if the address is reachable. + * @throws IOException if a network error occurs + * @throws IllegalArgumentException if {@code timeout} is negative. + * @since 1.5 + */ + public boolean isReachable(int timeout) throws IOException { + return isReachable(null, 0 , timeout); + } + + // Android-changed: Document that impl tries ICMP ECHO REQUESTs first. + // The sole implementation, Inet6AddressImpl.isReachable(), tries ICMP ECHO REQUESTs before + // TCP ECHO REQUESTs on Android. On Android, these are both possible without root access. + /** + * Test whether that address is reachable. Best effort is made by the + * implementation to try to reach the host, but firewalls and server + * configuration may block requests resulting in a unreachable status + * while some specific ports may be accessible. + * <p> + * Android implementation attempts ICMP ECHO REQUESTs first, on failure it + * will fall back to TCP ECHO REQUESTs. Success on either protocol will + * return true. + * <p> + * The {@code network interface} and {@code ttl} parameters + * let the caller specify which network interface the test will go through + * and the maximum number of hops the packets should go through. + * A negative value for the {@code ttl} will result in an + * IllegalArgumentException being thrown. + * <p> + * The timeout value, in milliseconds, indicates the maximum amount of time + * the try should take. If the operation times out before getting an + * answer, the host is deemed unreachable. A negative value will result + * in an IllegalArgumentException being thrown. + * + * @param netif the NetworkInterface through which the + * test will be done, or null for any interface + * @param ttl the maximum numbers of hops to try or 0 for the + * default + * @param timeout the time, in milliseconds, before the call aborts + * @throws IllegalArgumentException if either {@code timeout} + * or {@code ttl} are negative. + * @return a {@code boolean}indicating if the address is reachable. + * @throws IOException if a network error occurs + * @since 1.5 + */ + public boolean isReachable(NetworkInterface netif, int ttl, + int timeout) throws IOException { + if (ttl < 0) + throw new IllegalArgumentException("ttl can't be negative"); + if (timeout < 0) + throw new IllegalArgumentException("timeout can't be negative"); + + return impl.isReachable(this, timeout, netif, ttl); + } + + // BEGIN Android-added: isReachableByICMP(timeout). + /** + * @hide For testing only + */ + public boolean isReachableByICMP(int timeout) throws IOException { + return ((Inet6AddressImpl) impl).icmpEcho(this, timeout, null, 0); + } + // END Android-added: isReachableByICMP(timeout). + + /** + * Gets the host name for this IP address. + * + * <p>If this InetAddress was created with a host name, + * this host name will be remembered and returned; + * otherwise, a reverse name lookup will be performed + * and the result will be returned based on the system + * configured name lookup service. If a lookup of the name service + * is required, call + * {@link #getCanonicalHostName() getCanonicalHostName}. + * + * <p>If there is a security manager, its + * {@code checkConnect} method is first called + * with the hostname and {@code -1} + * as its arguments to see if the operation is allowed. + * If the operation is not allowed, it will return + * the textual representation of the IP address. + * + * @return the host name for this IP address, or if the operation + * is not allowed by the security check, the textual + * representation of the IP address. + * + * @see InetAddress#getCanonicalHostName + * @see SecurityManager#checkConnect + */ + public String getHostName() { + // Android-changed: Remove SecurityManager check. + if (holder().getHostName() == null) { + holder().hostName = InetAddress.getHostFromNameService(this); + } + return holder().getHostName(); + } + + // BEGIN Android-removed: Android doesn't support SecurityManager. + /* + * Returns the hostname for this address. + * If the host is equal to null, then this address refers to any + * of the local machine's available network addresses. + * this is package private so SocketPermission can make calls into + * here without a security check. + * + * <p>If there is a security manager, this method first + * calls its {@code checkConnect} method + * with the hostname and {@code -1} + * as its arguments to see if the calling code is allowed to know + * the hostname for this IP address, i.e., to connect to the host. + * If the operation is not allowed, it will return + * the textual representation of the IP address. + * + * @return the host name for this IP address, or if the operation + * is not allowed by the security check, the textual + * representation of the IP address. + * + * @param check make security check if true + * + * @see SecurityManager#checkConnect + * + String getHostName(boolean check) { + if (holder().getHostName() == null) { + holder().hostName = InetAddress.getHostFromNameService(this, check); + } + return holder().getHostName(); + } + */ + // END Android-removed: Android doesn't support SecurityManager. + + /** + * Gets the fully qualified domain name for this IP address. + * Best effort method, meaning we may not be able to return + * the FQDN depending on the underlying system configuration. + * + * <p>If there is a security manager, this method first + * calls its {@code checkConnect} method + * with the hostname and {@code -1} + * as its arguments to see if the calling code is allowed to know + * the hostname for this IP address, i.e., to connect to the host. + * If the operation is not allowed, it will return + * the textual representation of the IP address. + * + * @return the fully qualified domain name for this IP address, + * or if the operation is not allowed by the security check, + * the textual representation of the IP address. + * + * @see SecurityManager#checkConnect + * + * @since 1.4 + */ + public String getCanonicalHostName() { + // Android-changed: Remove SecurityManager check. + if (canonicalHostName == null) { + canonicalHostName = InetAddress.getHostFromNameService(this); + } + return canonicalHostName; + } + + // Android-changed: Remove SecurityManager check. + // * @param check make security check if true + /** + * Returns the hostname for this address. + * + * <p>If there is a security manager, this method first + * calls its {@code checkConnect} method + * with the hostname and {@code -1} + * as its arguments to see if the calling code is allowed to know + * the hostname for this IP address, i.e., to connect to the host. + * If the operation is not allowed, it will return + * the textual representation of the IP address. + * + * @return the host name for this IP address, or if the operation + * is not allowed by the security check, the textual + * representation of the IP address. + * + * @see SecurityManager#checkConnect + */ + private static String getHostFromNameService(InetAddress addr) { + String host = null; + try { + // first lookup the hostname + // Android-changed: Android has only one name service. + host = nameService.getHostByAddr(addr.getAddress()); + + /* now get all the IP addresses for this hostname, + * and make sure one of them matches the original IP + * address. We do this to try and prevent spoofing. + */ + InetAddress[] arr = nameService.lookupAllHostAddr(host, NETID_UNSET); + boolean ok = false; + + if (arr != null) { + for(int i = 0; !ok && i < arr.length; i++) { + ok = addr.equals(arr[i]); + } + } + + //XXX: if it looks a spoof just return the address? + if (!ok) { + host = addr.getHostAddress(); + return host; + } + } catch (UnknownHostException e) { + host = addr.getHostAddress(); + } + + return host; + } + + /** + * Returns the raw IP address of this {@code InetAddress} + * object. The result is in network byte order: the highest order + * byte of the address is in {@code getAddress()[0]}. + * + * @return the raw IP address of this object. + */ + public byte[] getAddress() { + return null; + } + + /** + * Returns the IP address string in textual presentation. + * + * @return the raw IP address in a string format. + * @since JDK1.0.2 + */ + public String getHostAddress() { + return null; + } + + /** + * Returns a hashcode for this IP address. + * + * @return a hash code value for this IP address. + */ + public int hashCode() { + return -1; + } + + /** + * Compares this object against the specified object. + * The result is {@code true} if and only if the argument is + * not {@code null} and it represents the same IP address as + * this object. + * <p> + * Two instances of {@code InetAddress} represent the same IP + * address if the length of the byte arrays returned by + * {@code getAddress} is the same for both, and each of the + * array components is the same for the byte arrays. + * + * @param obj the object to compare against. + * @return {@code true} if the objects are the same; + * {@code false} otherwise. + * @see java.net.InetAddress#getAddress() + */ + public boolean equals(Object obj) { + return false; + } + + /** + * Converts this IP address to a {@code String}. The + * string returned is of the form: hostname / literal IP + * address. + * + * If the host name is unresolved, no reverse name service lookup + * is performed. The hostname part will be represented by an empty string. + * + * @return a string representation of this IP address. + */ + public String toString() { + String hostName = holder().getHostName(); + return ((hostName != null) ? hostName : "") + + "/" + getHostAddress(); + } + + // BEGIN Android-removed: Resolves a hostname using Libcore.os. + /* + * Cached addresses - our own litle nis, not! + * + private static Cache addressCache = new Cache(Cache.Type.Positive); + + private static Cache negativeCache = new Cache(Cache.Type.Negative); + + private static boolean addressCacheInit = false; + + static InetAddress[] unknown_array; // put THIS in cache + + static InetAddressImpl impl; + + private static final HashMap<String, Void> lookupTable = new HashMap<>(); + + /** + * Represents a cache entry + * + static final class CacheEntry { + + CacheEntry(InetAddress[] addresses, long expiration) { + this.addresses = addresses; + this.expiration = expiration; + } + + InetAddress[] addresses; + long expiration; + } + + /** + * A cache that manages entries based on a policy specified + * at creation time. + * + static final class Cache { + private LinkedHashMap<String, CacheEntry> cache; + private Type type; + + enum Type {Positive, Negative}; + + /** + * Create cache + * + public Cache(Type type) { + this.type = type; + cache = new LinkedHashMap<String, CacheEntry>(); + } + + private int getPolicy() { + if (type == Type.Positive) { + return InetAddressCachePolicy.get(); + } else { + return InetAddressCachePolicy.getNegative(); + } + } + + /** + * Add an entry to the cache. If there's already an + * entry then for this host then the entry will be + * replaced. + * + public Cache put(String host, InetAddress[] addresses) { + int policy = getPolicy(); + if (policy == InetAddressCachePolicy.NEVER) { + return this; + } + + // purge any expired entries + + if (policy != InetAddressCachePolicy.FOREVER) { + + // As we iterate in insertion order we can + // terminate when a non-expired entry is found. + LinkedList<String> expired = new LinkedList<>(); + long now = System.currentTimeMillis(); + for (String key : cache.keySet()) { + CacheEntry entry = cache.get(key); + + if (entry.expiration >= 0 && entry.expiration < now) { + expired.add(key); + } else { + break; + } + } + + for (String key : expired) { + cache.remove(key); + } + } + + // create new entry and add it to the cache + // -- as a HashMap replaces existing entries we + // don't need to explicitly check if there is + // already an entry for this host. + long expiration; + if (policy == InetAddressCachePolicy.FOREVER) { + expiration = -1; + } else { + expiration = System.currentTimeMillis() + (policy * 1000); + } + CacheEntry entry = new CacheEntry(addresses, expiration); + cache.put(host, entry); + return this; + } + + /** + * Query the cache for the specific host. If found then + * return its CacheEntry, or null if not found. + * + public CacheEntry get(String host) { + int policy = getPolicy(); + if (policy == InetAddressCachePolicy.NEVER) { + return null; + } + CacheEntry entry = cache.get(host); + + // check if entry has expired + if (entry != null && policy != InetAddressCachePolicy.FOREVER) { + if (entry.expiration >= 0 && + entry.expiration < System.currentTimeMillis()) { + cache.remove(host); + entry = null; + } + } + + return entry; + } + } + + /* + * Initialize cache and insert anyLocalAddress into the + * unknown array with no expiry. + * + private static void cacheInitIfNeeded() { + assert Thread.holdsLock(addressCache); + if (addressCacheInit) { + return; + } + unknown_array = new InetAddress[1]; + unknown_array[0] = impl.anyLocalAddress(); + + addressCache.put(impl.anyLocalAddress().getHostName(), + unknown_array); + + addressCacheInit = true; + } + + /* + * Cache the given hostname and addresses. + * + private static void cacheAddresses(String hostname, + InetAddress[] addresses, + boolean success) { + hostname = hostname.toLowerCase(); + synchronized (addressCache) { + cacheInitIfNeeded(); + if (success) { + addressCache.put(hostname, addresses); + } else { + negativeCache.put(hostname, addresses); + } + } + } + + /* + * Lookup hostname in cache (positive & negative cache). If + * found return addresses, null if not found. + * + private static InetAddress[] getCachedAddresses(String hostname) { + hostname = hostname.toLowerCase(); + + // search both positive & negative caches + + synchronized (addressCache) { + cacheInitIfNeeded(); + + CacheEntry entry = addressCache.get(hostname); + if (entry == null) { + entry = negativeCache.get(hostname); + } + + if (entry != null) { + return entry.addresses; + } + } + + // not found + return null; + } + + private static NameService createNSProvider(String provider) { + if (provider == null) + return null; + + NameService nameService = null; + if (provider.equals("default")) { + // initialize the default name service + nameService = new NameService() { + public InetAddress[] lookupAllHostAddr(String host) + throws UnknownHostException { + return impl.lookupAllHostAddr(host); + } + public String getHostByAddr(byte[] addr) + throws UnknownHostException { + return impl.getHostByAddr(addr); + } + }; + } else { + final String providerName = provider; + try { + nameService = java.security.AccessController.doPrivileged( + new java.security.PrivilegedExceptionAction<NameService>() { + public NameService run() { + Iterator<NameServiceDescriptor> itr = + ServiceLoader.load(NameServiceDescriptor.class) + .iterator(); + while (itr.hasNext()) { + NameServiceDescriptor nsd = itr.next(); + if (providerName. + equalsIgnoreCase(nsd.getType()+"," + +nsd.getProviderName())) { + try { + return nsd.createNameService(); + } catch (Exception e) { + e.printStackTrace(); + System.err.println( + "Cannot create name service:" + +providerName+": " + e); + } + } + } + + return null; + } + } + ); + } catch (java.security.PrivilegedActionException e) { + } + } + + return nameService; + } + + static { + // create the impl + impl = InetAddressImplFactory.create(); + + // get name service if provided and requested + String provider = null;; + String propPrefix = "sun.net.spi.nameservice.provider."; + int n = 1; + nameServices = new ArrayList<NameService>(); + provider = AccessController.doPrivileged( + new GetPropertyAction(propPrefix + n)); + while (provider != null) { + NameService ns = createNSProvider(provider); + if (ns != null) + nameServices.add(ns); + + n++; + provider = AccessController.doPrivileged( + new GetPropertyAction(propPrefix + n)); + } + + // if not designate any name services provider, + // create a default one + if (nameServices.size() == 0) { + NameService ns = createNSProvider("default"); + nameServices.add(ns); + } + } + */ + // END Android-removed: Resolves a hostname using Libcore.os. + + /** + * Creates an InetAddress based on the provided host name and IP address. + * No name service is checked for the validity of the address. + * + * <p> The host name can either be a machine name, such as + * "{@code java.sun.com}", or a textual representation of its IP + * address. + * <p> No validity checking is done on the host name either. + * + * <p> If addr specifies an IPv4 address an instance of Inet4Address + * will be returned; otherwise, an instance of Inet6Address + * will be returned. + * + * <p> IPv4 address byte array must be 4 bytes long and IPv6 byte array + * must be 16 bytes long + * + * @param host the specified host + * @param addr the raw IP address in network byte order + * @return an InetAddress object created from the raw IP address. + * @exception UnknownHostException if IP address is of illegal length + * @since 1.4 + */ + public static InetAddress getByAddress(String host, byte[] addr) throws UnknownHostException { + return getByAddress(host, addr, -1 /* scopeId */); + } + + // Android-added: Called by native code in Libcore.io. + // Do not delete. Called from native code. + private static InetAddress getByAddress(String host, byte[] addr, int scopeId) + throws UnknownHostException { + if (host != null && host.length() > 0 && host.charAt(0) == '[') { + if (host.charAt(host.length()-1) == ']') { + host = host.substring(1, host.length() -1); + } + } + if (addr != null) { + if (addr.length == Inet4Address.INADDRSZ) { + return new Inet4Address(host, addr); + } else if (addr.length == Inet6Address.INADDRSZ) { + byte[] newAddr + = IPAddressUtil.convertFromIPv4MappedAddress(addr); + if (newAddr != null) { + return new Inet4Address(host, newAddr); + } else { + return new Inet6Address(host, addr, scopeId); + } + } + } + throw new UnknownHostException("addr is of illegal length"); + } + + + /** + * Determines the IP address of a host, given the host's name. + * + * <p> The host name can either be a machine name, such as + * "{@code java.sun.com}", or a textual representation of its + * IP address. If a literal IP address is supplied, only the + * validity of the address format is checked. + * + * <p> For {@code host} specified in literal IPv6 address, + * either the form defined in RFC 2732 or the literal IPv6 address + * format defined in RFC 2373 is accepted. IPv6 scoped addresses are also + * supported. See <a href="Inet6Address.html#scoped">here</a> for a description of IPv6 + * scoped addresses. + * + * <p> If the host is {@code null} then an {@code InetAddress} + * representing an address of the loopback interface is returned. + * See <a href="http://www.ietf.org/rfc/rfc3330.txt">RFC 3330</a> + * section 2 and <a href="http://www.ietf.org/rfc/rfc2373.txt">RFC 2373</a> + * section 2.5.3. </p> + * + * @param host the specified host, or {@code null}. + * @return an IP address for the given host name. + * @exception UnknownHostException if no IP address for the + * {@code host} could be found, or if a scope_id was specified + * for a global IPv6 address. + * @exception SecurityException if a security manager exists + * and its checkConnect method doesn't allow the operation + */ + public static InetAddress getByName(String host) + throws UnknownHostException { + // Android-changed: Rewritten on the top of Libcore.os. + return impl.lookupAllHostAddr(host, NETID_UNSET)[0]; + } + + /** + * Given the name of a host, returns an array of its IP addresses, + * based on the configured name service on the system. + * + * <p> The host name can either be a machine name, such as + * "{@code java.sun.com}", or a textual representation of its IP + * address. If a literal IP address is supplied, only the + * validity of the address format is checked. + * + * <p> For {@code host} specified in <i>literal IPv6 address</i>, + * either the form defined in RFC 2732 or the literal IPv6 address + * format defined in RFC 2373 is accepted. A literal IPv6 address may + * also be qualified by appending a scoped zone identifier or scope_id. + * The syntax and usage of scope_ids is described + * <a href="Inet6Address.html#scoped">here</a>. + * <p> If the host is {@code null} then an {@code InetAddress} + * representing an address of the loopback interface is returned. + * See <a href="http://www.ietf.org/rfc/rfc3330.txt">RFC 3330</a> + * section 2 and <a href="http://www.ietf.org/rfc/rfc2373.txt">RFC 2373</a> + * section 2.5.3. </p> + * + * <p> If there is a security manager and {@code host} is not + * null and {@code host.length() } is not equal to zero, the + * security manager's + * {@code checkConnect} method is called + * with the hostname and {@code -1} + * as its arguments to see if the operation is allowed. + * + * @param host the name of the host, or {@code null}. + * @return an array of all the IP addresses for a given host name. + * + * @exception UnknownHostException if no IP address for the + * {@code host} could be found, or if a scope_id was specified + * for a global IPv6 address. + * @exception SecurityException if a security manager exists and its + * {@code checkConnect} method doesn't allow the operation. + * + * @see SecurityManager#checkConnect + */ + public static InetAddress[] getAllByName(String host) + throws UnknownHostException { + // Android-changed: Resolves a hostname using Libcore.os. + // Also, returns both the Inet4 and Inet6 loopback for null/empty host + return impl.lookupAllHostAddr(host, NETID_UNSET).clone(); + } + + /** + * Returns the loopback address. + * <p> + * The InetAddress returned will represent the IPv4 + * loopback address, 127.0.0.1, or the IPv6 loopback + * address, ::1. The IPv4 loopback address returned + * is only one of many in the form 127.*.*.* + * + * @return the InetAddress loopback instance. + * @since 1.7 + */ + public static InetAddress getLoopbackAddress() { + // Android-changed: Always returns IPv6 loopback address in Android. + return impl.loopbackAddresses()[0]; + } + + // BEGIN Android-removed: Resolves a hostname using Libcore.os. + /* + * check if the literal address string has %nn appended + * returns -1 if not, or the numeric value otherwise. + * + * %nn may also be a string that represents the displayName of + * a currently available NetworkInterface. + * + private static int checkNumericZone (String s) throws UnknownHostException { + int percent = s.indexOf ('%'); + int slen = s.length(); + int digit, zone=0; + if (percent == -1) { + return -1; + } + for (int i=percent+1; i<slen; i++) { + char c = s.charAt(i); + if (c == ']') { + if (i == percent+1) { + /* empty per-cent field * + return -1; + } + break; + } + if ((digit = Character.digit (c, 10)) < 0) { + return -1; + } + zone = (zone * 10) + digit; + } + return zone; + } + + private static InetAddress[] getAllByName0 (String host) + throws UnknownHostException + { + return getAllByName0(host, true); + } + + /** + * package private so SocketPermission can call it + * + static InetAddress[] getAllByName0 (String host, boolean check) + throws UnknownHostException { + return getAllByName0 (host, null, check); + } + + private static InetAddress[] getAllByName0 (String host, InetAddress reqAddr, boolean check) + throws UnknownHostException { + + /* If it gets here it is presumed to be a hostname */ + /* Cache.get can return: null, unknownAddress, or InetAddress[] */ + + /* make sure the connection to the host is allowed, before we + * give out a hostname + * + if (check) { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkConnect(host, -1); + } + } + + InetAddress[] addresses = getCachedAddresses(host); + + /* If no entry in cache, then do the host lookup * + if (addresses == null) { + addresses = getAddressesFromNameService(host, reqAddr); + } + + if (addresses == unknown_array) + throw new UnknownHostException(host); + + return addresses.clone(); + } + + private static InetAddress[] getAddressesFromNameService(String host, InetAddress reqAddr) + throws UnknownHostException + { + InetAddress[] addresses = null; + boolean success = false; + UnknownHostException ex = null; + + // Check whether the host is in the lookupTable. + // 1) If the host isn't in the lookupTable when + // checkLookupTable() is called, checkLookupTable() + // would add the host in the lookupTable and + // return null. So we will do the lookup. + // 2) If the host is in the lookupTable when + // checkLookupTable() is called, the current thread + // would be blocked until the host is removed + // from the lookupTable. Then this thread + // should try to look up the addressCache. + // i) if it found the addresses in the + // addressCache, checkLookupTable() would + // return the addresses. + // ii) if it didn't find the addresses in the + // addressCache for any reason, + // it should add the host in the + // lookupTable and return null so the + // following code would do a lookup itself. + if ((addresses = checkLookupTable(host)) == null) { + try { + // This is the first thread which looks up the addresses + // this host or the cache entry for this host has been + // expired so this thread should do the lookup. + for (NameService nameService : nameServices) { + try { + /* + * Do not put the call to lookup() inside the + * constructor. if you do you will still be + * allocating space when the lookup fails. + * + + addresses = nameService.lookupAllHostAddr(host); + success = true; + break; + } catch (UnknownHostException uhe) { + if (host.equalsIgnoreCase("localhost")) { + InetAddress[] local = new InetAddress[] { impl.loopbackAddress() }; + addresses = local; + success = true; + break; + } + else { + addresses = unknown_array; + success = false; + ex = uhe; + } + } + } + + // More to do? + if (reqAddr != null && addresses.length > 1 && !addresses[0].equals(reqAddr)) { + // Find it? + int i = 1; + for (; i < addresses.length; i++) { + if (addresses[i].equals(reqAddr)) { + break; + } + } + // Rotate + if (i < addresses.length) { + InetAddress tmp, tmp2 = reqAddr; + for (int j = 0; j < i; j++) { + tmp = addresses[j]; + addresses[j] = tmp2; + tmp2 = tmp; + } + addresses[i] = tmp2; + } + } + // Cache the address. + cacheAddresses(host, addresses, success); + + if (!success && ex != null) + throw ex; + + } finally { + // Delete host from the lookupTable and notify + // all threads waiting on the lookupTable monitor. + updateLookupTable(host); + } + } + + return addresses; + } + + + private static InetAddress[] checkLookupTable(String host) { + synchronized (lookupTable) { + // If the host isn't in the lookupTable, add it in the + // lookuptable and return null. The caller should do + // the lookup. + if (lookupTable.containsKey(host) == false) { + lookupTable.put(host, null); + return null; + } + + // If the host is in the lookupTable, it means that another + // thread is trying to look up the addresses of this host. + // This thread should wait. + while (lookupTable.containsKey(host)) { + try { + lookupTable.wait(); + } catch (InterruptedException e) { + } + } + } + + // The other thread has finished looking up the addresses of + // the host. This thread should retry to get the addresses + // from the addressCache. If it doesn't get the addresses from + // the cache, it will try to look up the addresses itself. + InetAddress[] addresses = getCachedAddresses(host); + if (addresses == null) { + synchronized (lookupTable) { + lookupTable.put(host, null); + return null; + } + } + + return addresses; + } + + private static void updateLookupTable(String host) { + synchronized (lookupTable) { + lookupTable.remove(host); + lookupTable.notifyAll(); + } + } + */ + // END Android-removed: Resolves a hostname using Libcore.os. + + /** + * Returns an {@code InetAddress} object given the raw IP address . + * The argument is in network byte order: the highest order + * byte of the address is in {@code getAddress()[0]}. + * + * <p> This method doesn't block, i.e. no reverse name service lookup + * is performed. + * + * <p> IPv4 address byte array must be 4 bytes long and IPv6 byte array + * must be 16 bytes long + * + * @param addr the raw IP address in network byte order + * @return an InetAddress object created from the raw IP address. + * @exception UnknownHostException if IP address is of illegal length + * @since 1.4 + */ + public static InetAddress getByAddress(byte[] addr) + throws UnknownHostException { + return getByAddress(null, addr); + } + + // BEGIN Android-removed: Resolves a hostname using Libcore.os. + /* + private static InetAddress cachedLocalHost = null; + private static long cacheTime = 0; + private static final long maxCacheTime = 5000L; + private static final Object cacheLock = new Object(); + */ + // END Android-removed: Resolves a hostname using Libcore.os. + + /** + * Returns the address of the local host. This is achieved by retrieving + * the name of the host from the system, then resolving that name into + * an {@code InetAddress}. + * + * <P>Note: The resolved address may be cached for a short period of time. + * </P> + * + * <p>If there is a security manager, its + * {@code checkConnect} method is called + * with the local host name and {@code -1} + * as its arguments to see if the operation is allowed. + * If the operation is not allowed, an InetAddress representing + * the loopback address is returned. + * + * @return the address of the local host. + * + * @exception UnknownHostException if the local host name could not + * be resolved into an address. + * + * @see SecurityManager#checkConnect + * @see java.net.InetAddress#getByName(java.lang.String) + */ + public static InetAddress getLocalHost() throws UnknownHostException { + // BEGIN Android-changed: Resolves a hostname using Libcore.os. + /* + SecurityManager security = System.getSecurityManager(); + try { + String local = impl.getLocalHostName(); + + if (security != null) { + security.checkConnect(local, -1); + } + + if (local.equals("localhost")) { + return impl.loopbackAddress(); + } + + InetAddress ret = null; + synchronized (cacheLock) { + long now = System.currentTimeMillis(); + if (cachedLocalHost != null) { + if ((now - cacheTime) < maxCacheTime) // Less than 5s old? + ret = cachedLocalHost; + else + cachedLocalHost = null; + } + + // we are calling getAddressesFromNameService directly + // to avoid getting localHost from cache + if (ret == null) { + InetAddress[] localAddrs; + try { + localAddrs = + InetAddress.getAddressesFromNameService(local, null); + } catch (UnknownHostException uhe) { + // Rethrow with a more informative error message. + UnknownHostException uhe2 = + new UnknownHostException(local + ": " + + uhe.getMessage()); + uhe2.initCause(uhe); + throw uhe2; + } + cachedLocalHost = localAddrs[0]; + cacheTime = now; + ret = localAddrs[0]; + } + } + return ret; + } catch (java.lang.SecurityException e) { + return impl.loopbackAddress(); + } + */ + String local = Libcore.os.uname().nodename; + return impl.lookupAllHostAddr(local, NETID_UNSET)[0]; + // END Android-changed: Resolves a hostname using Libcore.os. + } + + // BEGIN Android-removed: Android doesn't need to call native init. + /** + * Perform class load-time initializations. + * + private static native void init(); + */ + // END Android-removed: Android doesn't need to call native init. + + /* + * Returns the InetAddress representing anyLocalAddress + * (typically 0.0.0.0 or ::0) + */ + static InetAddress anyLocalAddress() { + return impl.anyLocalAddress(); + } + + // BEGIN Android-removed: Android doesn't load user-provided implementation. + /* + * Load and instantiate an underlying impl class + * + static InetAddressImpl loadImpl(String implName) { + Object impl = null; + + /* + * Property "impl.prefix" will be prepended to the classname + * of the implementation object we instantiate, to which we + * delegate the real work (like native methods). This + * property can vary across implementations of the java. + * classes. The default is an empty String "". + * + String prefix = AccessController.doPrivileged( + new GetPropertyAction("impl.prefix", "")); + try { + impl = Class.forName("java.net." + prefix + implName).newInstance(); + } catch (ClassNotFoundException e) { + System.err.println("Class not found: java.net." + prefix + + implName + ":\ncheck impl.prefix property " + + "in your properties file."); + } catch (InstantiationException e) { + System.err.println("Could not instantiate: java.net." + prefix + + implName + ":\ncheck impl.prefix property " + + "in your properties file."); + } catch (IllegalAccessException e) { + System.err.println("Cannot access class: java.net." + prefix + + implName + ":\ncheck impl.prefix property " + + "in your properties file."); + } + + if (impl == null) { + try { + impl = Class.forName(implName).newInstance(); + } catch (Exception e) { + throw new Error("System property impl.prefix incorrect"); + } + } + + return (InetAddressImpl) impl; + } + */ + // END Android-removed: Android doesn't load user-provided implementation. + + private void readObjectNoData (ObjectInputStream s) throws + IOException, ClassNotFoundException { + // Android-changed: Don't use null to mean the boot classloader. + if (getClass().getClassLoader() != BOOT_CLASSLOADER) { + throw new SecurityException ("invalid address type"); + } + } + + // Android-changed: Don't use null to mean the boot classloader. + private static final ClassLoader BOOT_CLASSLOADER = Object.class.getClassLoader(); + + private void readObject (ObjectInputStream s) throws + IOException, ClassNotFoundException { + // Android-changed: Don't use null to mean the boot classloader. + if (getClass().getClassLoader() != BOOT_CLASSLOADER) { + throw new SecurityException ("invalid address type"); + } + GetField gf = s.readFields(); + String host = (String)gf.get("hostName", null); + int address= gf.get("address", 0); + int family= gf.get("family", 0); + holder = new InetAddressHolder(host, address, family); + } + + /* needed because the serializable fields no longer exist */ + + /** + * @serialField hostName String + * @serialField address int + * @serialField family int + */ + private static final ObjectStreamField[] serialPersistentFields = { + new ObjectStreamField("hostName", String.class), + new ObjectStreamField("address", int.class), + new ObjectStreamField("family", int.class), + }; + + private void writeObject (ObjectOutputStream s) throws + IOException { + // Android-changed: Don't use null to mean the boot classloader. + if (getClass().getClassLoader() != BOOT_CLASSLOADER) { + throw new SecurityException ("invalid address type"); + } + PutField pf = s.putFields(); + pf.put("hostName", holder().hostName); + pf.put("address", holder().address); + pf.put("family", holder().family); + s.writeFields(); + s.flush(); + } + + static final int NETID_UNSET = 0; + + // BEGIN Android-added: Add methods required by frameworks/base. + // Particularly those required to deal with scope ids. + /** + * Returns true if the string is a valid numeric IPv4 or IPv6 address (such as "192.168.0.1"). + * + * <p>This copes with all forms of address that Java supports, detailed in the + * {@link InetAddress} class documentation. An empty string is not treated as numeric. + * + * @hide used by frameworks/base to ensure that a getAllByName won't cause a DNS lookup. + * @deprecated Use {@link InetAddressUtils#isNumericAddress(String)} instead, if possible. + * @throws NullPointerException if the {@code address} is {@code null}. + */ + @Deprecated + public static boolean isNumeric(String address) { + return InetAddressUtils.parseNumericAddressNoThrowStripOptionalBrackets(address) != null; + } + + /** + * Returns an InetAddress corresponding to the given numeric address (such + * as {@code "192.168.0.1"} or {@code "2001:4860:800d::68"}). + * + * <p>This method will never do a DNS lookup. Non-numeric addresses are errors. Passing either + * an empty string or a {@code null} as the {@code numericAddress} will return the + * {@link Inet6Address#LOOPBACK} address. + * + * @hide used by frameworks/base's NetworkUtils.numericToInetAddress + * @throws IllegalArgumentException if {@code numericAddress} is not a numeric address + * @deprecated Use {@link InetAddressUtils#parseNumericAddress(String)} instead, if possible. + */ + @Deprecated + public static InetAddress parseNumericAddress(String numericAddress) { + if (numericAddress == null || numericAddress.isEmpty()) { + return Inet6Address.LOOPBACK; + } + InetAddress result = InetAddressUtils + .parseNumericAddressNoThrowStripOptionalBrackets(numericAddress); + if (result == null) { + throw new IllegalArgumentException("Not a numeric address: " + numericAddress); + } + return result; + } + + /** + * Removes all entries from the VM's DNS cache. This does not affect the C library's DNS + * cache, nor any caching DNS servers between you and the canonical server. + * @hide + */ + public static void clearDnsCache() { + impl.clearAddressCache(); + } + // END Android-added: Add methods required by frameworks/base. + // BEGIN Android-added: Support for network (netId)-specific DNS resolution. + /** + * Operates identically to {@code getByName} except host resolution is + * performed on the network designated by {@code netId}. + * + * @param host + * the hostName to be resolved to an address or {@code null}. + * @param netId the network to use for host resolution. + * @return the {@code InetAddress} instance representing the host. + * @throws UnknownHostException if the address lookup fails. + * @hide internal use only + */ + public static InetAddress getByNameOnNet(String host, int netId) throws UnknownHostException { + return impl.lookupAllHostAddr(host, netId)[0]; + } + + /** + * Operates identically to {@code getAllByName} except host resolution is + * performed on the network designated by {@code netId}. + * + * @param host the hostname or literal IP string to be resolved. + * @param netId the network to use for host resolution. + * @return the array of addresses associated with the specified host. + * @throws UnknownHostException if the address lookup fails. + * @hide internal use only + */ + public static InetAddress[] getAllByNameOnNet(String host, int netId) throws UnknownHostException { + return impl.lookupAllHostAddr(host, netId).clone(); + } + // END Android-added: Support for network (netId)-specific DNS resolution. +} +// BEGIN Android-removed: Android doesn't load user-provided implementation. +/* + * Simple factory to create the impl + * +class InetAddressImplFactory { + + static InetAddressImpl create() { + return InetAddress.loadImpl(isIPv6Supported() ? + "Inet6AddressImpl" : "Inet4AddressImpl"); + } + + static native boolean isIPv6Supported(); +} +*/ +// END Android-removed: Android doesn't load user-provided implementation.
diff --git a/java/net/InetAddressContainer.java b/java/net/InetAddressContainer.java new file mode 100644 index 0000000..28b6402 --- /dev/null +++ b/java/net/InetAddressContainer.java
@@ -0,0 +1,30 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +class InetAddressContainer { + InetAddress addr; +}
diff --git a/java/net/InetAddressImpl.java b/java/net/InetAddressImpl.java new file mode 100644 index 0000000..a636c1e --- /dev/null +++ b/java/net/InetAddressImpl.java
@@ -0,0 +1,78 @@ +/* + * Copyright (c) 2002, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; +import java.io.IOException; +/* + * Package private interface to "implementation" used by + * {@link InetAddress}. + * <p> + * See {@link java.net.Inet4AddressImp} and + * {@link java.net.Inet6AddressImp}. + * + * @since 1.4 + */ +interface InetAddressImpl { + + // BEGIN Android-changed: Rewrote hostname lookup methods on top of Libcore.os. + /* + String getLocalHostName() throws UnknownHostException; + InetAddress[] + lookupAllHostAddr(String hostname) throws UnknownHostException; + */ + /** + * Lookup all addresses for {@code hostname} on the given {@code netId}. + */ + InetAddress[] lookupAllHostAddr(String hostname, int netId) throws UnknownHostException; + + /** + * Reverse-lookup the host name for a given {@code addr}. + */ + String getHostByAddr(byte[] addr) throws UnknownHostException; + + /** + * Clear address caches (if any). + */ + public void clearAddressCache(); + // END Android-changed: Rewrote hostname lookup methods on top of Libcore.os. + + /** + * Return the "any" local address. + */ + InetAddress anyLocalAddress(); + + // Android-changed: Let loopbackAddresses() return both Inet4 and Inet6 loopbacks. + // InetAddress loopbackAddress(); + /** + * Return a list of loop back adresses for this implementation. + */ + InetAddress[] loopbackAddresses(); + + /** + * Whether {@code addr} is reachable over {@code netif}. + */ + boolean isReachable(InetAddress addr, int timeout, NetworkInterface netif, + int ttl) throws IOException; +}
diff --git a/java/net/InetSocketAddress.annotated.java b/java/net/InetSocketAddress.annotated.java new file mode 100644 index 0000000..554b8e3 --- /dev/null +++ b/java/net/InetSocketAddress.annotated.java
@@ -0,0 +1,60 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + + +@SuppressWarnings({"unchecked", "deprecation", "all"}) +public class InetSocketAddress extends java.net.SocketAddress { + [email protected] +public InetSocketAddress() { throw new RuntimeException("Stub!"); } + +public InetSocketAddress(int port) { throw new RuntimeException("Stub!"); } + +public InetSocketAddress(java.net.InetAddress addr, int port) { throw new RuntimeException("Stub!"); } + +public InetSocketAddress(java.lang.String hostname, int port) { throw new RuntimeException("Stub!"); } + +public static java.net.InetSocketAddress createUnresolved(java.lang.String host, int port) { throw new RuntimeException("Stub!"); } + +public final int getPort() { throw new RuntimeException("Stub!"); } + +public final java.net.InetAddress getAddress() { throw new RuntimeException("Stub!"); } + +public final java.lang.String getHostName() { throw new RuntimeException("Stub!"); } + +public final java.lang.String getHostString() { throw new RuntimeException("Stub!"); } + +public final boolean isUnresolved() { throw new RuntimeException("Stub!"); } + +public java.lang.String toString() { throw new RuntimeException("Stub!"); } + +public final boolean equals(java.lang.Object obj) { throw new RuntimeException("Stub!"); } + +public final int hashCode() { throw new RuntimeException("Stub!"); } +} +
diff --git a/java/net/InetSocketAddress.java b/java/net/InetSocketAddress.java new file mode 100644 index 0000000..74b559b --- /dev/null +++ b/java/net/InetSocketAddress.java
@@ -0,0 +1,434 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.net; + +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamException; +import java.io.ObjectStreamField; + +/** + * + * This class implements an IP Socket Address (IP address + port number) + * It can also be a pair (hostname + port number), in which case an attempt + * will be made to resolve the hostname. If resolution fails then the address + * is said to be <I>unresolved</I> but can still be used on some circumstances + * like connecting through a proxy. + * <p> + * It provides an immutable object used by sockets for binding, connecting, or + * as returned values. + * <p> + * The <i>wildcard</i> is a special local IP address. It usually means "any" + * and can only be used for {@code bind} operations. + * + * @see java.net.Socket + * @see java.net.ServerSocket + * @since 1.4 + */ +public class InetSocketAddress + extends SocketAddress +{ + // Private implementation class pointed to by all public methods. + private static class InetSocketAddressHolder { + // The hostname of the Socket Address + private String hostname; + // The IP address of the Socket Address + private InetAddress addr; + // The port number of the Socket Address + private int port; + + private InetSocketAddressHolder(String hostname, InetAddress addr, int port) { + this.hostname = hostname; + this.addr = addr; + this.port = port; + } + + private int getPort() { + return port; + } + + private InetAddress getAddress() { + return addr; + } + + private String getHostName() { + if (hostname != null) + return hostname; + if (addr != null) + return addr.getHostName(); + return null; + } + + private String getHostString() { + if (hostname != null) + return hostname; + if (addr != null) { + if (addr.holder().getHostName() != null) + return addr.holder().getHostName(); + else + return addr.getHostAddress(); + } + return null; + } + + private boolean isUnresolved() { + return addr == null; + } + + @Override + public String toString() { + if (isUnresolved()) { + return hostname + ":" + port; + } else { + return addr.toString() + ":" + port; + } + } + + @Override + public final boolean equals(Object obj) { + if (obj == null || !(obj instanceof InetSocketAddressHolder)) + return false; + InetSocketAddressHolder that = (InetSocketAddressHolder)obj; + boolean sameIP; + if (addr != null) + sameIP = addr.equals(that.addr); + else if (hostname != null) + sameIP = (that.addr == null) && + hostname.equalsIgnoreCase(that.hostname); + else + sameIP = (that.addr == null) && (that.hostname == null); + return sameIP && (port == that.port); + } + + @Override + public final int hashCode() { + if (addr != null) + return addr.hashCode() + port; + if (hostname != null) + return hostname.toLowerCase().hashCode() + port; + return port; + } + } + + private final transient InetSocketAddressHolder holder; + + private static final long serialVersionUID = 5076001401234631237L; + + private static int checkPort(int port) { + if (port < 0 || port > 0xFFFF) + throw new IllegalArgumentException("port out of range:" + port); + return port; + } + + private static String checkHost(String hostname) { + if (hostname == null) + throw new IllegalArgumentException("hostname can't be null"); + return hostname; + } + + // BEGIN Android-added: InetSocketAddress() ctor used by IoBridge. + /** + * @hide internal use only + */ + public InetSocketAddress() { + // These will be filled in the native implementation of recvfrom. + holder = new InetSocketAddressHolder(null, null, 0); + } + // END Android-added: InetSocketAddress() ctor used by IoBridge. + + /** + * Creates a socket address where the IP address is the wildcard address + * and the port number a specified value. + * <p> + * A valid port value is between 0 and 65535. + * A port number of {@code zero} will let the system pick up an + * ephemeral port in a {@code bind} operation. + * <p> + * @param port The port number + * @throws IllegalArgumentException if the port parameter is outside the specified + * range of valid port values. + */ + public InetSocketAddress(int port) { + // Android-changed: Defaults to IPv6. + // this(InetAddress.anyLocalAddress(), port); + this((InetAddress)null, port); + } + + /** + * + * Creates a socket address from an IP address and a port number. + * <p> + * A valid port value is between 0 and 65535. + * A port number of {@code zero} will let the system pick up an + * ephemeral port in a {@code bind} operation. + * <P> + * A {@code null} address will assign the <i>wildcard</i> address. + * <p> + * @param addr The IP address + * @param port The port number + * @throws IllegalArgumentException if the port parameter is outside the specified + * range of valid port values. + */ + public InetSocketAddress(InetAddress addr, int port) { + holder = new InetSocketAddressHolder( + null, + // Android-changed: Defaults to IPv6 if addr is null. + // addr == null ? InetAddress.anyLocalAddress() : addr, + addr == null ? Inet6Address.ANY : addr, + checkPort(port)); + } + + /** + * + * Creates a socket address from a hostname and a port number. + * <p> + * An attempt will be made to resolve the hostname into an InetAddress. + * If that attempt fails, the address will be flagged as <I>unresolved</I>. + * <p> + * If there is a security manager, its {@code checkConnect} method + * is called with the host name as its argument to check the permission + * to resolve it. This could result in a SecurityException. + * <P> + * A valid port value is between 0 and 65535. + * A port number of {@code zero} will let the system pick up an + * ephemeral port in a {@code bind} operation. + * <P> + * @param hostname the Host name + * @param port The port number + * @throws IllegalArgumentException if the port parameter is outside the range + * of valid port values, or if the hostname parameter is <TT>null</TT>. + * @throws SecurityException if a security manager is present and + * permission to resolve the host name is + * denied. + * @see #isUnresolved() + */ + public InetSocketAddress(String hostname, int port) { + checkHost(hostname); + InetAddress addr = null; + String host = null; + try { + addr = InetAddress.getByName(hostname); + } catch(UnknownHostException e) { + host = hostname; + } + holder = new InetSocketAddressHolder(host, addr, checkPort(port)); + } + + // private constructor for creating unresolved instances + private InetSocketAddress(int port, String hostname) { + holder = new InetSocketAddressHolder(hostname, null, port); + } + + /** + * + * Creates an unresolved socket address from a hostname and a port number. + * <p> + * No attempt will be made to resolve the hostname into an InetAddress. + * The address will be flagged as <I>unresolved</I>. + * <p> + * A valid port value is between 0 and 65535. + * A port number of {@code zero} will let the system pick up an + * ephemeral port in a {@code bind} operation. + * <P> + * @param host the Host name + * @param port The port number + * @throws IllegalArgumentException if the port parameter is outside + * the range of valid port values, or if the hostname + * parameter is <TT>null</TT>. + * @see #isUnresolved() + * @return a {@code InetSocketAddress} representing the unresolved + * socket address + * @since 1.5 + */ + public static InetSocketAddress createUnresolved(String host, int port) { + return new InetSocketAddress(checkPort(port), checkHost(host)); + } + + /** + * @serialField hostname String + * @serialField addr InetAddress + * @serialField port int + */ + private static final ObjectStreamField[] serialPersistentFields = { + new ObjectStreamField("hostname", String.class), + new ObjectStreamField("addr", InetAddress.class), + new ObjectStreamField("port", int.class)}; + + private void writeObject(ObjectOutputStream out) + throws IOException + { + // Don't call defaultWriteObject() + ObjectOutputStream.PutField pfields = out.putFields(); + pfields.put("hostname", holder.hostname); + pfields.put("addr", holder.addr); + pfields.put("port", holder.port); + out.writeFields(); + } + + private void readObject(ObjectInputStream in) + throws IOException, ClassNotFoundException + { + // Don't call defaultReadObject() + ObjectInputStream.GetField oisFields = in.readFields(); + final String oisHostname = (String)oisFields.get("hostname", null); + final InetAddress oisAddr = (InetAddress)oisFields.get("addr", null); + final int oisPort = oisFields.get("port", -1); + + // Check that our invariants are satisfied + checkPort(oisPort); + if (oisHostname == null && oisAddr == null) + throw new InvalidObjectException("hostname and addr " + + "can't both be null"); + + InetSocketAddressHolder h = new InetSocketAddressHolder(oisHostname, + oisAddr, + oisPort); + UNSAFE.putObject(this, FIELDS_OFFSET, h); + } + + private void readObjectNoData() + throws ObjectStreamException + { + throw new InvalidObjectException("Stream data required"); + } + + private static final long FIELDS_OFFSET; + private static final sun.misc.Unsafe UNSAFE; + static { + try { + sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe(); + FIELDS_OFFSET = unsafe.objectFieldOffset( + InetSocketAddress.class.getDeclaredField("holder")); + UNSAFE = unsafe; + } catch (ReflectiveOperationException e) { + throw new Error(e); + } + } + + /** + * Gets the port number. + * + * @return the port number. + */ + public final int getPort() { + return holder.getPort(); + } + + /** + * + * Gets the {@code InetAddress}. + * + * @return the InetAdress or {@code null} if it is unresolved. + */ + public final InetAddress getAddress() { + return holder.getAddress(); + } + + /** + * Gets the {@code hostname}. + * Note: This method may trigger a name service reverse lookup if the + * address was created with a literal IP address. + * + * @return the hostname part of the address. + */ + public final String getHostName() { + return holder.getHostName(); + } + + /** + * Returns the hostname, or the String form of the address if it + * doesn't have a hostname (it was created using a literal). + * This has the benefit of <b>not</b> attempting a reverse lookup. + * + * @return the hostname, or String representation of the address. + * @since 1.7 + */ + public final String getHostString() { + return holder.getHostString(); + } + + /** + * Checks whether the address has been resolved or not. + * + * @return {@code true} if the hostname couldn't be resolved into + * an {@code InetAddress}. + */ + public final boolean isUnresolved() { + return holder.isUnresolved(); + } + + /** + * Constructs a string representation of this InetSocketAddress. + * This String is constructed by calling toString() on the InetAddress + * and concatenating the port number (with a colon). If the address + * is unresolved then the part before the colon will only contain the hostname. + * + * @return a string representation of this object. + */ + @Override + public String toString() { + return holder.toString(); + } + + /** + * Compares this object against the specified object. + * The result is {@code true} if and only if the argument is + * not {@code null} and it represents the same address as + * this object. + * <p> + * Two instances of {@code InetSocketAddress} represent the same + * address if both the InetAddresses (or hostnames if it is unresolved) and port + * numbers are equal. + * If both addresses are unresolved, then the hostname and the port number + * are compared. + * + * Note: Hostnames are case insensitive. e.g. "FooBar" and "foobar" are + * considered equal. + * + * @param obj the object to compare against. + * @return {@code true} if the objects are the same; + * {@code false} otherwise. + * @see java.net.InetAddress#equals(java.lang.Object) + */ + @Override + public final boolean equals(Object obj) { + if (obj == null || !(obj instanceof InetSocketAddress)) + return false; + return holder.equals(((InetSocketAddress) obj).holder); + } + + /** + * Returns a hashcode for this socket address. + * + * @return a hash code value for this socket address. + */ + @Override + public final int hashCode() { + return holder.hashCode(); + } +}
diff --git a/java/net/InterfaceAddress.java b/java/net/InterfaceAddress.java new file mode 100644 index 0000000..85f9e58 --- /dev/null +++ b/java/net/InterfaceAddress.java
@@ -0,0 +1,156 @@ +/* + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +/** + * This class represents a Network Interface address. In short it's an + * IP address, a subnet mask and a broadcast address when the address is + * an IPv4 one. An IP address and a network prefix length in the case + * of IPv6 address. + * + * @see java.net.NetworkInterface + * @since 1.6 + */ +public class InterfaceAddress { + private InetAddress address = null; + private Inet4Address broadcast = null; + private short maskLength = 0; + + /* + * Package private constructor. Can't be built directly, instances are + * obtained through the NetworkInterface class. + */ + InterfaceAddress() { + } + + // BEGIN Android-added: Rewrote NetworkInterface on top of Libcore.io. + InterfaceAddress(InetAddress address, Inet4Address broadcast, InetAddress netmask) { + this.address = address; + this.broadcast = broadcast; + this.maskLength = countPrefixLength(netmask); + } + + /** + * Counts the prefix length for the netmask address. + * + * A valid netmask address must start with a continuous sequence of 1, followed by a continuous + * sequence of 0. + */ + private short countPrefixLength(InetAddress netmask) { + short count = 0; + for (byte b : netmask.getAddress()) { + for (; b != 0; b <<= 1, ++count); + } + return count; + } + // END Android-added: Rewrote NetworkInterface on top of Libcore.io. + + /** + * Returns an {@code InetAddress} for this address. + * + * @return the {@code InetAddress} for this address. + */ + public InetAddress getAddress() { + return address; + } + + /** + * Returns an {@code InetAddress} for the broadcast address + * for this InterfaceAddress. + * <p> + * Only IPv4 networks have broadcast address therefore, in the case + * of an IPv6 network, {@code null} will be returned. + * + * @return the {@code InetAddress} representing the broadcast + * address or {@code null} if there is no broadcast address. + */ + public InetAddress getBroadcast() { + return broadcast; + } + + /** + * Returns the network prefix length for this address. This is also known + * as the subnet mask in the context of IPv4 addresses. + * Typical IPv4 values would be 8 (255.0.0.0), 16 (255.255.0.0) + * or 24 (255.255.255.0). <p> + * Typical IPv6 values would be 128 (::1/128) or 10 (fe80::203:baff:fe27:1243/10) + * + * @return a {@code short} representing the prefix length for the + * subnet of that address. + */ + public short getNetworkPrefixLength() { + return maskLength; + } + + /** + * Compares this object against the specified object. + * The result is {@code true} if and only if the argument is + * not {@code null} and it represents the same interface address as + * this object. + * <p> + * Two instances of {@code InterfaceAddress} represent the same + * address if the InetAddress, the prefix length and the broadcast are + * the same for both. + * + * @param obj the object to compare against. + * @return {@code true} if the objects are the same; + * {@code false} otherwise. + * @see java.net.InterfaceAddress#hashCode() + */ + public boolean equals(Object obj) { + if (!(obj instanceof InterfaceAddress)) { + return false; + } + InterfaceAddress cmp = (InterfaceAddress) obj; + if ( !(address == null ? cmp.address == null : address.equals(cmp.address)) ) + return false; + if ( !(broadcast == null ? cmp.broadcast == null : broadcast.equals(cmp.broadcast)) ) + return false; + if (maskLength != cmp.maskLength) + return false; + return true; + } + + /** + * Returns a hashcode for this Interface address. + * + * @return a hash code value for this Interface address. + */ + public int hashCode() { + return address.hashCode() + ((broadcast != null) ? broadcast.hashCode() : 0) + maskLength; + } + + /** + * Converts this Interface address to a {@code String}. The + * string returned is of the form: InetAddress / prefix length [ broadcast address ]. + * + * @return a string representation of this Interface address. + */ + public String toString() { + return address + "/" + maskLength + " [" + broadcast + "]"; + } + +}
diff --git a/java/net/JarURLConnection.java b/java/net/JarURLConnection.java new file mode 100644 index 0000000..70dedec --- /dev/null +++ b/java/net/JarURLConnection.java
@@ -0,0 +1,309 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.IOException; +import java.util.jar.JarFile; +import java.util.jar.JarEntry; +import java.util.jar.Attributes; +import java.util.jar.Manifest; +import java.security.Permission; +import sun.net.www.ParseUtil; + +/** + * A URL Connection to a Java ARchive (JAR) file or an entry in a JAR + * file. + * + * <p>The syntax of a JAR URL is: + * + * <pre> + * jar:<url>!/{entry} + * </pre> + * + * <p>for example: + * + * <p>{@code jar:http://www.foo.com/bar/baz.jar!/COM/foo/Quux.class} + * + * <p>Jar URLs should be used to refer to a JAR file or entries in + * a JAR file. The example above is a JAR URL which refers to a JAR + * entry. If the entry name is omitted, the URL refers to the whole + * JAR file: + * + * {@code jar:http://www.foo.com/bar/baz.jar!/} + * + * <p>Users should cast the generic URLConnection to a + * JarURLConnection when they know that the URL they created is a JAR + * URL, and they need JAR-specific functionality. For example: + * + * <pre> + * URL url = new URL("jar:file:/home/duke/duke.jar!/"); + * JarURLConnection jarConnection = (JarURLConnection)url.openConnection(); + * Manifest manifest = jarConnection.getManifest(); + * </pre> + * + * <p>JarURLConnection instances can only be used to read from JAR files. + * It is not possible to get a {@link java.io.OutputStream} to modify or write + * to the underlying JAR file using this class. + * <p>Examples: + * + * <dl> + * + * <dt>A Jar entry + * <dd>{@code jar:http://www.foo.com/bar/baz.jar!/COM/foo/Quux.class} + * + * <dt>A Jar file + * <dd>{@code jar:http://www.foo.com/bar/baz.jar!/} + * + * <dt>A Jar directory + * <dd>{@code jar:http://www.foo.com/bar/baz.jar!/COM/foo/} + * + * </dl> + * + * <p>{@code !/} is referred to as the <em>separator</em>. + * + * <p>When constructing a JAR url via {@code new URL(context, spec)}, + * the following rules apply: + * + * <ul> + * + * <li>if there is no context URL and the specification passed to the + * URL constructor doesn't contain a separator, the URL is considered + * to refer to a JarFile. + * + * <li>if there is a context URL, the context URL is assumed to refer + * to a JAR file or a Jar directory. + * + * <li>if the specification begins with a '/', the Jar directory is + * ignored, and the spec is considered to be at the root of the Jar + * file. + * + * <p>Examples: + * + * <dl> + * + * <dt>context: <b>jar:http://www.foo.com/bar/jar.jar!/</b>, + * spec:<b>baz/entry.txt</b> + * + * <dd>url:<b>jar:http://www.foo.com/bar/jar.jar!/baz/entry.txt</b> + * + * <dt>context: <b>jar:http://www.foo.com/bar/jar.jar!/baz</b>, + * spec:<b>entry.txt</b> + * + * <dd>url:<b>jar:http://www.foo.com/bar/jar.jar!/baz/entry.txt</b> + * + * <dt>context: <b>jar:http://www.foo.com/bar/jar.jar!/baz</b>, + * spec:<b>/entry.txt</b> + * + * <dd>url:<b>jar:http://www.foo.com/bar/jar.jar!/entry.txt</b> + * + * </dl> + * + * </ul> + * + * @see java.net.URL + * @see java.net.URLConnection + * + * @see java.util.jar.JarFile + * @see java.util.jar.JarInputStream + * @see java.util.jar.Manifest + * @see java.util.zip.ZipEntry + * + * @author Benjamin Renaud + * @since 1.2 + */ +public abstract class JarURLConnection extends URLConnection { + + private URL jarFileURL; + private String entryName; + + /** + * The connection to the JAR file URL, if the connection has been + * initiated. This should be set by connect. + */ + protected URLConnection jarFileURLConnection; + + /** + * Creates the new JarURLConnection to the specified URL. + * @param url the URL + * @throws MalformedURLException if no legal protocol + * could be found in a specification string or the + * string could not be parsed. + */ + + protected JarURLConnection(URL url) throws MalformedURLException { + super(url); + parseSpecs(url); + } + + /* get the specs for a given url out of the cache, and compute and + * cache them if they're not there. + */ + private void parseSpecs(URL url) throws MalformedURLException { + String spec = url.getFile(); + + int separator = spec.indexOf("!/"); + /* + * REMIND: we don't handle nested JAR URLs + */ + if (separator == -1) { + throw new MalformedURLException("no !/ found in url spec:" + spec); + } + + jarFileURL = new URL(spec.substring(0, separator++)); + entryName = null; + + /* if ! is the last letter of the innerURL, entryName is null */ + if (++separator != spec.length()) { + entryName = spec.substring(separator, spec.length()); + entryName = ParseUtil.decode (entryName); + } + } + + /** + * Returns the URL for the Jar file for this connection. + * + * @return the URL for the Jar file for this connection. + */ + public URL getJarFileURL() { + return jarFileURL; + } + + /** + * Return the entry name for this connection. This method + * returns null if the JAR file URL corresponding to this + * connection points to a JAR file and not a JAR file entry. + * + * @return the entry name for this connection, if any. + */ + public String getEntryName() { + return entryName; + } + + /** + * Return the JAR file for this connection. + * + * @return the JAR file for this connection. If the connection is + * a connection to an entry of a JAR file, the JAR file object is + * returned + * + * @exception IOException if an IOException occurs while trying to + * connect to the JAR file for this connection. + * + * @see #connect + */ + public abstract JarFile getJarFile() throws IOException; + + /** + * Returns the Manifest for this connection, or null if none. + * + * @return the manifest object corresponding to the JAR file object + * for this connection. + * + * @exception IOException if getting the JAR file for this + * connection causes an IOException to be thrown. + * + * @see #getJarFile + */ + public Manifest getManifest() throws IOException { + return getJarFile().getManifest(); + } + + /** + * Return the JAR entry object for this connection, if any. This + * method returns null if the JAR file URL corresponding to this + * connection points to a JAR file and not a JAR file entry. + * + * @return the JAR entry object for this connection, or null if + * the JAR URL for this connection points to a JAR file. + * + * @exception IOException if getting the JAR file for this + * connection causes an IOException to be thrown. + * + * @see #getJarFile + * @see #getJarEntry + */ + public JarEntry getJarEntry() throws IOException { + return getJarFile().getJarEntry(entryName); + } + + /** + * Return the Attributes object for this connection if the URL + * for it points to a JAR file entry, null otherwise. + * + * @return the Attributes object for this connection if the URL + * for it points to a JAR file entry, null otherwise. + * + * @exception IOException if getting the JAR entry causes an + * IOException to be thrown. + * + * @see #getJarEntry + */ + public Attributes getAttributes() throws IOException { + JarEntry e = getJarEntry(); + return e != null ? e.getAttributes() : null; + } + + /** + * Returns the main Attributes for the JAR file for this + * connection. + * + * @return the main Attributes for the JAR file for this + * connection. + * + * @exception IOException if getting the manifest causes an + * IOException to be thrown. + * + * @see #getJarFile + * @see #getManifest + */ + public Attributes getMainAttributes() throws IOException { + Manifest man = getManifest(); + return man != null ? man.getMainAttributes() : null; + } + + /** + * Return the Certificate object for this connection if the URL + * for it points to a JAR file entry, null otherwise. This method + * can only be called once + * the connection has been completely verified by reading + * from the input stream until the end of the stream has been + * reached. Otherwise, this method will return {@code null} + * + * @return the Certificate object for this connection if the URL + * for it points to a JAR file entry, null otherwise. + * + * @exception IOException if getting the JAR entry causes an + * IOException to be thrown. + * + * @see #getJarEntry + */ + public java.security.cert.Certificate[] getCertificates() + throws IOException + { + JarEntry e = getJarEntry(); + return e != null ? e.getCertificates() : null; + } +}
diff --git a/java/net/MalformedURLException.java b/java/net/MalformedURLException.java new file mode 100644 index 0000000..7aef75c --- /dev/null +++ b/java/net/MalformedURLException.java
@@ -0,0 +1,56 @@ +/* + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.IOException; + +/** + * Thrown to indicate that a malformed URL has occurred. Either no + * legal protocol could be found in a specification string or the + * string could not be parsed. + * + * @author Arthur van Hoff + * @since JDK1.0 + */ +public class MalformedURLException extends IOException { + private static final long serialVersionUID = -182787522200415866L; + + /** + * Constructs a {@code MalformedURLException} with no detail message. + */ + public MalformedURLException() { + } + + /** + * Constructs a {@code MalformedURLException} with the + * specified detail message. + * + * @param msg the detail message. + */ + public MalformedURLException(String msg) { + super(msg); + } +}
diff --git a/java/net/MulticastSocket.java b/java/net/MulticastSocket.java new file mode 100644 index 0000000..83a18c3 --- /dev/null +++ b/java/net/MulticastSocket.java
@@ -0,0 +1,712 @@ +/* + * Copyright (c) 1995, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.IOException; +import java.util.Enumeration; + +// Android-changed: Updated example code to handle non-ASCII characters +/** + * The multicast datagram socket class is useful for sending + * and receiving IP multicast packets. A MulticastSocket is + * a (UDP) DatagramSocket, with additional capabilities for + * joining "groups" of other multicast hosts on the internet. + * <P> + * A multicast group is specified by a class D IP address + * and by a standard UDP port number. Class D IP addresses + * are in the range <CODE>224.0.0.0</CODE> to <CODE>239.255.255.255</CODE>, + * inclusive. The address 224.0.0.0 is reserved and should not be used. + * <P> + * One would join a multicast group by first creating a MulticastSocket + * with the desired port, then invoking the + * <CODE>joinGroup(InetAddress groupAddr)</CODE> + * method: + * <PRE> + * // join a Multicast group and send the group salutations + * ... + * String msg = "Hello"; + * InetAddress group = InetAddress.getByName("228.5.6.7"); + * MulticastSocket s = new MulticastSocket(6789); + * s.joinGroup(group); + * byte[] bytes = msg.getBytes(StandardCharsets.UTF_8); + * DatagramPacket hi = new DatagramPacket(bytes, bytes.length, + * group, 6789); + * s.send(hi); + * // get their responses! + * byte[] buf = new byte[1000]; + * DatagramPacket recv = new DatagramPacket(buf, buf.length); + * s.receive(recv); + * ... + * // OK, I'm done talking - leave the group... + * s.leaveGroup(group); + * </PRE> + * + * When one sends a message to a multicast group, <B>all</B> subscribing + * recipients to that host and port receive the message (within the + * time-to-live range of the packet, see below). The socket needn't + * be a member of the multicast group to send messages to it. + * <P> + * When a socket subscribes to a multicast group/port, it receives + * datagrams sent by other hosts to the group/port, as do all other + * members of the group and port. A socket relinquishes membership + * in a group by the leaveGroup(InetAddress addr) method. <B> + * Multiple MulticastSocket's</B> may subscribe to a multicast group + * and port concurrently, and they will all receive group datagrams. + * <P> + * Currently applets are not allowed to use multicast sockets. + * + * @author Pavani Diwanji + * @since JDK1.1 + */ +public +class MulticastSocket extends DatagramSocket { + + /** + * Used on some platforms to record if an outgoing interface + * has been set for this socket. + */ + private boolean interfaceSet; + + /** + * Create a multicast socket. + * + * <p>If there is a security manager, + * its {@code checkListen} method is first called + * with 0 as its argument to ensure the operation is allowed. + * This could result in a SecurityException. + * <p> + * When the socket is created the + * {@link DatagramSocket#setReuseAddress(boolean)} method is + * called to enable the SO_REUSEADDR socket option. + * + * @exception IOException if an I/O exception occurs + * while creating the MulticastSocket + * @exception SecurityException if a security manager exists and its + * {@code checkListen} method doesn't allow the operation. + * @see SecurityManager#checkListen + * @see java.net.DatagramSocket#setReuseAddress(boolean) + */ + public MulticastSocket() throws IOException { + this(new InetSocketAddress(0)); + } + + /** + * Create a multicast socket and bind it to a specific port. + * + * <p>If there is a security manager, + * its {@code checkListen} method is first called + * with the {@code port} argument + * as its argument to ensure the operation is allowed. + * This could result in a SecurityException. + * <p> + * When the socket is created the + * {@link DatagramSocket#setReuseAddress(boolean)} method is + * called to enable the SO_REUSEADDR socket option. + * + * @param port port to use + * @exception IOException if an I/O exception occurs + * while creating the MulticastSocket + * @exception SecurityException if a security manager exists and its + * {@code checkListen} method doesn't allow the operation. + * @see SecurityManager#checkListen + * @see java.net.DatagramSocket#setReuseAddress(boolean) + */ + public MulticastSocket(int port) throws IOException { + this(new InetSocketAddress(port)); + } + + /** + * Create a MulticastSocket bound to the specified socket address. + * <p> + * Or, if the address is {@code null}, create an unbound socket. + * + * <p>If there is a security manager, + * its {@code checkListen} method is first called + * with the SocketAddress port as its argument to ensure the operation is allowed. + * This could result in a SecurityException. + * <p> + * When the socket is created the + * {@link DatagramSocket#setReuseAddress(boolean)} method is + * called to enable the SO_REUSEADDR socket option. + * + * @param bindaddr Socket address to bind to, or {@code null} for + * an unbound socket. + * @exception IOException if an I/O exception occurs + * while creating the MulticastSocket + * @exception SecurityException if a security manager exists and its + * {@code checkListen} method doesn't allow the operation. + * @see SecurityManager#checkListen + * @see java.net.DatagramSocket#setReuseAddress(boolean) + * + * @since 1.4 + */ + public MulticastSocket(SocketAddress bindaddr) throws IOException { + super((SocketAddress) null); + + // Enable SO_REUSEADDR before binding + setReuseAddress(true); + + if (bindaddr != null) { + try { + bind(bindaddr); + } finally { + if (!isBound()) + close(); + } + } + } + + /** + * The lock on the socket's TTL. This is for set/getTTL and + * send(packet,ttl). + */ + private Object ttlLock = new Object(); + + /** + * The lock on the socket's interface - used by setInterface + * and getInterface + */ + private Object infLock = new Object(); + + /** + * The "last" interface set by setInterface on this MulticastSocket + */ + private InetAddress infAddress = null; + + + /** + * Set the default time-to-live for multicast packets sent out + * on this {@code MulticastSocket} in order to control the + * scope of the multicasts. + * + * <p>The ttl is an <b>unsigned</b> 8-bit quantity, and so <B>must</B> be + * in the range {@code 0 <= ttl <= 0xFF }. + * + * @param ttl the time-to-live + * @exception IOException if an I/O exception occurs + * while setting the default time-to-live value + * @deprecated use the setTimeToLive method instead, which uses + * <b>int</b> instead of <b>byte</b> as the type for ttl. + * @see #getTTL() + */ + @Deprecated + public void setTTL(byte ttl) throws IOException { + if (isClosed()) + throw new SocketException("Socket is closed"); + getImpl().setTTL(ttl); + } + + /** + * Set the default time-to-live for multicast packets sent out + * on this {@code MulticastSocket} in order to control the + * scope of the multicasts. + * + * <P> The ttl <B>must</B> be in the range {@code 0 <= ttl <= + * 255} or an {@code IllegalArgumentException} will be thrown. + * Multicast packets sent with a TTL of {@code 0} are not transmitted + * on the network but may be delivered locally. + * + * @param ttl + * the time-to-live + * + * @throws IOException + * if an I/O exception occurs while setting the + * default time-to-live value + * + * @see #getTimeToLive() + */ + public void setTimeToLive(int ttl) throws IOException { + if (ttl < 0 || ttl > 255) { + throw new IllegalArgumentException("ttl out of range"); + } + if (isClosed()) + throw new SocketException("Socket is closed"); + getImpl().setTimeToLive(ttl); + } + + /** + * Get the default time-to-live for multicast packets sent out on + * the socket. + * + * @exception IOException if an I/O exception occurs + * while getting the default time-to-live value + * @return the default time-to-live value + * @deprecated use the getTimeToLive method instead, which returns + * an <b>int</b> instead of a <b>byte</b>. + * @see #setTTL(byte) + */ + @Deprecated + public byte getTTL() throws IOException { + if (isClosed()) + throw new SocketException("Socket is closed"); + return getImpl().getTTL(); + } + + /** + * Get the default time-to-live for multicast packets sent out on + * the socket. + * @exception IOException if an I/O exception occurs while + * getting the default time-to-live value + * @return the default time-to-live value + * @see #setTimeToLive(int) + */ + public int getTimeToLive() throws IOException { + if (isClosed()) + throw new SocketException("Socket is closed"); + return getImpl().getTimeToLive(); + } + + /** + * Joins a multicast group. Its behavior may be affected by + * {@code setInterface} or {@code setNetworkInterface}. + * + * <p>If there is a security manager, this method first + * calls its {@code checkMulticast} method + * with the {@code mcastaddr} argument + * as its argument. + * + * @param mcastaddr is the multicast address to join + * + * @exception IOException if there is an error joining + * or when the address is not a multicast address. + * @exception SecurityException if a security manager exists and its + * {@code checkMulticast} method doesn't allow the join. + * + * @see SecurityManager#checkMulticast(InetAddress) + */ + public void joinGroup(InetAddress mcastaddr) throws IOException { + if (isClosed()) { + throw new SocketException("Socket is closed"); + } + + checkAddress(mcastaddr, "joinGroup"); + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkMulticast(mcastaddr); + } + + if (!mcastaddr.isMulticastAddress()) { + throw new SocketException("Not a multicast address"); + } + + /** + * required for some platforms where it's not possible to join + * a group without setting the interface first. + */ + NetworkInterface defaultInterface = NetworkInterface.getDefault(); + + if (!interfaceSet && defaultInterface != null) { + setNetworkInterface(defaultInterface); + } + + getImpl().join(mcastaddr); + } + + /** + * Leave a multicast group. Its behavior may be affected by + * {@code setInterface} or {@code setNetworkInterface}. + * + * <p>If there is a security manager, this method first + * calls its {@code checkMulticast} method + * with the {@code mcastaddr} argument + * as its argument. + * + * @param mcastaddr is the multicast address to leave + * @exception IOException if there is an error leaving + * or when the address is not a multicast address. + * @exception SecurityException if a security manager exists and its + * {@code checkMulticast} method doesn't allow the operation. + * + * @see SecurityManager#checkMulticast(InetAddress) + */ + public void leaveGroup(InetAddress mcastaddr) throws IOException { + if (isClosed()) { + throw new SocketException("Socket is closed"); + } + + checkAddress(mcastaddr, "leaveGroup"); + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkMulticast(mcastaddr); + } + + if (!mcastaddr.isMulticastAddress()) { + throw new SocketException("Not a multicast address"); + } + + getImpl().leave(mcastaddr); + } + + /** + * Joins the specified multicast group at the specified interface. + * + * <p>If there is a security manager, this method first + * calls its {@code checkMulticast} method + * with the {@code mcastaddr} argument + * as its argument. + * + * @param mcastaddr is the multicast address to join + * @param netIf specifies the local interface to receive multicast + * datagram packets, or <i>null</i> to defer to the interface set by + * {@link MulticastSocket#setInterface(InetAddress)} or + * {@link MulticastSocket#setNetworkInterface(NetworkInterface)} + * + * @exception IOException if there is an error joining + * or when the address is not a multicast address. + * @exception SecurityException if a security manager exists and its + * {@code checkMulticast} method doesn't allow the join. + * @throws IllegalArgumentException if mcastaddr is null or is a + * SocketAddress subclass not supported by this socket + * + * @see SecurityManager#checkMulticast(InetAddress) + * @since 1.4 + */ + public void joinGroup(SocketAddress mcastaddr, NetworkInterface netIf) + throws IOException { + if (isClosed()) + throw new SocketException("Socket is closed"); + + if (mcastaddr == null || !(mcastaddr instanceof InetSocketAddress)) + throw new IllegalArgumentException("Unsupported address type"); + + if (oldImpl) + throw new UnsupportedOperationException(); + + checkAddress(((InetSocketAddress)mcastaddr).getAddress(), "joinGroup"); + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkMulticast(((InetSocketAddress)mcastaddr).getAddress()); + } + + if (!((InetSocketAddress)mcastaddr).getAddress().isMulticastAddress()) { + throw new SocketException("Not a multicast address"); + } + + getImpl().joinGroup(mcastaddr, netIf); + } + + /** + * Leave a multicast group on a specified local interface. + * + * <p>If there is a security manager, this method first + * calls its {@code checkMulticast} method + * with the {@code mcastaddr} argument + * as its argument. + * + * @param mcastaddr is the multicast address to leave + * @param netIf specifies the local interface or <i>null</i> to defer + * to the interface set by + * {@link MulticastSocket#setInterface(InetAddress)} or + * {@link MulticastSocket#setNetworkInterface(NetworkInterface)} + * @exception IOException if there is an error leaving + * or when the address is not a multicast address. + * @exception SecurityException if a security manager exists and its + * {@code checkMulticast} method doesn't allow the operation. + * @throws IllegalArgumentException if mcastaddr is null or is a + * SocketAddress subclass not supported by this socket + * + * @see SecurityManager#checkMulticast(InetAddress) + * @since 1.4 + */ + public void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf) + throws IOException { + if (isClosed()) + throw new SocketException("Socket is closed"); + + if (mcastaddr == null || !(mcastaddr instanceof InetSocketAddress)) + throw new IllegalArgumentException("Unsupported address type"); + + if (oldImpl) + throw new UnsupportedOperationException(); + + checkAddress(((InetSocketAddress)mcastaddr).getAddress(), "leaveGroup"); + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkMulticast(((InetSocketAddress)mcastaddr).getAddress()); + } + + if (!((InetSocketAddress)mcastaddr).getAddress().isMulticastAddress()) { + throw new SocketException("Not a multicast address"); + } + + getImpl().leaveGroup(mcastaddr, netIf); + } + + /** + * Set the multicast network interface used by methods + * whose behavior would be affected by the value of the + * network interface. Useful for multihomed hosts. + * @param inf the InetAddress + * @exception SocketException if there is an error in + * the underlying protocol, such as a TCP error. + * @see #getInterface() + */ + public void setInterface(InetAddress inf) throws SocketException { + if (isClosed()) { + throw new SocketException("Socket is closed"); + } + checkAddress(inf, "setInterface"); + synchronized (infLock) { + getImpl().setOption(SocketOptions.IP_MULTICAST_IF, inf); + infAddress = inf; + interfaceSet = true; + } + } + + /** + * Retrieve the address of the network interface used for + * multicast packets. + * + * @return An {@code InetAddress} representing + * the address of the network interface used for + * multicast packets. + * + * @exception SocketException if there is an error in + * the underlying protocol, such as a TCP error. + * + * @see #setInterface(java.net.InetAddress) + */ + public InetAddress getInterface() throws SocketException { + if (isClosed()) { + throw new SocketException("Socket is closed"); + } + synchronized (infLock) { + InetAddress ia = + (InetAddress)getImpl().getOption(SocketOptions.IP_MULTICAST_IF); + + /** + * No previous setInterface or interface can be + * set using setNetworkInterface + */ + if (infAddress == null) { + return ia; + } + + /** + * Same interface set with setInterface? + */ + if (ia.equals(infAddress)) { + return ia; + } + + /** + * Different InetAddress from what we set with setInterface + * so enumerate the current interface to see if the + * address set by setInterface is bound to this interface. + */ + try { + NetworkInterface ni = NetworkInterface.getByInetAddress(ia); + Enumeration<InetAddress> addrs = ni.getInetAddresses(); + while (addrs.hasMoreElements()) { + InetAddress addr = addrs.nextElement(); + if (addr.equals(infAddress)) { + return infAddress; + } + } + + /** + * No match so reset infAddress to indicate that the + * interface has changed via means + */ + infAddress = null; + return ia; + } catch (Exception e) { + return ia; + } + } + } + + /** + * Specify the network interface for outgoing multicast datagrams + * sent on this socket. + * + * @param netIf the interface + * @exception SocketException if there is an error in + * the underlying protocol, such as a TCP error. + * @see #getNetworkInterface() + * @since 1.4 + */ + public void setNetworkInterface(NetworkInterface netIf) + throws SocketException { + + synchronized (infLock) { + getImpl().setOption(SocketOptions.IP_MULTICAST_IF2, netIf); + infAddress = null; + interfaceSet = true; + } + } + + /** + * Get the multicast network interface set. + * + * @exception SocketException if there is an error in + * the underlying protocol, such as a TCP error. + * @return the multicast {@code NetworkInterface} currently set + * @see #setNetworkInterface(NetworkInterface) + * @since 1.4 + */ + public NetworkInterface getNetworkInterface() throws SocketException { + // Android-changed: Support Integer IP_MULTICAST_IF2 values for app compat. + Integer niIndex + = (Integer)getImpl().getOption(SocketOptions.IP_MULTICAST_IF2); + if (niIndex == 0) { + InetAddress[] addrs = new InetAddress[1]; + addrs[0] = InetAddress.anyLocalAddress(); + return new NetworkInterface(addrs[0].getHostName(), 0, addrs); + } else { + return NetworkInterface.getByIndex(niIndex); + } + } + + /** + * Disable/Enable local loopback of multicast datagrams + * The option is used by the platform's networking code as a hint + * for setting whether multicast data will be looped back to + * the local socket. + * + * <p>Because this option is a hint, applications that want to + * verify what loopback mode is set to should call + * {@link #getLoopbackMode()} + * @param disable {@code true} to disable the LoopbackMode + * @throws SocketException if an error occurs while setting the value + * @since 1.4 + * @see #getLoopbackMode + */ + public void setLoopbackMode(boolean disable) throws SocketException { + getImpl().setOption(SocketOptions.IP_MULTICAST_LOOP, Boolean.valueOf(disable)); + } + + /** + * Get the setting for local loopback of multicast datagrams. + * + * @throws SocketException if an error occurs while getting the value + * @return true if the LoopbackMode has been disabled + * @since 1.4 + * @see #setLoopbackMode + */ + public boolean getLoopbackMode() throws SocketException { + return ((Boolean)getImpl().getOption(SocketOptions.IP_MULTICAST_LOOP)).booleanValue(); + } + + /** + * Sends a datagram packet to the destination, with a TTL (time- + * to-live) other than the default for the socket. This method + * need only be used in instances where a particular TTL is desired; + * otherwise it is preferable to set a TTL once on the socket, and + * use that default TTL for all packets. This method does <B>not + * </B> alter the default TTL for the socket. Its behavior may be + * affected by {@code setInterface}. + * + * <p>If there is a security manager, this method first performs some + * security checks. First, if {@code p.getAddress().isMulticastAddress()} + * is true, this method calls the + * security manager's {@code checkMulticast} method + * with {@code p.getAddress()} and {@code ttl} as its arguments. + * If the evaluation of that expression is false, + * this method instead calls the security manager's + * {@code checkConnect} method with arguments + * {@code p.getAddress().getHostAddress()} and + * {@code p.getPort()}. Each call to a security manager method + * could result in a SecurityException if the operation is not allowed. + * + * @param p is the packet to be sent. The packet should contain + * the destination multicast ip address and the data to be sent. + * One does not need to be the member of the group to send + * packets to a destination multicast address. + * @param ttl optional time to live for multicast packet. + * default ttl is 1. + * + * @exception IOException is raised if an error occurs i.e + * error while setting ttl. + * @exception SecurityException if a security manager exists and its + * {@code checkMulticast} or {@code checkConnect} + * method doesn't allow the send. + * + * @deprecated Use the following code or its equivalent instead: + * ...... + * int ttl = mcastSocket.getTimeToLive(); + * mcastSocket.setTimeToLive(newttl); + * mcastSocket.send(p); + * mcastSocket.setTimeToLive(ttl); + * ...... + * + * @see DatagramSocket#send + * @see DatagramSocket#receive + * @see SecurityManager#checkMulticast(java.net.InetAddress, byte) + * @see SecurityManager#checkConnect + */ + @Deprecated + public void send(DatagramPacket p, byte ttl) + throws IOException { + if (isClosed()) + throw new SocketException("Socket is closed"); + checkAddress(p.getAddress(), "send"); + synchronized(ttlLock) { + synchronized(p) { + if (connectState == ST_NOT_CONNECTED) { + // Security manager makes sure that the multicast address + // is allowed one and that the ttl used is less + // than the allowed maxttl. + SecurityManager security = System.getSecurityManager(); + if (security != null) { + if (p.getAddress().isMulticastAddress()) { + security.checkMulticast(p.getAddress(), ttl); + } else { + security.checkConnect(p.getAddress().getHostAddress(), + p.getPort()); + } + } + } else { + // we're connected + InetAddress packetAddress = null; + packetAddress = p.getAddress(); + if (packetAddress == null) { + p.setAddress(connectedAddress); + p.setPort(connectedPort); + } else if ((!packetAddress.equals(connectedAddress)) || + p.getPort() != connectedPort) { + throw new SecurityException("connected address and packet address" + + " differ"); + } + } + byte dttl = getTTL(); + try { + if (ttl != dttl) { + // set the ttl + getImpl().setTTL(ttl); + } + // call the datagram method to send + getImpl().send(p); + } finally { + // set it back to default + if (ttl != dttl) { + getImpl().setTTL(dttl); + } + } + } // synch p + } //synch ttl + } //method +}
diff --git a/java/net/NetPermission.java b/java/net/NetPermission.java new file mode 100644 index 0000000..b5a0eab --- /dev/null +++ b/java/net/NetPermission.java
@@ -0,0 +1,47 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.security.*; + +// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager. +// See comments in java.lang.SecurityManager for details. +/** + * Legacy security code; do not use. + */ + +public final class NetPermission extends BasicPermission { + + public NetPermission(String name) + { + super(""); + } + + public NetPermission(String name, String actions) + { + super("", ""); + } +}
diff --git a/java/net/NetworkInterface.java b/java/net/NetworkInterface.java new file mode 100644 index 0000000..a30b6bf --- /dev/null +++ b/java/net/NetworkInterface.java
@@ -0,0 +1,748 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import android.system.ErrnoException; + +import java.io.FileDescriptor; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; + +import android.system.StructIfaddrs; +import libcore.io.IoUtils; +import libcore.io.Libcore; +import sun.security.action.*; +import java.security.AccessController; + +import static android.system.OsConstants.*; + +// Android-note: NetworkInterface has been rewritten to avoid native code. +// Fix upstream bug not returning link-down interfaces. http://b/26238832 +// Android-added: Document restrictions for targetSdkVersion >= R. http://b/141455849 +/** + * This class represents a Network Interface made up of a name, + * and a list of IP addresses assigned to this interface. + * It is used to identify the local interface on which a multicast group + * is joined. + * + * Interfaces are normally known by names such as "le0". + * <p> + * <a name="access-restrictions"></a>Note that information about + * {@link NetworkInterface}s may be restricted. For example, non-system apps + * with {@code targetSdkVersion >= android.os.Build.VERSION_CODES.R} will only + * have access to information about {@link NetworkInterface}s that are + * associated with an {@link InetAddress}. + * + * @since 1.4 + */ +public final class NetworkInterface { + private String name; + private String displayName; + private int index; + private InetAddress addrs[]; + private InterfaceAddress bindings[]; + // Android-changed: Rewrote NetworkInterface on top of Libcore.io. + // private NetworkInterface childs[]; + private List<NetworkInterface> childs; + private NetworkInterface parent = null; + private boolean virtual = false; + private static final NetworkInterface defaultInterface; + private static final int defaultIndex; /* index of defaultInterface */ + + // Android-changed: Fix upstream bug not returning link-down interfaces. http://b/26238832 + private byte[] hardwareAddr; + + static { + // Android-removed: Android doesn't need to call native init. + /* + AccessController.doPrivileged( + new java.security.PrivilegedAction<Void>() { + public Void run() { + System.loadLibrary("net"); + return null; + } + }); + + init(); + */ + defaultInterface = DefaultInterface.getDefault(); + if (defaultInterface != null) { + defaultIndex = defaultInterface.getIndex(); + } else { + defaultIndex = 0; + } + } + + /** + * Returns an NetworkInterface object with index set to 0 and name to null. + * Setting such an interface on a MulticastSocket will cause the + * kernel to choose one interface for sending multicast packets. + * + */ + NetworkInterface() { + } + + NetworkInterface(String name, int index, InetAddress[] addrs) { + this.name = name; + this.index = index; + this.addrs = addrs; + } + + /** + * Get the name of this network interface. + * + * @return the name of this network interface + */ + public String getName() { + return name; + } + + /** + * Convenience method to return an Enumeration with all or a + * subset of the InetAddresses bound to this network interface. + * <p> + * If there is a security manager, its {@code checkConnect} + * method is called for each InetAddress. Only InetAddresses where + * the {@code checkConnect} doesn't throw a SecurityException + * will be returned in the Enumeration. However, if the caller has the + * {@link NetPermission}("getNetworkInformation") permission, then all + * InetAddresses are returned. + * @return an Enumeration object with all or a subset of the InetAddresses + * bound to this network interface + */ + public Enumeration<InetAddress> getInetAddresses() { + + class checkedAddresses implements Enumeration<InetAddress> { + + private int i=0, count=0; + private InetAddress local_addrs[]; + + checkedAddresses() { + local_addrs = new InetAddress[addrs.length]; + boolean trusted = true; + + SecurityManager sec = System.getSecurityManager(); + if (sec != null) { + try { + sec.checkPermission(new NetPermission("getNetworkInformation")); + } catch (SecurityException e) { + trusted = false; + } + } + for (int j=0; j<addrs.length; j++) { + try { + if (sec != null && !trusted) { + sec.checkConnect(addrs[j].getHostAddress(), -1); + } + local_addrs[count++] = addrs[j]; + } catch (SecurityException e) { } + } + + } + + public InetAddress nextElement() { + if (i < count) { + return local_addrs[i++]; + } else { + throw new NoSuchElementException(); + } + } + + public boolean hasMoreElements() { + return (i < count); + } + } + return new checkedAddresses(); + + } + + /** + * Get a List of all or a subset of the {@code InterfaceAddresses} + * of this network interface. + * <p> + * If there is a security manager, its {@code checkConnect} + * method is called with the InetAddress for each InterfaceAddress. + * Only InterfaceAddresses where the {@code checkConnect} doesn't throw + * a SecurityException will be returned in the List. + * + * @return a {@code List} object with all or a subset of the + * InterfaceAddresss of this network interface + * @since 1.6 + */ + public java.util.List<InterfaceAddress> getInterfaceAddresses() { + java.util.List<InterfaceAddress> lst = new java.util.ArrayList<InterfaceAddress>(1); + // BEGIN Android-changed: Cherry-picked upstream OpenJDK9 change rev 59a110a38cea + // http://b/30628919 + if (bindings != null) { + SecurityManager sec = System.getSecurityManager(); + for (int j=0; j<bindings.length; j++) { + try { + if (sec != null) { + sec.checkConnect(bindings[j].getAddress().getHostAddress(), -1); + } + lst.add(bindings[j]); + } catch (SecurityException e) { } + } + } + // END Android-changed: Cherry-picked upstream OpenJDK9 change rev 59a110a38cea + return lst; + } + + /** + * Get an Enumeration with all the subinterfaces (also known as virtual + * interfaces) attached to this network interface. + * <p> + * For instance eth0:1 will be a subinterface to eth0. + * + * @return an Enumeration object with all of the subinterfaces + * of this network interface + * @since 1.6 + */ + public Enumeration<NetworkInterface> getSubInterfaces() { + // Android-changed: Rewrote NetworkInterface on top of Libcore.io. + return Collections.enumeration(childs); + } + + /** + * Returns the parent NetworkInterface of this interface if this is + * a subinterface, or {@code null} if it is a physical + * (non virtual) interface or has no parent. + * + * @return The {@code NetworkInterface} this interface is attached to. + * @since 1.6 + */ + public NetworkInterface getParent() { + return parent; + } + + /** + * Returns the index of this network interface. The index is an integer greater + * or equal to zero, or {@code -1} for unknown. This is a system specific value + * and interfaces with the same name can have different indexes on different + * machines. + * + * @return the index of this network interface or {@code -1} if the index is + * unknown + * @see #getByIndex(int) + * @since 1.7 + */ + public int getIndex() { + return index; + } + + /** + * Get the display name of this network interface. + * A display name is a human readable String describing the network + * device. + * + * @return a non-empty string representing the display name of this network + * interface, or null if no display name is available. + */ + public String getDisplayName() { + /* strict TCK conformance */ + return "".equals(displayName) ? null : displayName; + } + + // Android-added: Document restrictions for targetSdkVersion >= R. http://b/141455849 + /** + * Searches for the network interface with the specified name. + * + * @param name + * The name of the network interface. + * + * @return A {@code NetworkInterface} with the specified name, + * or {@code null} if the network interface with the specified + * name does not exist or <a href="#access-restrictions">can't be + * accessed</a>. + * + * @throws SocketException + * If an I/O error occurs. + * + * @throws NullPointerException + * If the specified name is {@code null}. + */ + public static NetworkInterface getByName(String name) throws SocketException { + if (name == null) + throw new NullPointerException(); + + // Android-changed: Rewrote NetworkInterface on top of Libcore.io. + NetworkInterface[] nis = getAll(); + for (NetworkInterface ni : nis) { + if (ni.getName().equals(name)) { + return ni; + } + } + return null; + } + + // Android-added: Document restrictions for targetSdkVersion >= R. http://b/141455849 + /** + * Get a network interface given its index. + * + * @param index an integer, the index of the interface + * @return the NetworkInterface obtained from its index, or {@code null} if + * an interface with the specified index does not exist or + * <a href="#access-restrictions">can't be accessed</a>. + * @throws SocketException if an I/O error occurs. + * @throws IllegalArgumentException if index has a negative value + * @see #getIndex() + * @since 1.7 + */ + public static NetworkInterface getByIndex(int index) throws SocketException { + if (index < 0) + throw new IllegalArgumentException("Interface index can't be negative"); + + // Android-changed: Rewrote NetworkInterface on top of Libcore.io. + NetworkInterface[] nis = getAll(); + for (NetworkInterface ni : nis) { + if (ni.getIndex() == index) { + return ni; + } + } + return null; + } + + /** + * Convenience method to search for a network interface that + * has the specified Internet Protocol (IP) address bound to + * it. + * <p> + * If the specified IP address is bound to multiple network + * interfaces it is not defined which network interface is + * returned. + * + * @param addr + * The {@code InetAddress} to search with. + * + * @return A {@code NetworkInterface} + * or {@code null} if there is no network interface + * with the specified IP address. + * + * @throws SocketException + * If an I/O error occurs. + * + * @throws NullPointerException + * If the specified address is {@code null}. + */ + public static NetworkInterface getByInetAddress(InetAddress addr) throws SocketException { + if (addr == null) { + throw new NullPointerException(); + } + if (!(addr instanceof Inet4Address || addr instanceof Inet6Address)) { + throw new IllegalArgumentException ("invalid address type"); + } + + // Android-changed: Rewrote NetworkInterface on top of Libcore.io. + NetworkInterface[] nis = getAll(); + for (NetworkInterface ni : nis) { + for (InetAddress inetAddress : Collections.list(ni.getInetAddresses())) { + if (inetAddress.equals(addr)) { + return ni; + } + } + } + return null; + } + + // Android-added: Document restrictions for targetSdkVersion >= R. http://b/141455849 + /** + * Returns all the interfaces on this machine. The {@code Enumeration} + * contains at least one element, possibly representing a loopback + * interface that only supports communication between entities on + * this machine. + * + * NOTE: can use getNetworkInterfaces()+getInetAddresses() + * to obtain all IP addresses for this node + * <p> + * For non-system apps with + * {@code targetSdkVersion >= android.os.Build.VERSION_CODES.R}, this + * method will only return information for {@link NetworkInterface}s that + * are associated with an {@link InetAddress}. + * + * @return an Enumeration of NetworkInterfaces found on this machine + * that <a href="#access-restrictions">are accessible</a>. + * @exception SocketException if an I/O error occurs. + */ + + public static Enumeration<NetworkInterface> getNetworkInterfaces() + throws SocketException { + final NetworkInterface[] netifs = getAll(); + // Android-changed: Rewrote NetworkInterface on top of Libcore.io. + // // specified to return null if no network interfaces + // if (netifs == null) + if (netifs.length == 0) + return null; + + // Android-changed: Rewrote NetworkInterface on top of Libcore.io. + /* + return new Enumeration<NetworkInterface>() { + private int i = 0; + public NetworkInterface nextElement() { + if (netifs != null && i < netifs.length) { + NetworkInterface netif = netifs[i++]; + return netif; + } else { + throw new NoSuchElementException(); + } + } + + public boolean hasMoreElements() { + return (netifs != null && i < netifs.length); + } + }; + */ + return Collections.enumeration(Arrays.asList(netifs)); + } + + // BEGIN Android-changed: Rewrote NetworkInterface on top of Libcore.io. + // private native static NetworkInterface[] getAll() + // throws SocketException; + private static NetworkInterface[] getAll() throws SocketException { + // Group Ifaddrs by interface name. + Map<String, List<StructIfaddrs>> inetMap = new HashMap<>(); + + StructIfaddrs[] ifaddrs; + try { + ifaddrs = Libcore.os.getifaddrs(); + } catch (ErrnoException e) { + throw e.rethrowAsSocketException(); + } + + for (StructIfaddrs ifa : ifaddrs) { + String name = ifa.ifa_name; + + List<StructIfaddrs> ifas; + if ((ifas = inetMap.get(name)) == null) { + ifas = new ArrayList<>(); + inetMap.put(name, ifas); + } + + ifas.add(ifa); + } + + // Populate NetworkInterface instances. + Map<String, NetworkInterface> nis = new HashMap<>(inetMap.size()); + for (Map.Entry<String, List<StructIfaddrs>> e : inetMap.entrySet()) { + String name = e.getKey(); + int index = Libcore.os.if_nametoindex(e.getKey()); + if (index == 0) { + // This interface has gone away between getifaddrs and if_nametoindex + continue; + } + + NetworkInterface ni = new NetworkInterface(name, index, null); + ni.displayName = name; + + List<InetAddress> addrs = new ArrayList<>(); + List<InterfaceAddress> binds = new ArrayList<>(); + + for (StructIfaddrs ifa : e.getValue()) { + if (ifa.ifa_addr != null) { + addrs.add(ifa.ifa_addr); + binds.add(new InterfaceAddress(ifa.ifa_addr, (Inet4Address) ifa.ifa_broadaddr, + ifa.ifa_netmask)); + } + + if (ifa.hwaddr != null) { + ni.hardwareAddr = ifa.hwaddr; + } + } + + ni.addrs = addrs.toArray(new InetAddress[addrs.size()]); + ni.bindings = binds.toArray(new InterfaceAddress[binds.size()]); + ni.childs = new ArrayList<>(0); + nis.put(name, ni); + } + + // Populate childs/parent. + for (Map.Entry<String, NetworkInterface> e : nis.entrySet()) { + NetworkInterface ni = e.getValue(); + String niName = ni.getName(); + int colonIdx = niName.indexOf(':'); + if (colonIdx != -1) { + // This is a virtual interface. + String parentName = niName.substring(0, colonIdx); + NetworkInterface parent = nis.get(parentName); + + ni.virtual = true; + ni.parent = parent; + parent.childs.add(ni); + } + } + + return nis.values().toArray(new NetworkInterface[nis.size()]); + } + // END Android-changed: Rewrote NetworkInterface on top of Libcore.io. + + /** + * Returns whether a network interface is up and running. + * + * @return {@code true} if the interface is up and running. + * @exception SocketException if an I/O error occurs. + * @since 1.6 + */ + + public boolean isUp() throws SocketException { + // Android-changed: Rewrote NetworkInterface on top of Libcore.io. + final int mask = IFF_UP | IFF_RUNNING; + return (getFlags() & mask) == mask; + } + + /** + * Returns whether a network interface is a loopback interface. + * + * @return {@code true} if the interface is a loopback interface. + * @exception SocketException if an I/O error occurs. + * @since 1.6 + */ + + public boolean isLoopback() throws SocketException { + // Android-changed: Rewrote NetworkInterface on top of Libcore.io. + return (getFlags() & IFF_LOOPBACK) != 0; + } + + /** + * Returns whether a network interface is a point to point interface. + * A typical point to point interface would be a PPP connection through + * a modem. + * + * @return {@code true} if the interface is a point to point + * interface. + * @exception SocketException if an I/O error occurs. + * @since 1.6 + */ + + public boolean isPointToPoint() throws SocketException { + // Android-changed: Rewrote NetworkInterface on top of Libcore.io. + return (getFlags() & IFF_POINTOPOINT) != 0; + } + + /** + * Returns whether a network interface supports multicasting or not. + * + * @return {@code true} if the interface supports Multicasting. + * @exception SocketException if an I/O error occurs. + * @since 1.6 + */ + + public boolean supportsMulticast() throws SocketException { + // Android-changed: Rewrote NetworkInterface on top of Libcore.io. + return (getFlags() & IFF_MULTICAST) != 0; + } + + // Android-added: Document restrictions for targetSdkVersion >= R. http://b/141455849 + /** + * Returns the hardware address (usually MAC) of the interface if it + * has one and if it can be accessed given the current privileges. + * If a security manager is set, then the caller must have + * the permission {@link NetPermission}("getNetworkInformation"). + * + * @return a byte array containing the address, or {@code null} if + * the address doesn't exist, is not accessible or a security + * manager is set and the caller does not have the permission + * NetPermission("getNetworkInformation"). For example, this + * method will generally return {@code null} when called by + * non-system apps having + * {@code targetSdkVersion >= android.os.Build.VERSION_CODES.R}. + * + * @exception SocketException if an I/O error occurs. + * @since 1.6 + */ + public byte[] getHardwareAddress() throws SocketException { + // BEGIN Android-changed: Fix upstream not returning link-down interfaces. http://b/26238832 + /* + for (InetAddress addr : addrs) { + if (addr instanceof Inet4Address) { + return getMacAddr0(((Inet4Address)addr).getAddress(), name, index); + } + } + return getMacAddr0(null, name, index); + */ + NetworkInterface ni = getByName(name); + if (ni == null) { + throw new SocketException("NetworkInterface doesn't exist anymore"); + } + return ni.hardwareAddr; + // END Android-changed: Fix upstream not returning link-down interfaces. http://b/26238832 + } + + /** + * Returns the Maximum Transmission Unit (MTU) of this interface. + * + * @return the value of the MTU for that interface. + * @exception SocketException if an I/O error occurs. + * @since 1.6 + */ + public int getMTU() throws SocketException { + // Android-changed: Rewrote NetworkInterface on top of Libcore.io. + // return getMTU0(name, index); + FileDescriptor fd = null; + try { + fd = Libcore.rawOs.socket(AF_INET, SOCK_DGRAM, 0); + return Libcore.rawOs.ioctlMTU(fd, name); + } catch (ErrnoException e) { + throw e.rethrowAsSocketException(); + } catch (Exception ex) { + throw new SocketException(ex); + } finally { + IoUtils.closeQuietly(fd); + } + } + + /** + * Returns whether this interface is a virtual interface (also called + * subinterface). + * Virtual interfaces are, on some systems, interfaces created as a child + * of a physical interface and given different settings (like address or + * MTU). Usually the name of the interface will the name of the parent + * followed by a colon (:) and a number identifying the child since there + * can be several virtual interfaces attached to a single physical + * interface. + * + * @return {@code true} if this interface is a virtual interface. + * @since 1.6 + */ + public boolean isVirtual() { + return virtual; + } + + // BEGIN Android-removed: Rewrote NetworkInterface on top of Libcore.io. + /* + private native static boolean isUp0(String name, int ind) throws SocketException; + private native static boolean isLoopback0(String name, int ind) throws SocketException; + private native static boolean supportsMulticast0(String name, int ind) throws SocketException; + private native static boolean isP2P0(String name, int ind) throws SocketException; + private native static byte[] getMacAddr0(byte[] inAddr, String name, int ind) throws SocketException; + private native static int getMTU0(String name, int ind) throws SocketException; + */ + // END Android-removed: Rewrote NetworkInterface on top of Libcore.io. + + // BEGIN Android-added: Rewrote NetworkInterface on top of Libcore.io. + private int getFlags() throws SocketException { + FileDescriptor fd = null; + try { + fd = Libcore.rawOs.socket(AF_INET, SOCK_DGRAM, 0); + return Libcore.rawOs.ioctlFlags(fd, name); + } catch (ErrnoException e) { + throw e.rethrowAsSocketException(); + } catch (Exception ex) { + throw new SocketException(ex); + } finally { + IoUtils.closeQuietly(fd); + } + } + // END Android-added: Rewrote NetworkInterface on top of Libcore.io. + + /** + * Compares this object against the specified object. + * The result is {@code true} if and only if the argument is + * not {@code null} and it represents the same NetworkInterface + * as this object. + * <p> + * Two instances of {@code NetworkInterface} represent the same + * NetworkInterface if both name and addrs are the same for both. + * + * @param obj the object to compare against. + * @return {@code true} if the objects are the same; + * {@code false} otherwise. + * @see java.net.InetAddress#getAddress() + */ + public boolean equals(Object obj) { + if (!(obj instanceof NetworkInterface)) { + return false; + } + NetworkInterface that = (NetworkInterface)obj; + if (this.name != null ) { + if (!this.name.equals(that.name)) { + return false; + } + } else { + if (that.name != null) { + return false; + } + } + + if (this.addrs == null) { + return that.addrs == null; + } else if (that.addrs == null) { + return false; + } + + /* Both addrs not null. Compare number of addresses */ + + if (this.addrs.length != that.addrs.length) { + return false; + } + + InetAddress[] thatAddrs = that.addrs; + int count = thatAddrs.length; + + for (int i=0; i<count; i++) { + boolean found = false; + for (int j=0; j<count; j++) { + if (addrs[i].equals(thatAddrs[j])) { + found = true; + break; + } + } + if (!found) { + return false; + } + } + return true; + } + + public int hashCode() { + return name == null? 0: name.hashCode(); + } + + public String toString() { + String result = "name:"; + result += name == null? "null": name; + if (displayName != null) { + result += " (" + displayName + ")"; + } + return result; + } + + // Android-removed: Android doesn't need to call native init. + // private static native void init(); + + /** + * Returns the default network interface of this system + * + * @return the default interface + */ + static NetworkInterface getDefault() { + return defaultInterface; + } +}
diff --git a/java/net/NoRouteToHostException.java b/java/net/NoRouteToHostException.java new file mode 100644 index 0000000..76f0814 --- /dev/null +++ b/java/net/NoRouteToHostException.java
@@ -0,0 +1,54 @@ +/* + * Copyright (c) 1996, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +/** + * Signals that an error occurred while attempting to connect a + * socket to a remote address and port. Typically, the remote + * host cannot be reached because of an intervening firewall, or + * if an intermediate router is down. + * + * @since JDK1.1 + */ +public class NoRouteToHostException extends SocketException { + private static final long serialVersionUID = -1897550894873493790L; + + /** + * Constructs a new NoRouteToHostException with the specified detail + * message as to why the remote host cannot be reached. + * A detail message is a String that gives a specific + * description of this error. + * @param msg the detail message + */ + public NoRouteToHostException(String msg) { + super(msg); + } + + /** + * Construct a new NoRouteToHostException with no detailed message. + */ + public NoRouteToHostException() {} +}
diff --git a/java/net/PasswordAuthentication.java b/java/net/PasswordAuthentication.java new file mode 100644 index 0000000..5529568 --- /dev/null +++ b/java/net/PasswordAuthentication.java
@@ -0,0 +1,81 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + + +/** + * The class PasswordAuthentication is a data holder that is used by + * Authenticator. It is simply a repository for a user name and a password. + * + * @see java.net.Authenticator + * @see java.net.Authenticator#getPasswordAuthentication() + * + * @author Bill Foote + * @since 1.2 + */ + +public final class PasswordAuthentication { + + private String userName; + private char[] password; + + /** + * Creates a new {@code PasswordAuthentication} object from the given + * user name and password. + * + * <p> Note that the given user password is cloned before it is stored in + * the new {@code PasswordAuthentication} object. + * + * @param userName the user name + * @param password the user's password + */ + public PasswordAuthentication(String userName, char[] password) { + this.userName = userName; + this.password = password.clone(); + } + + /** + * Returns the user name. + * + * @return the user name + */ + public String getUserName() { + return userName; + } + + /** + * Returns the user password. + * + * <p> Note that this method returns a reference to the password. It is + * the caller's responsibility to zero out the password information after + * it is no longer needed. + * + * @return the password + */ + public char[] getPassword() { + return password; + } +}
diff --git a/java/net/PlainDatagramSocketImpl.java b/java/net/PlainDatagramSocketImpl.java new file mode 100644 index 0000000..d4b1f2c --- /dev/null +++ b/java/net/PlainDatagramSocketImpl.java
@@ -0,0 +1,256 @@ +/* + * Copyright (c) 2007,2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.net; + +import android.system.ErrnoException; +import android.system.StructGroupReq; + +import java.io.IOException; +import libcore.io.IoBridge; +import libcore.io.Libcore; +import libcore.util.EmptyArray; + +import jdk.net.*; + +import static android.system.OsConstants.AF_INET6; +import static android.system.OsConstants.AF_UNSPEC; +import static android.system.OsConstants.IPPROTO_IP; +import static android.system.OsConstants.IP_MULTICAST_ALL; +import static android.system.OsConstants.MSG_PEEK; +import static android.system.OsConstants.POLLERR; +import static android.system.OsConstants.POLLIN; +import static android.system.OsConstants.SOCK_DGRAM; +import static libcore.io.IoBridge.JAVA_IP_MULTICAST_TTL; +import static libcore.io.IoBridge.JAVA_MCAST_JOIN_GROUP; +import static libcore.io.IoBridge.JAVA_MCAST_LEAVE_GROUP; +import static sun.net.ExtendedOptionsImpl.*; + +/* + * On Unix systems we simply delegate to native methods. + * + * @author Chris Hegarty + */ + +class PlainDatagramSocketImpl extends AbstractPlainDatagramSocketImpl +{ + // Android-removed: init method has been removed + // static { + // init(); + // } + + protected <T> void setOption(SocketOption<T> name, T value) throws IOException { + if (!name.equals(ExtendedSocketOptions.SO_FLOW_SLA)) { + super.setOption(name, value); + } else { + if (isClosed()) { + throw new SocketException("Socket closed"); + } + checkSetOptionPermission(name); + checkValueType(value, SocketFlow.class); + setFlowOption(getFileDescriptor(), (SocketFlow)value); + } + } + + protected <T> T getOption(SocketOption<T> name) throws IOException { + if (!name.equals(ExtendedSocketOptions.SO_FLOW_SLA)) { + return super.getOption(name); + } + if (isClosed()) { + throw new SocketException("Socket closed"); + } + checkGetOptionPermission(name); + SocketFlow flow = SocketFlow.create(); + getFlowOption(getFileDescriptor(), flow); + return (T)flow; + } + + protected void socketSetOption(int opt, Object val) throws SocketException { + try { + socketSetOption0(opt, val); + } catch (SocketException se) { + if (!connected) + throw se; + } + } + + // BEGIN Android-changed: Rewrote on top of Libcore.io. + protected synchronized void bind0(int lport, InetAddress laddr) throws SocketException { + if (isClosed()) { + throw new SocketException("Socket closed"); + } + + IoBridge.bind(fd, laddr, lport); + + if (lport == 0) { + // Now that we're a connected socket, let's extract the port number that the system + // chose for us and store it in the Socket object. + localPort = IoBridge.getLocalInetSocketAddress(fd).getPort(); + } else { + localPort = lport; + } + } + + protected void send(DatagramPacket p) throws IOException { + if (isClosed()) { + throw new SocketException("Socket closed"); + } + if (p.getData() == null || p.getAddress() == null) { + throw new NullPointerException("null buffer || null address"); + } + + int port = connected ? 0 : p.getPort(); + InetAddress address = connected ? null : p.getAddress(); + IoBridge.sendto(fd, p.getData(), p.getOffset(), p.getLength(), 0, address, port); + } + + protected synchronized int peek(InetAddress i) throws IOException { + DatagramPacket p = new DatagramPacket(EmptyArray.BYTE, 0); + doRecv(p, MSG_PEEK); + i.holder().address = p.getAddress().holder().address; + return p.getPort(); + } + + protected synchronized int peekData(DatagramPacket p) throws IOException { + doRecv(p, MSG_PEEK); + return p.getPort(); + } + + protected synchronized void receive0(DatagramPacket p) throws IOException { + doRecv(p, 0); + } + + private void doRecv(DatagramPacket p, int flags) throws IOException { + if (isClosed()) { + throw new SocketException("Socket closed"); + } + + if (timeout != 0) { + IoBridge.poll(fd, POLLIN | POLLERR, timeout); + } + + IoBridge.recvfrom(false, fd, p.getData(), p.getOffset(), p.bufLength, flags, p, + connected); + } + + protected void setTimeToLive(int ttl) throws IOException { + IoBridge.setSocketOption(fd, JAVA_IP_MULTICAST_TTL, ttl); + } + + protected int getTimeToLive() throws IOException { + return (Integer) IoBridge.getSocketOption(fd, JAVA_IP_MULTICAST_TTL); + } + + protected void setTTL(byte ttl) throws IOException { + setTimeToLive((int) ttl & 0xff); + } + + protected 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); + } + + protected void join(InetAddress inetaddr, NetworkInterface netIf) throws IOException { + if (isClosed()) { + throw new SocketException("Socket closed"); + } + + IoBridge.setSocketOption(fd, JAVA_MCAST_JOIN_GROUP, makeGroupReq(inetaddr, netIf)); + } + + protected void leave(InetAddress inetaddr, NetworkInterface netIf) + throws IOException { + if (isClosed()) { + throw new SocketException("Socket closed"); + } + + IoBridge.setSocketOption(fd, JAVA_MCAST_LEAVE_GROUP, makeGroupReq(inetaddr, netIf)); + } + + protected void datagramSocketCreate() throws SocketException { + fd = IoBridge.socket(AF_INET6, SOCK_DGRAM, 0); + IoBridge.setSocketOption(fd, SO_BROADCAST, true); + + try { + Libcore.os.setsockoptInt(fd, IPPROTO_IP, IP_MULTICAST_ALL, 0); + } catch (ErrnoException errnoException) { + throw errnoException.rethrowAsSocketException(); + } + } + + protected void datagramSocketClose() { + try { + IoBridge.closeAndSignalBlockedThreads(fd); + } catch (IOException ignored) { } + } + + protected void socketSetOption0(int opt, Object val) throws SocketException { + if (isClosed()) { + throw new SocketException("Socket closed"); + } + + IoBridge.setSocketOption(fd, opt, val); + } + + protected Object socketGetOption(int opt) throws SocketException { + if (isClosed()) { + throw new SocketException("Socket closed"); + } + + return IoBridge.getSocketOption(fd, opt); + } + + protected void connect0(InetAddress address, int port) throws SocketException { + if (isClosed()) { + throw new SocketException("Socket closed"); + } + + IoBridge.connect(fd, address, port); + } + + protected void disconnect0(int family) { + if (isClosed()) { + return; + } + + InetAddress inetAddressUnspec = new InetAddress(); + inetAddressUnspec.holder().family = AF_UNSPEC; + + try { + IoBridge.connect(fd, inetAddressUnspec, 0); + } catch (SocketException ignored) { } + } + // END Android-changed: Rewrote on top of Libcore.io. + + // Android-removed: JNI has been removed + // /** + // * Perform class load-time initializations. + // */ + // private native static void init(); +}
diff --git a/java/net/PlainSocketImpl.java b/java/net/PlainSocketImpl.java new file mode 100644 index 0000000..89ee53e --- /dev/null +++ b/java/net/PlainSocketImpl.java
@@ -0,0 +1,314 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 2007, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.net; + +import android.system.ErrnoException; + +import java.io.IOException; +import java.io.FileDescriptor; +import java.util.Set; +import java.util.HashSet; +import java.util.Collections; +import libcore.io.AsynchronousCloseMonitor; +import libcore.io.IoBridge; +import libcore.io.IoUtils; +import libcore.io.Libcore; + +import jdk.net.*; + +import static android.system.OsConstants.AF_INET6; +import static android.system.OsConstants.AF_UNIX; +import static android.system.OsConstants.EAGAIN; +import static android.system.OsConstants.EBADF; +import static android.system.OsConstants.EINVAL; +import static android.system.OsConstants.MSG_OOB; +import static android.system.OsConstants.POLLERR; +import static android.system.OsConstants.POLLIN; +import static android.system.OsConstants.SOCK_DGRAM; +import static android.system.OsConstants.SOCK_STREAM; +import static android.system.OsConstants.SHUT_RDWR; +import static sun.net.ExtendedOptionsImpl.*; + +// Android-changed: Rewritten to use android.system POSIX calls and assume AF_INET6. +/* + * On Unix systems we simply delegate to native methods. + * + * @author Chris Hegarty + */ + +class PlainSocketImpl extends AbstractPlainSocketImpl +{ + // Android-removed: Android doesn't need to call native initProto. + /* + static { + initProto(); + } + */ + + /** + * Constructs an empty instance. + */ + PlainSocketImpl() { + // Android-changed: Let PlainSocketImpl construct its own FileDescriptor. + this.fd = new FileDescriptor(); + } + + /** + * Constructs an instance with the given file descriptor. + */ + // Android-removed: Let PlainSocketImpl construct its own FileDescriptor. + /* + PlainSocketImpl(FileDescriptor fd) { + this.fd = fd; + } + */ + + protected <T> void setOption(SocketOption<T> name, T value) throws IOException { + if (!name.equals(ExtendedSocketOptions.SO_FLOW_SLA)) { + super.setOption(name, value); + } else { + if (isClosedOrPending()) { + throw new SocketException("Socket closed"); + } + checkSetOptionPermission(name); + checkValueType(value, SocketFlow.class); + setFlowOption(getFileDescriptor(), (SocketFlow)value); + } + } + + protected <T> T getOption(SocketOption<T> name) throws IOException { + if (!name.equals(ExtendedSocketOptions.SO_FLOW_SLA)) { + return super.getOption(name); + } + if (isClosedOrPending()) { + throw new SocketException("Socket closed"); + } + checkGetOptionPermission(name); + SocketFlow flow = SocketFlow.create(); + getFlowOption(getFileDescriptor(), flow); + return (T)flow; + } + + // BEGIN Android-changed: Rewrote on top of Libcore.io. + protected void socketSetOption(int opt, Object val) throws SocketException { + try { + socketSetOption0(opt, val); + } catch (SocketException se) { + if (socket == null || !socket.isConnected()) + throw se; + } + } + + void socketCreate(boolean isStream) throws IOException { + // The fd object must not change after calling bind, because we rely on this undocumented + // behaviour. See libcore.java.net.SocketTest#testFileDescriptorStaysSame. + fd.setInt$(IoBridge.socket(AF_INET6, isStream ? SOCK_STREAM : SOCK_DGRAM, 0).getInt$()); + IoUtils.setFdOwner(fd, this); + + if (serverSocket != null) { + IoUtils.setBlocking(fd, false); + IoBridge.setSocketOption(fd, SO_REUSEADDR, true); + } + } + + void socketConnect(InetAddress address, int port, int timeout) throws IOException { + if (fd == null || !fd.valid()) { + throw new SocketException("Socket closed"); + } + + IoBridge.connect(fd, address, port, timeout); + + this.address = address; + this.port = port; + + if (localport == 0) { + // If socket is pending close, fd becomes an AF_UNIX socket and calling + // getLocalInetSocketAddress will fail. + // http://b/34645743 + if (!isClosedOrPending()) { + localport = IoBridge.getLocalInetSocketAddress(fd).getPort(); + } + } + } + + void socketBind(InetAddress address, int port) throws IOException { + if (fd == null || !fd.valid()) { + throw new SocketException("Socket closed"); + } + + IoBridge.bind(fd, address, port); + + this.address = address; + if (port == 0) { + // Now that we're a connected socket, let's extract the port number that the system + // chose for us and store it in the Socket object. + localport = IoBridge.getLocalInetSocketAddress(fd).getPort(); + } else { + localport = port; + } + } + + void socketListen(int count) throws IOException { + if (fd == null || !fd.valid()) { + throw new SocketException("Socket closed"); + } + + try { + Libcore.os.listen(fd, count); + } catch (ErrnoException errnoException) { + throw errnoException.rethrowAsSocketException(); + } + } + + void socketAccept(SocketImpl s) throws IOException { + if (fd == null || !fd.valid()) { + throw new SocketException("Socket closed"); + } + + // poll() with a timeout of 0 means "poll for zero millis", but a Socket timeout == 0 means + // "wait forever". When timeout == 0 we pass -1 to poll. + if (timeout <= 0) { + IoBridge.poll(fd, POLLIN | POLLERR, -1); + } else { + IoBridge.poll(fd, POLLIN | POLLERR, timeout); + } + + InetSocketAddress peerAddress = new InetSocketAddress(); + try { + FileDescriptor newfd = Libcore.os.accept(fd, peerAddress); + + s.fd.setInt$(newfd.getInt$()); + IoUtils.setFdOwner(s.fd, s); + s.address = peerAddress.getAddress(); + s.port = peerAddress.getPort(); + } catch (ErrnoException errnoException) { + if (errnoException.errno == EAGAIN) { + SocketTimeoutException e = new SocketTimeoutException(); + e.initCause(errnoException); + throw e; + } else if (errnoException.errno == EINVAL || errnoException.errno == EBADF) { + throw new SocketException("Socket closed"); + } + errnoException.rethrowAsSocketException(); + } + + s.localport = IoBridge.getLocalInetSocketAddress(s.fd).getPort(); + } + + int socketAvailable() throws IOException { + return IoBridge.available(fd); + } + + void socketClose0(boolean useDeferredClose) throws IOException { + if (fd == null || !fd.valid()) { + throw new SocketException("socket already closed"); + } + + FileDescriptor markerFD = null; + if (useDeferredClose) { + markerFD = getMarkerFD(); + } + + if (useDeferredClose && markerFD != null) { + try { + Libcore.os.dup2(markerFD, fd.getInt$()); + Libcore.os.close(markerFD); + + // This effectively closes the socket, needs to signal threads that blocks on this + // file descriptor. + AsynchronousCloseMonitor.signalBlockedThreads(fd); + } catch (ErrnoException errnoException) { + // close should not throw + } + } else { + // If requested or a markerFD cannot be created, a non-deferred close is performed + // instead. + IoBridge.closeAndSignalBlockedThreads(fd); + } + } + + /* + * Create the marker file descriptor by establishing a loopback connection which we shutdown but + * do not close the fd. The result is an fd that can be used for read/write. + * + * The purpose is to keep hold of the raw fd handle until we are sure it is not used in any + * thread. Otherwise if we close the file descriptor directly, the system might reuse the raw fd + * number and threads holding old fd value might behave incorrectly. + */ + private FileDescriptor getMarkerFD() throws SocketException { + FileDescriptor fd1 = new FileDescriptor(); + FileDescriptor fd2 = new FileDescriptor(); + try { + Libcore.os.socketpair(AF_UNIX, SOCK_STREAM, 0, fd1, fd2); + + // Shutdown fd1, any reads to this fd will get EOF; any writes will get an error. + Libcore.os.shutdown(fd1, SHUT_RDWR); + Libcore.os.close(fd2); + } catch (ErrnoException errnoException) { + // We might have reached the maximum file descriptor number and socketpair(2) would + // fail. In this case, return null and let caller to fall back to an alternative method + // that does not allocate more file descriptors. + return null; + } + return fd1; + } + + void socketShutdown(int howto) throws IOException { + try { + Libcore.os.shutdown(fd, howto); + } catch (ErrnoException errnoException) { + throw errnoException.rethrowAsIOException(); + } + } + + void socketSetOption0(int cmd, Object value) throws SocketException { + // OpenJDK does not set SO_TIMEOUT on Linux. + if (cmd == SO_TIMEOUT) { + return; + } + + IoBridge.setSocketOption(fd, cmd, value); + } + + Object socketGetOption(int opt) throws SocketException { + return IoBridge.getSocketOption(fd, opt); + } + + void socketSendUrgentData(int data) throws IOException { + if (fd == null || !fd.valid()) { + throw new SocketException("Socket closed"); + } + + try { + byte[] buffer = new byte[] { (byte) data }; + Libcore.os.sendto(fd, buffer, 0, 1, MSG_OOB, null, 0); + } catch (ErrnoException errnoException) { + throw errnoException.rethrowAsSocketException(); + } + } + // END Android-changed: Rewrote on top of Libcore.io. + +}
diff --git a/java/net/PortUnreachableException.java b/java/net/PortUnreachableException.java new file mode 100644 index 0000000..3e7558b --- /dev/null +++ b/java/net/PortUnreachableException.java
@@ -0,0 +1,59 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +/** + * Signals that an ICMP Port Unreachable message has been + * received on a connected datagram. + * + * @since 1.4 + */ + +public class PortUnreachableException extends SocketException { + private static final long serialVersionUID = 8462541992376507323L; + + /** + * Constructs a new {@code PortUnreachableException} with a + * detail message. + * @param msg the detail message + */ + public PortUnreachableException(String msg) { + super(msg); + } + + /** + * Construct a new {@code PortUnreachableException} with no + * detailed message. + */ + public PortUnreachableException() {} + + // Android-added: PortUnreachableException ctor used by IoBridge. + /** @hide */ + public PortUnreachableException(String msg, Throwable cause) { + super(msg, cause); + } +}
diff --git a/java/net/ProtocolException.java b/java/net/ProtocolException.java new file mode 100644 index 0000000..5944282 --- /dev/null +++ b/java/net/ProtocolException.java
@@ -0,0 +1,57 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.IOException; + +/** + * Thrown to indicate that there is an error in the underlying + * protocol, such as a TCP error. + * + * @author Chris Warth + * @since JDK1.0 + */ +public +class ProtocolException extends IOException { + private static final long serialVersionUID = -6098449442062388080L; + + /** + * Constructs a new {@code ProtocolException} with the + * specified detail message. + * + * @param host the detail message. + */ + public ProtocolException(String host) { + super(host); + } + + /** + * Constructs a new {@code ProtocolException} with no detail message. + */ + public ProtocolException() { + } +}
diff --git a/java/net/ProtocolFamily.java b/java/net/ProtocolFamily.java new file mode 100644 index 0000000..5d02326 --- /dev/null +++ b/java/net/ProtocolFamily.java
@@ -0,0 +1,41 @@ +/* + * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +/** + * Represents a family of communication protocols. + * + * @since 1.7 + */ + +public interface ProtocolFamily { + /** + * Returns the name of the protocol family. + * + * @return the name of the protocol family + */ + String name(); +}
diff --git a/java/net/Proxy.java b/java/net/Proxy.java new file mode 100644 index 0000000..8ba020e --- /dev/null +++ b/java/net/Proxy.java
@@ -0,0 +1,171 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +/** + * This class represents a proxy setting, typically a type (http, socks) and + * a socket address. + * A {@code Proxy} is an immutable object. + * + * @see java.net.ProxySelector + * @author Yingxian Wang + * @author Jean-Christophe Collet + * @since 1.5 + */ +public class Proxy { + + /** + * Represents the proxy type. + * + * @since 1.5 + */ + public enum Type { + /** + * Represents a direct connection, or the absence of a proxy. + */ + DIRECT, + /** + * Represents proxy for high level protocols such as HTTP or FTP. + */ + HTTP, + /** + * Represents a SOCKS (V4 or V5) proxy. + */ + SOCKS + }; + + private Type type; + private SocketAddress sa; + + /** + * A proxy setting that represents a {@code DIRECT} connection, + * basically telling the protocol handler not to use any proxying. + * Used, for instance, to create sockets bypassing any other global + * proxy settings (like SOCKS): + * <P> + * {@code Socket s = new Socket(Proxy.NO_PROXY);} + * + */ + public final static Proxy NO_PROXY = new Proxy(); + + // Creates the proxy that represents a {@code DIRECT} connection. + private Proxy() { + type = Type.DIRECT; + sa = null; + } + + /** + * Creates an entry representing a PROXY connection. + * Certain combinations are illegal. For instance, for types Http, and + * Socks, a SocketAddress <b>must</b> be provided. + * <P> + * Use the {@code Proxy.NO_PROXY} constant + * for representing a direct connection. + * + * @param type the {@code Type} of the proxy + * @param sa the {@code SocketAddress} for that proxy + * @throws IllegalArgumentException when the type and the address are + * incompatible + */ + public Proxy(Type type, SocketAddress sa) { + if ((type == Type.DIRECT) || !(sa instanceof InetSocketAddress)) + throw new IllegalArgumentException("type " + type + " is not compatible with address " + sa); + this.type = type; + this.sa = sa; + } + + /** + * Returns the proxy type. + * + * @return a Type representing the proxy type + */ + public Type type() { + return type; + } + + /** + * Returns the socket address of the proxy, or + * {@code null} if its a direct connection. + * + * @return a {@code SocketAddress} representing the socket end + * point of the proxy + */ + public SocketAddress address() { + return sa; + } + + /** + * Constructs a string representation of this Proxy. + * This String is constructed by calling toString() on its type + * and concatenating " @ " and the toString() result from its address + * if its type is not {@code DIRECT}. + * + * @return a string representation of this object. + */ + public String toString() { + if (type() == Type.DIRECT) + return "DIRECT"; + return type() + " @ " + address(); + } + + /** + * Compares this object against the specified object. + * The result is {@code true} if and only if the argument is + * not {@code null} and it represents the same proxy as + * this object. + * <p> + * Two instances of {@code Proxy} represent the same + * address if both the SocketAddresses and type are equal. + * + * @param obj the object to compare against. + * @return {@code true} if the objects are the same; + * {@code false} otherwise. + * @see java.net.InetSocketAddress#equals(java.lang.Object) + */ + public final boolean equals(Object obj) { + if (obj == null || !(obj instanceof Proxy)) + return false; + Proxy p = (Proxy) obj; + if (p.type() == type()) { + if (address() == null) { + return (p.address() == null); + } else + return address().equals(p.address()); + } + return false; + } + + /** + * Returns a hashcode for this Proxy. + * + * @return a hash code value for this Proxy. + */ + public final int hashCode() { + if (address() == null) + return type().hashCode(); + return type().hashCode() + address().hashCode(); + } +}
diff --git a/java/net/ProxySelector.java b/java/net/ProxySelector.java new file mode 100644 index 0000000..d6bb536 --- /dev/null +++ b/java/net/ProxySelector.java
@@ -0,0 +1,165 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.IOException; +import java.util.List; +import sun.security.util.SecurityConstants; + +/** + * Selects the proxy server to use, if any, when connecting to the + * network resource referenced by a URL. A proxy selector is a + * concrete sub-class of this class and is registered by invoking the + * {@link java.net.ProxySelector#setDefault setDefault} method. The + * currently registered proxy selector can be retrieved by calling + * {@link java.net.ProxySelector#getDefault getDefault} method. + * + * <p> When a proxy selector is registered, for instance, a subclass + * of URLConnection class should call the {@link #select select} + * method for each URL request so that the proxy selector can decide + * if a direct, or proxied connection should be used. The {@link + * #select select} method returns an iterator over a collection with + * the preferred connection approach. + * + * <p> If a connection cannot be established to a proxy (PROXY or + * SOCKS) servers then the caller should call the proxy selector's + * {@link #connectFailed connectFailed} method to notify the proxy + * selector that the proxy server is unavailable. </p> + * + * <P>The default proxy selector does enforce a + * <a href="doc-files/net-properties.html#Proxies">set of System Properties</a> + * related to proxy settings.</P> + * + * @author Yingxian Wang + * @author Jean-Christophe Collet + * @since 1.5 + */ +public abstract class ProxySelector { + /** + * The system wide proxy selector that selects the proxy server to + * use, if any, when connecting to a remote object referenced by + * an URL. + * + * @see #setDefault(ProxySelector) + */ + private static ProxySelector theProxySelector; + + static { + try { + Class<?> c = Class.forName("sun.net.spi.DefaultProxySelector"); + if (c != null && ProxySelector.class.isAssignableFrom(c)) { + theProxySelector = (ProxySelector) c.newInstance(); + } + } catch (Exception e) { + theProxySelector = null; + } + } + + /** + * Gets the system-wide proxy selector. + * + * @throws SecurityException + * If a security manager has been installed and it denies + * {@link NetPermission}{@code ("getProxySelector")} + * @see #setDefault(ProxySelector) + * @return the system-wide {@code ProxySelector} + * @since 1.5 + */ + public static ProxySelector getDefault() { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(SecurityConstants.GET_PROXYSELECTOR_PERMISSION); + } + return theProxySelector; + } + + /** + * Sets (or unsets) the system-wide proxy selector. + * + * Note: non-standard protocol handlers may ignore this setting. + * + * @param ps The HTTP proxy selector, or + * {@code null} to unset the proxy selector. + * + * @throws SecurityException + * If a security manager has been installed and it denies + * {@link NetPermission}{@code ("setProxySelector")} + * + * @see #getDefault() + * @since 1.5 + */ + public static void setDefault(ProxySelector ps) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(SecurityConstants.SET_PROXYSELECTOR_PERMISSION); + } + theProxySelector = ps; + } + + /** + * Selects all the applicable proxies based on the protocol to + * access the resource with and a destination address to access + * the resource at. + * The format of the URI is defined as follow: + * <UL> + * <LI>http URI for http connections</LI> + * <LI>https URI for https connections + * <LI>{@code socket://host:port}<br> + * for tcp client sockets connections</LI> + * </UL> + * + * @param uri + * The URI that a connection is required to + * + * @return a List of Proxies. Each element in the + * the List is of type + * {@link java.net.Proxy Proxy}; + * when no proxy is available, the list will + * contain one element of type + * {@link java.net.Proxy Proxy} + * that represents a direct connection. + * @throws IllegalArgumentException if the argument is null + */ + public abstract List<Proxy> select(URI uri); + + /** + * Called to indicate that a connection could not be established + * to a proxy/socks server. An implementation of this method can + * temporarily remove the proxies or reorder the sequence of + * proxies returned by {@link #select(URI)}, using the address + * and the IOException caught when trying to connect. + * + * @param uri + * The URI that the proxy at sa failed to serve. + * @param sa + * The socket address of the proxy/SOCKS server + * + * @param ioe + * The I/O exception thrown when the connect failed. + * @throws IllegalArgumentException if either argument is null + */ + public abstract void connectFailed(URI uri, SocketAddress sa, IOException ioe); +}
diff --git a/java/net/ResponseCache.java b/java/net/ResponseCache.java new file mode 100644 index 0000000..2dfaf4a --- /dev/null +++ b/java/net/ResponseCache.java
@@ -0,0 +1,163 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.IOException; +import java.util.Map; +import java.util.List; +import sun.security.util.SecurityConstants; + +/** + * Represents implementations of URLConnection caches. An instance of + * such a class can be registered with the system by doing + * ResponseCache.setDefault(ResponseCache), and the system will call + * this object in order to: + * + * <ul><li>store resource data which has been retrieved from an + * external source into the cache</li> + * <li>try to fetch a requested resource that may have been + * stored in the cache</li> + * </ul> + * + * The ResponseCache implementation decides which resources + * should be cached, and for how long they should be cached. If a + * request resource cannot be retrieved from the cache, then the + * protocol handlers will fetch the resource from its original + * location. + * + * The settings for URLConnection#useCaches controls whether the + * protocol is allowed to use a cached response. + * + * For more information on HTTP caching, see <a + * href="http://www.ietf.org/rfc/rfc2616.txt"><i>RFC 2616: Hypertext + * Transfer Protocol -- HTTP/1.1</i></a> + * + * @author Yingxian Wang + * @since 1.5 + */ +public abstract class ResponseCache { + + /** + * The system wide cache that provides access to a url + * caching mechanism. + * + * @see #setDefault(ResponseCache) + * @see #getDefault() + */ + private static ResponseCache theResponseCache; + + /** + * Gets the system-wide response cache. + * + * @throws SecurityException + * If a security manager has been installed and it denies + * {@link NetPermission}{@code ("getResponseCache")} + * + * @see #setDefault(ResponseCache) + * @return the system-wide {@code ResponseCache} + * @since 1.5 + */ + public synchronized static ResponseCache getDefault() { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(SecurityConstants.GET_RESPONSECACHE_PERMISSION); + } + return theResponseCache; + } + + /** + * Sets (or unsets) the system-wide cache. + * + * Note: non-standard procotol handlers may ignore this setting. + * + * @param responseCache The response cache, or + * {@code null} to unset the cache. + * + * @throws SecurityException + * If a security manager has been installed and it denies + * {@link NetPermission}{@code ("setResponseCache")} + * + * @see #getDefault() + * @since 1.5 + */ + public synchronized static void setDefault(ResponseCache responseCache) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(SecurityConstants.SET_RESPONSECACHE_PERMISSION); + } + theResponseCache = responseCache; + } + + /** + * Retrieve the cached response based on the requesting uri, + * request method and request headers. Typically this method is + * called by the protocol handler before it sends out the request + * to get the network resource. If a cached response is returned, + * that resource is used instead. + * + * @param uri a {@code URI} used to reference the requested + * network resource + * @param rqstMethod a {@code String} representing the request + * method + * @param rqstHeaders - a Map from request header + * field names to lists of field values representing + * the current request headers + * @return a {@code CacheResponse} instance if available + * from cache, or null otherwise + * @throws IOException if an I/O error occurs + * @throws IllegalArgumentException if any one of the arguments is null + * + * @see java.net.URLConnection#setUseCaches(boolean) + * @see java.net.URLConnection#getUseCaches() + * @see java.net.URLConnection#setDefaultUseCaches(boolean) + * @see java.net.URLConnection#getDefaultUseCaches() + */ + public abstract CacheResponse + get(URI uri, String rqstMethod, Map<String, List<String>> rqstHeaders) + throws IOException; + + /** + * The protocol handler calls this method after a resource has + * been retrieved, and the ResponseCache must decide whether or + * not to store the resource in its cache. If the resource is to + * be cached, then put() must return a CacheRequest object which + * contains an OutputStream that the protocol handler will + * use to write the resource into the cache. If the resource is + * not to be cached, then put must return null. + * + * @param uri a {@code URI} used to reference the requested + * network resource + * @param conn - a URLConnection instance that is used to fetch + * the response to be cached + * @return a {@code CacheRequest} for recording the + * response to be cached. Null return indicates that + * the caller does not intend to cache the response. + * @throws IOException if an I/O error occurs + * @throws IllegalArgumentException if any one of the arguments is + * null + */ + public abstract CacheRequest put(URI uri, URLConnection conn) throws IOException; +}
diff --git a/java/net/SecureCacheResponse.java b/java/net/SecureCacheResponse.java new file mode 100644 index 0000000..64fd414 --- /dev/null +++ b/java/net/SecureCacheResponse.java
@@ -0,0 +1,108 @@ +/* + * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.security.cert.Certificate; +import javax.net.ssl.SSLPeerUnverifiedException; +import java.security.Principal; +import java.util.List; + +/** + * Represents a cache response originally retrieved through secure + * means, such as TLS. + * + * @since 1.5 + */ +public abstract class SecureCacheResponse extends CacheResponse { + /** + * Returns the cipher suite in use on the original connection that + * retrieved the network resource. + * + * @return a string representing the cipher suite + */ + public abstract String getCipherSuite(); + + /** + * Returns the certificate chain that were sent to the server during + * handshaking of the original connection that retrieved the + * network resource. Note: This method is useful only + * when using certificate-based cipher suites. + * + * @return an immutable List of Certificate representing the + * certificate chain that was sent to the server. If no + * certificate chain was sent, null will be returned. + * @see #getLocalPrincipal() + */ + public abstract List<Certificate> getLocalCertificateChain(); + + /** + * Returns the server's certificate chain, which was established as + * part of defining the session in the original connection that + * retrieved the network resource, from cache. Note: This method + * can be used only when using certificate-based cipher suites; + * using it with non-certificate-based cipher suites, such as + * Kerberos, will throw an SSLPeerUnverifiedException. + * + * @return an immutable List of Certificate representing the server's + * certificate chain. + * @throws SSLPeerUnverifiedException if the peer is not verified. + * @see #getPeerPrincipal() + */ + public abstract List<Certificate> getServerCertificateChain() + throws SSLPeerUnverifiedException; + + /** + * Returns the server's principal which was established as part of + * defining the session during the original connection that + * retrieved the network resource. + * + * @return the server's principal. Returns an X500Principal of the + * end-entity certiticate for X509-based cipher suites, and + * KerberosPrincipal for Kerberos cipher suites. + * + * @throws SSLPeerUnverifiedException if the peer was not verified. + * + * @see #getServerCertificateChain() + * @see #getLocalPrincipal() + */ + public abstract Principal getPeerPrincipal() + throws SSLPeerUnverifiedException; + + /** + * Returns the principal that was sent to the server during + * handshaking in the original connection that retrieved the + * network resource. + * + * @return the principal sent to the server. Returns an X500Principal + * of the end-entity certificate for X509-based cipher suites, and + * KerberosPrincipal for Kerberos cipher suites. If no principal was + * sent, then null is returned. + * + * @see #getLocalCertificateChain() + * @see #getPeerPrincipal() + */ + public abstract Principal getLocalPrincipal(); +}
diff --git a/java/net/ServerSocket.annotated.java b/java/net/ServerSocket.annotated.java new file mode 100644 index 0000000..1fcd05f --- /dev/null +++ b/java/net/ServerSocket.annotated.java
@@ -0,0 +1,86 @@ +/* + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package java.net; + +import java.nio.channels.ServerSocketChannel; +import java.io.IOException; + +@SuppressWarnings({"unchecked", "deprecation", "all"}) +public class ServerSocket implements java.io.Closeable { + +public ServerSocket() throws java.io.IOException { throw new RuntimeException("Stub!"); } + +public ServerSocket(int port) throws java.io.IOException { throw new RuntimeException("Stub!"); } + +public ServerSocket(int port, int backlog) throws java.io.IOException { throw new RuntimeException("Stub!"); } + +public ServerSocket(int port, int backlog, java.net.InetAddress bindAddr) throws java.io.IOException { throw new RuntimeException("Stub!"); } + +public void bind(java.net.SocketAddress endpoint) throws java.io.IOException { throw new RuntimeException("Stub!"); } + +public void bind(java.net.SocketAddress endpoint, int backlog) throws java.io.IOException { throw new RuntimeException("Stub!"); } + +public java.net.InetAddress getInetAddress() { throw new RuntimeException("Stub!"); } + +public int getLocalPort() { throw new RuntimeException("Stub!"); } + +public java.net.SocketAddress getLocalSocketAddress() { throw new RuntimeException("Stub!"); } + +public java.net.Socket accept() throws java.io.IOException { throw new RuntimeException("Stub!"); } + [email protected] +public SocketImpl getImpl() throws SocketException { throw new RuntimeException("Stub!"); } + +protected final void implAccept(java.net.Socket s) throws java.io.IOException { throw new RuntimeException("Stub!"); } + +public void close() throws java.io.IOException { throw new RuntimeException("Stub!"); } + +public java.nio.channels.ServerSocketChannel getChannel() { throw new RuntimeException("Stub!"); } + +public boolean isBound() { throw new RuntimeException("Stub!"); } + +public boolean isClosed() { throw new RuntimeException("Stub!"); } + +public synchronized void setSoTimeout(int timeout) throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public synchronized int getSoTimeout() throws java.io.IOException { throw new RuntimeException("Stub!"); } + +public void setReuseAddress(boolean on) throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public boolean getReuseAddress() throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public java.lang.String toString() { throw new RuntimeException("Stub!"); } + +public static synchronized void setSocketFactory(java.net.SocketImplFactory fac) throws java.io.IOException { throw new RuntimeException("Stub!"); } + +public synchronized void setReceiveBufferSize(int size) throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public synchronized int getReceiveBufferSize() throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) { throw new RuntimeException("Stub!"); } +} +
diff --git a/java/net/ServerSocket.java b/java/net/ServerSocket.java new file mode 100644 index 0000000..444a84d --- /dev/null +++ b/java/net/ServerSocket.java
@@ -0,0 +1,932 @@ +/* + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.nio.channels.ServerSocketChannel; +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; + +/** + * This class implements server sockets. A server socket waits for + * requests to come in over the network. It performs some operation + * based on that request, and then possibly returns a result to the requester. + * <p> + * The actual work of the server socket is performed by an instance + * of the {@code SocketImpl} class. An application can + * change the socket factory that creates the socket + * implementation to configure itself to create sockets + * appropriate to the local firewall. + * + * @author unascribed + * @see java.net.SocketImpl + * @see java.net.ServerSocket#setSocketFactory(java.net.SocketImplFactory) + * @see java.nio.channels.ServerSocketChannel + * @since JDK1.0 + */ +public +class ServerSocket implements java.io.Closeable { + /** + * Various states of this socket. + */ + private boolean created = false; + private boolean bound = false; + private boolean closed = false; + private Object closeLock = new Object(); + + /** + * The implementation of this Socket. + */ + private SocketImpl impl; + + /** + * Are we using an older SocketImpl? + */ + private boolean oldImpl = false; + + /** + * Package-private constructor to create a ServerSocket associated with + * the given SocketImpl. + */ + ServerSocket(SocketImpl impl) { + this.impl = impl; + impl.setServerSocket(this); + } + + /** + * Creates an unbound server socket. + * + * @exception IOException IO error when opening the socket. + * @revised 1.4 + */ + public ServerSocket() throws IOException { + setImpl(); + } + + /** + * Creates a server socket, bound to the specified port. A port number + * of {@code 0} means that the port number is automatically + * allocated, typically from an ephemeral port range. This port + * number can then be retrieved by calling {@link #getLocalPort getLocalPort}. + * <p> + * The maximum queue length for incoming connection indications (a + * request to connect) is set to {@code 50}. If a connection + * indication arrives when the queue is full, the connection is refused. + * <p> + * If the application has specified a server socket factory, that + * factory's {@code createSocketImpl} method is called to create + * the actual socket implementation. Otherwise a "plain" socket is created. + * <p> + * If there is a security manager, + * its {@code checkListen} method is called + * with the {@code port} argument + * as its argument to ensure the operation is allowed. + * This could result in a SecurityException. + * + * + * @param port the port number, or {@code 0} to use a port + * number that is automatically allocated. + * + * @exception IOException if an I/O error occurs when opening the socket. + * @exception SecurityException + * if a security manager exists and its {@code checkListen} + * method doesn't allow the operation. + * @exception IllegalArgumentException if the port parameter is outside + * the specified range of valid port values, which is between + * 0 and 65535, inclusive. + * + * @see java.net.SocketImpl + * @see java.net.SocketImplFactory#createSocketImpl() + * @see java.net.ServerSocket#setSocketFactory(java.net.SocketImplFactory) + * @see SecurityManager#checkListen + */ + public ServerSocket(int port) throws IOException { + this(port, 50, null); + } + + /** + * Creates a server socket and binds it to the specified local port + * number, with the specified backlog. + * A port number of {@code 0} means that the port number is + * automatically allocated, typically from an ephemeral port range. + * This port number can then be retrieved by calling + * {@link #getLocalPort getLocalPort}. + * <p> + * The maximum queue length for incoming connection indications (a + * request to connect) is set to the {@code backlog} parameter. If + * a connection indication arrives when the queue is full, the + * connection is refused. + * <p> + * If the application has specified a server socket factory, that + * factory's {@code createSocketImpl} method is called to create + * the actual socket implementation. Otherwise a "plain" socket is created. + * <p> + * If there is a security manager, + * its {@code checkListen} method is called + * with the {@code port} argument + * as its argument to ensure the operation is allowed. + * This could result in a SecurityException. + * + * The {@code backlog} argument is the requested maximum number of + * pending connections on the socket. Its exact semantics are implementation + * specific. In particular, an implementation may impose a maximum length + * or may choose to ignore the parameter altogther. The value provided + * should be greater than {@code 0}. If it is less than or equal to + * {@code 0}, then an implementation specific default will be used. + * <P> + * + * @param port the port number, or {@code 0} to use a port + * number that is automatically allocated. + * @param backlog requested maximum length of the queue of incoming + * connections. + * + * @exception IOException if an I/O error occurs when opening the socket. + * @exception SecurityException + * if a security manager exists and its {@code checkListen} + * method doesn't allow the operation. + * @exception IllegalArgumentException if the port parameter is outside + * the specified range of valid port values, which is between + * 0 and 65535, inclusive. + * + * @see java.net.SocketImpl + * @see java.net.SocketImplFactory#createSocketImpl() + * @see java.net.ServerSocket#setSocketFactory(java.net.SocketImplFactory) + * @see SecurityManager#checkListen + */ + public ServerSocket(int port, int backlog) throws IOException { + this(port, backlog, null); + } + + /** + * Create a server with the specified port, listen backlog, and + * local IP address to bind to. The <i>bindAddr</i> argument + * can be used on a multi-homed host for a ServerSocket that + * will only accept connect requests to one of its addresses. + * If <i>bindAddr</i> is null, it will default accepting + * connections on any/all local addresses. + * The port must be between 0 and 65535, inclusive. + * A port number of {@code 0} means that the port number is + * automatically allocated, typically from an ephemeral port range. + * This port number can then be retrieved by calling + * {@link #getLocalPort getLocalPort}. + * + * <P>If there is a security manager, this method + * calls its {@code checkListen} method + * with the {@code port} argument + * as its argument to ensure the operation is allowed. + * This could result in a SecurityException. + * + * The {@code backlog} argument is the requested maximum number of + * pending connections on the socket. Its exact semantics are implementation + * specific. In particular, an implementation may impose a maximum length + * or may choose to ignore the parameter altogther. The value provided + * should be greater than {@code 0}. If it is less than or equal to + * {@code 0}, then an implementation specific default will be used. + * <P> + * @param port the port number, or {@code 0} to use a port + * number that is automatically allocated. + * @param backlog requested maximum length of the queue of incoming + * connections. + * @param bindAddr the local InetAddress the server will bind to + * + * @throws SecurityException if a security manager exists and + * its {@code checkListen} method doesn't allow the operation. + * + * @throws IOException if an I/O error occurs when opening the socket. + * @exception IllegalArgumentException if the port parameter is outside + * the specified range of valid port values, which is between + * 0 and 65535, inclusive. + * + * @see SocketOptions + * @see SocketImpl + * @see SecurityManager#checkListen + * @since JDK1.1 + */ + public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException { + setImpl(); + if (port < 0 || port > 0xFFFF) + throw new IllegalArgumentException( + "Port value out of range: " + port); + if (backlog < 1) + backlog = 50; + try { + bind(new InetSocketAddress(bindAddr, port), backlog); + } catch(SecurityException e) { + close(); + throw e; + } catch(IOException e) { + close(); + throw e; + } + } + + // Android-changed: Made getImpl() public and @hide, for internal use. + /** + * Get the {@code SocketImpl} attached to this socket, creating + * it if necessary. + * + * @return the {@code SocketImpl} attached to that ServerSocket. + * @throws SocketException if creation fails. + * @since 1.4 + * @hide + */ + public SocketImpl getImpl() throws SocketException { + if (!created) + createImpl(); + return impl; + } + + private void checkOldImpl() { + if (impl == null) + return; + // SocketImpl.connect() is a protected method, therefore we need to use + // getDeclaredMethod, therefore we need permission to access the member + try { + AccessController.doPrivileged( + new PrivilegedExceptionAction<Void>() { + public Void run() throws NoSuchMethodException { + impl.getClass().getDeclaredMethod("connect", + SocketAddress.class, + int.class); + return null; + } + }); + } catch (java.security.PrivilegedActionException e) { + oldImpl = true; + } + } + + private void setImpl() { + if (factory != null) { + impl = factory.createSocketImpl(); + checkOldImpl(); + } else { + // No need to do a checkOldImpl() here, we know it's an up to date + // SocketImpl! + impl = new SocksSocketImpl(); + } + if (impl != null) + impl.setServerSocket(this); + } + + /** + * Creates the socket implementation. + * + * @throws IOException if creation fails + * @since 1.4 + */ + void createImpl() throws SocketException { + if (impl == null) + setImpl(); + try { + impl.create(true); + created = true; + } catch (IOException e) { + throw new SocketException(e.getMessage()); + } + } + + /** + * + * Binds the {@code ServerSocket} to a specific address + * (IP address and port number). + * <p> + * If the address is {@code null}, then the system will pick up + * an ephemeral port and a valid local address to bind the socket. + * <p> + * @param endpoint The IP address and port number to bind to. + * @throws IOException if the bind operation fails, or if the socket + * is already bound. + * @throws SecurityException if a {@code SecurityManager} is present and + * its {@code checkListen} method doesn't allow the operation. + * @throws IllegalArgumentException if endpoint is a + * SocketAddress subclass not supported by this socket + * @since 1.4 + */ + public void bind(SocketAddress endpoint) throws IOException { + bind(endpoint, 50); + } + + /** + * + * Binds the {@code ServerSocket} to a specific address + * (IP address and port number). + * <p> + * If the address is {@code null}, then the system will pick up + * an ephemeral port and a valid local address to bind the socket. + * <P> + * The {@code backlog} argument is the requested maximum number of + * pending connections on the socket. Its exact semantics are implementation + * specific. In particular, an implementation may impose a maximum length + * or may choose to ignore the parameter altogther. The value provided + * should be greater than {@code 0}. If it is less than or equal to + * {@code 0}, then an implementation specific default will be used. + * @param endpoint The IP address and port number to bind to. + * @param backlog requested maximum length of the queue of + * incoming connections. + * @throws IOException if the bind operation fails, or if the socket + * is already bound. + * @throws SecurityException if a {@code SecurityManager} is present and + * its {@code checkListen} method doesn't allow the operation. + * @throws IllegalArgumentException if endpoint is a + * SocketAddress subclass not supported by this socket + * @since 1.4 + */ + public void bind(SocketAddress endpoint, int backlog) throws IOException { + if (isClosed()) + throw new SocketException("Socket is closed"); + if (!oldImpl && isBound()) + throw new SocketException("Already bound"); + if (endpoint == null) + endpoint = new InetSocketAddress(0); + if (!(endpoint instanceof InetSocketAddress)) + throw new IllegalArgumentException("Unsupported address type"); + InetSocketAddress epoint = (InetSocketAddress) endpoint; + if (epoint.isUnresolved()) + throw new SocketException("Unresolved address"); + if (backlog < 1) + backlog = 50; + try { + SecurityManager security = System.getSecurityManager(); + if (security != null) + security.checkListen(epoint.getPort()); + getImpl().bind(epoint.getAddress(), epoint.getPort()); + getImpl().listen(backlog); + bound = true; + } catch(SecurityException e) { + bound = false; + throw e; + } catch(IOException e) { + bound = false; + throw e; + } + } + + /** + * Returns the local address of this server socket. + * <p> + * If the socket was bound prior to being {@link #close closed}, + * then this method will continue to return the local address + * after the socket is closed. + * <p> + * If there is a security manager set, its {@code checkConnect} method is + * called with the local address and {@code -1} as its arguments to see + * if the operation is allowed. If the operation is not allowed, + * the {@link InetAddress#getLoopbackAddress loopback} address is returned. + * + * @return the address to which this socket is bound, + * or the loopback address if denied by the security manager, + * or {@code null} if the socket is unbound. + * + * @see SecurityManager#checkConnect + */ + public InetAddress getInetAddress() { + if (!isBound()) + return null; + try { + InetAddress in = getImpl().getInetAddress(); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkConnect(in.getHostAddress(), -1); + return in; + } catch (SecurityException e) { + return InetAddress.getLoopbackAddress(); + } catch (SocketException e) { + // nothing + // If we're bound, the impl has been created + // so we shouldn't get here + } + return null; + } + + /** + * Returns the port number on which this socket is listening. + * <p> + * If the socket was bound prior to being {@link #close closed}, + * then this method will continue to return the port number + * after the socket is closed. + * + * @return the port number to which this socket is listening or + * -1 if the socket is not bound yet. + */ + public int getLocalPort() { + if (!isBound()) + return -1; + try { + return getImpl().getLocalPort(); + } catch (SocketException e) { + // nothing + // If we're bound, the impl has been created + // so we shouldn't get here + } + return -1; + } + + /** + * Returns the address of the endpoint this socket is bound to. + * <p> + * If the socket was bound prior to being {@link #close closed}, + * then this method will continue to return the address of the endpoint + * after the socket is closed. + * <p> + * If there is a security manager set, its {@code checkConnect} method is + * called with the local address and {@code -1} as its arguments to see + * if the operation is allowed. If the operation is not allowed, + * a {@code SocketAddress} representing the + * {@link InetAddress#getLoopbackAddress loopback} address and the local + * port to which the socket is bound is returned. + * + * @return a {@code SocketAddress} representing the local endpoint of + * this socket, or a {@code SocketAddress} representing the + * loopback address if denied by the security manager, + * or {@code null} if the socket is not bound yet. + * + * @see #getInetAddress() + * @see #getLocalPort() + * @see #bind(SocketAddress) + * @see SecurityManager#checkConnect + * @since 1.4 + */ + + public SocketAddress getLocalSocketAddress() { + if (!isBound()) + return null; + return new InetSocketAddress(getInetAddress(), getLocalPort()); + } + + /** + * Listens for a connection to be made to this socket and accepts + * it. The method blocks until a connection is made. + * + * <p>A new Socket {@code s} is created and, if there + * is a security manager, + * the security manager's {@code checkAccept} method is called + * with {@code s.getInetAddress().getHostAddress()} and + * {@code s.getPort()} + * as its arguments to ensure the operation is allowed. + * This could result in a SecurityException. + * + * @exception IOException if an I/O error occurs when waiting for a + * connection. + * @exception SecurityException if a security manager exists and its + * {@code checkAccept} method doesn't allow the operation. + * @exception SocketTimeoutException if a timeout was previously set with setSoTimeout and + * the timeout has been reached. + * @exception java.nio.channels.IllegalBlockingModeException + * if this socket has an associated channel, the channel is in + * non-blocking mode, and there is no connection ready to be + * accepted + * + * @return the new Socket + * @see SecurityManager#checkAccept + * @revised 1.4 + * @spec JSR-51 + */ + public Socket accept() throws IOException { + if (isClosed()) + throw new SocketException("Socket is closed"); + if (!isBound()) + throw new SocketException("Socket is not bound yet"); + Socket s = new Socket((SocketImpl) null); + implAccept(s); + return s; + } + + /** + * Subclasses of ServerSocket use this method to override accept() + * to return their own subclass of socket. So a FooServerSocket + * will typically hand this method an <i>empty</i> FooSocket. On + * return from implAccept the FooSocket will be connected to a client. + * + * @param s the Socket + * @throws java.nio.channels.IllegalBlockingModeException + * if this socket has an associated channel, + * and the channel is in non-blocking mode + * @throws IOException if an I/O error occurs when waiting + * for a connection. + * @since JDK1.1 + * @revised 1.4 + * @spec JSR-51 + */ + protected final void implAccept(Socket s) throws IOException { + SocketImpl si = null; + try { + if (s.impl == null) + s.setImpl(); + else { + s.impl.reset(); + } + si = s.impl; + s.impl = null; + si.address = new InetAddress(); + si.fd = new FileDescriptor(); + getImpl().accept(si); + + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkAccept(si.getInetAddress().getHostAddress(), + si.getPort()); + } + } catch (IOException e) { + if (si != null) + si.reset(); + s.impl = si; + throw e; + } catch (SecurityException e) { + if (si != null) + si.reset(); + s.impl = si; + throw e; + } + s.impl = si; + s.postAccept(); + } + + /** + * Closes this socket. + * + * Any thread currently blocked in {@link #accept()} will throw + * a {@link SocketException}. + * + * <p> If this socket has an associated channel then the channel is closed + * as well. + * + * @exception IOException if an I/O error occurs when closing the socket. + * @revised 1.4 + * @spec JSR-51 + */ + public void close() throws IOException { + synchronized(closeLock) { + if (isClosed()) + return; + if (created) + impl.close(); + closed = true; + } + } + + /** + * Returns the unique {@link java.nio.channels.ServerSocketChannel} object + * associated with this socket, if any. + * + * <p> A server socket will have a channel if, and only if, the channel + * itself was created via the {@link + * java.nio.channels.ServerSocketChannel#open ServerSocketChannel.open} + * method. + * + * @return the server-socket channel associated with this socket, + * or {@code null} if this socket was not created + * for a channel + * + * @since 1.4 + * @spec JSR-51 + */ + public ServerSocketChannel getChannel() { + return null; + } + + /** + * Returns the binding state of the ServerSocket. + * + * @return true if the ServerSocket successfully bound to an address + * @since 1.4 + */ + public boolean isBound() { + // Before 1.3 ServerSockets were always bound during creation + return bound || oldImpl; + } + + /** + * Returns the closed state of the ServerSocket. + * + * @return true if the socket has been closed + * @since 1.4 + */ + public boolean isClosed() { + synchronized(closeLock) { + return closed; + } + } + + /** + * Enable/disable {@link SocketOptions#SO_TIMEOUT SO_TIMEOUT} with the + * specified timeout, in milliseconds. With this option set to a non-zero + * timeout, a call to accept() for this ServerSocket + * will block for only this amount of time. If the timeout expires, + * a <B>java.net.SocketTimeoutException</B> is raised, though the + * ServerSocket is still valid. The option <B>must</B> be enabled + * prior to entering the blocking operation to have effect. The + * timeout must be {@code > 0}. + * A timeout of zero is interpreted as an infinite timeout. + * @param timeout the specified timeout, in milliseconds + * @exception SocketException if there is an error in + * the underlying protocol, such as a TCP error. + * @since JDK1.1 + * @see #getSoTimeout() + */ + public synchronized void setSoTimeout(int timeout) throws SocketException { + if (isClosed()) + throw new SocketException("Socket is closed"); + getImpl().setOption(SocketOptions.SO_TIMEOUT, new Integer(timeout)); + } + + /** + * Retrieve setting for {@link SocketOptions#SO_TIMEOUT SO_TIMEOUT}. + * 0 returns implies that the option is disabled (i.e., timeout of infinity). + * @return the {@link SocketOptions#SO_TIMEOUT SO_TIMEOUT} value + * @exception IOException if an I/O error occurs + * @since JDK1.1 + * @see #setSoTimeout(int) + */ + public synchronized int getSoTimeout() throws IOException { + if (isClosed()) + throw new SocketException("Socket is closed"); + Object o = getImpl().getOption(SocketOptions.SO_TIMEOUT); + /* extra type safety */ + if (o instanceof Integer) { + return ((Integer) o).intValue(); + } else { + return 0; + } + } + + /** + * Enable/disable the {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} + * socket option. + * <p> + * When a TCP connection is closed the connection may remain + * in a timeout state for a period of time after the connection + * is closed (typically known as the {@code TIME_WAIT} state + * or {@code 2MSL} wait state). + * For applications using a well known socket address or port + * it may not be possible to bind a socket to the required + * {@code SocketAddress} if there is a connection in the + * timeout state involving the socket address or port. + * <p> + * Enabling {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} prior to + * binding the socket using {@link #bind(SocketAddress)} allows the socket + * to be bound even though a previous connection is in a timeout state. + * <p> + * When a {@code ServerSocket} is created the initial setting + * of {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} is not defined. + * Applications can use {@link #getReuseAddress()} to determine the initial + * setting of {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR}. + * <p> + * The behaviour when {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} is + * enabled or disabled after a socket is bound (See {@link #isBound()}) + * is not defined. + * + * @param on whether to enable or disable the socket option + * @exception SocketException if an error occurs enabling or + * disabling the {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} + * socket option, or the socket is closed. + * @since 1.4 + * @see #getReuseAddress() + * @see #bind(SocketAddress) + * @see #isBound() + * @see #isClosed() + */ + public void setReuseAddress(boolean on) throws SocketException { + if (isClosed()) + throw new SocketException("Socket is closed"); + getImpl().setOption(SocketOptions.SO_REUSEADDR, Boolean.valueOf(on)); + } + + /** + * Tests if {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} is enabled. + * + * @return a {@code boolean} indicating whether or not + * {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} is enabled. + * @exception SocketException if there is an error + * in the underlying protocol, such as a TCP error. + * @since 1.4 + * @see #setReuseAddress(boolean) + */ + public boolean getReuseAddress() throws SocketException { + if (isClosed()) + throw new SocketException("Socket is closed"); + return ((Boolean) (getImpl().getOption(SocketOptions.SO_REUSEADDR))).booleanValue(); + } + + /** + * Returns the implementation address and implementation port of + * this socket as a {@code String}. + * <p> + * If there is a security manager set, its {@code checkConnect} method is + * called with the local address and {@code -1} as its arguments to see + * if the operation is allowed. If the operation is not allowed, + * an {@code InetAddress} representing the + * {@link InetAddress#getLoopbackAddress loopback} address is returned as + * the implementation address. + * + * @return a string representation of this socket. + */ + public String toString() { + if (!isBound()) + return "ServerSocket[unbound]"; + InetAddress in; + if (System.getSecurityManager() != null) + in = InetAddress.getLoopbackAddress(); + else + in = impl.getInetAddress(); + return "ServerSocket[addr=" + in + + ",localport=" + impl.getLocalPort() + "]"; + } + + void setBound() { + bound = true; + } + + void setCreated() { + created = true; + } + + /** + * The factory for all server sockets. + */ + private static SocketImplFactory factory = null; + + /** + * Sets the server socket implementation factory for the + * application. The factory can be specified only once. + * <p> + * When an application creates a new server socket, the socket + * implementation factory's {@code createSocketImpl} method is + * called to create the actual socket implementation. + * <p> + * Passing {@code null} to the method is a no-op unless the factory + * was already set. + * <p> + * If there is a security manager, this method first calls + * the security manager's {@code checkSetFactory} method + * to ensure the operation is allowed. + * This could result in a SecurityException. + * + * @param fac the desired factory. + * @exception IOException if an I/O error occurs when setting the + * socket factory. + * @exception SocketException if the factory has already been defined. + * @exception SecurityException if a security manager exists and its + * {@code checkSetFactory} method doesn't allow the operation. + * @see java.net.SocketImplFactory#createSocketImpl() + * @see SecurityManager#checkSetFactory + */ + public static synchronized void setSocketFactory(SocketImplFactory fac) throws IOException { + if (factory != null) { + throw new SocketException("factory already defined"); + } + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkSetFactory(); + } + factory = fac; + } + + /** + * Sets a default proposed value for the + * {@link SocketOptions#SO_RCVBUF SO_RCVBUF} option for sockets + * accepted from this {@code ServerSocket}. The value actually set + * in the accepted socket must be determined by calling + * {@link Socket#getReceiveBufferSize()} after the socket + * is returned by {@link #accept()}. + * <p> + * The value of {@link SocketOptions#SO_RCVBUF SO_RCVBUF} is used both to + * set the size of the internal socket receive buffer, and to set the size + * of the TCP receive window that is advertized to the remote peer. + * <p> + * It is possible to change the value subsequently, by calling + * {@link Socket#setReceiveBufferSize(int)}. However, if the application + * wishes to allow a receive window larger than 64K bytes, as defined by RFC1323 + * then the proposed value must be set in the ServerSocket <B>before</B> + * it is bound to a local address. This implies, that the ServerSocket must be + * created with the no-argument constructor, then setReceiveBufferSize() must + * be called and lastly the ServerSocket is bound to an address by calling bind(). + * <p> + * Failure to do this will not cause an error, and the buffer size may be set to the + * requested value but the TCP receive window in sockets accepted from + * this ServerSocket will be no larger than 64K bytes. + * + * @exception SocketException if there is an error + * in the underlying protocol, such as a TCP error. + * + * @param size the size to which to set the receive buffer + * size. This value must be greater than 0. + * + * @exception IllegalArgumentException if the + * value is 0 or is negative. + * + * @since 1.4 + * @see #getReceiveBufferSize + */ + public synchronized void setReceiveBufferSize (int size) throws SocketException { + if (!(size > 0)) { + throw new IllegalArgumentException("negative receive size"); + } + if (isClosed()) + throw new SocketException("Socket is closed"); + getImpl().setOption(SocketOptions.SO_RCVBUF, new Integer(size)); + } + + /** + * Gets the value of the {@link SocketOptions#SO_RCVBUF SO_RCVBUF} option + * for this {@code ServerSocket}, that is the proposed buffer size that + * will be used for Sockets accepted from this {@code ServerSocket}. + * + * <p>Note, the value actually set in the accepted socket is determined by + * calling {@link Socket#getReceiveBufferSize()}. + * @return the value of the {@link SocketOptions#SO_RCVBUF SO_RCVBUF} + * option for this {@code Socket}. + * @exception SocketException if there is an error + * in the underlying protocol, such as a TCP error. + * @see #setReceiveBufferSize(int) + * @since 1.4 + */ + public synchronized int getReceiveBufferSize() + throws SocketException{ + if (isClosed()) + throw new SocketException("Socket is closed"); + int result = 0; + Object o = getImpl().getOption(SocketOptions.SO_RCVBUF); + if (o instanceof Integer) { + result = ((Integer)o).intValue(); + } + return result; + } + + /** + * Sets performance preferences for this ServerSocket. + * + * <p> Sockets use the TCP/IP protocol by default. Some implementations + * may offer alternative protocols which have different performance + * characteristics than TCP/IP. This method allows the application to + * express its own preferences as to how these tradeoffs should be made + * when the implementation chooses from the available protocols. + * + * <p> Performance preferences are described by three integers + * whose values indicate the relative importance of short connection time, + * low latency, and high bandwidth. The absolute values of the integers + * are irrelevant; in order to choose a protocol the values are simply + * compared, with larger values indicating stronger preferences. If the + * application prefers short connection time over both low latency and high + * bandwidth, for example, then it could invoke this method with the values + * {@code (1, 0, 0)}. If the application prefers high bandwidth above low + * latency, and low latency above short connection time, then it could + * invoke this method with the values {@code (0, 1, 2)}. + * + * <p> Invoking this method after this socket has been bound + * will have no effect. This implies that in order to use this capability + * requires the socket to be created with the no-argument constructor. + * + * @param connectionTime + * An {@code int} expressing the relative importance of a short + * connection time + * + * @param latency + * An {@code int} expressing the relative importance of low + * latency + * + * @param bandwidth + * An {@code int} expressing the relative importance of high + * bandwidth + * + * @since 1.5 + */ + public void setPerformancePreferences(int connectionTime, + int latency, + int bandwidth) + { + /* Not implemented yet */ + } + + // Android-added: getFileDescriptor$(), for testing / internal use. + /** + * @hide internal use only + */ + public FileDescriptor getFileDescriptor$() { + return impl.getFileDescriptor(); + } +}
diff --git a/java/net/Socket.annotated.java b/java/net/Socket.annotated.java new file mode 100644 index 0000000..e021788 --- /dev/null +++ b/java/net/Socket.annotated.java
@@ -0,0 +1,146 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package java.net; + +import java.nio.channels.SocketChannel; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +@SuppressWarnings({"unchecked", "deprecation", "all"}) +public class Socket implements java.io.Closeable { + +public Socket() { throw new RuntimeException("Stub!"); } + +public Socket(java.net.Proxy proxy) { throw new RuntimeException("Stub!"); } + +protected Socket(java.net.SocketImpl impl) throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public Socket(java.lang.String host, int port) throws java.io.IOException, java.net.UnknownHostException { throw new RuntimeException("Stub!"); } + +public Socket(java.net.InetAddress address, int port) throws java.io.IOException { throw new RuntimeException("Stub!"); } + +public Socket(java.lang.String host, int port, java.net.InetAddress localAddr, int localPort) throws java.io.IOException { throw new RuntimeException("Stub!"); } + +public Socket(java.net.InetAddress address, int port, java.net.InetAddress localAddr, int localPort) throws java.io.IOException { throw new RuntimeException("Stub!"); } + +@Deprecated +public Socket(java.lang.String host, int port, boolean stream) throws java.io.IOException { throw new RuntimeException("Stub!"); } + +@Deprecated +public Socket(java.net.InetAddress host, int port, boolean stream) throws java.io.IOException { throw new RuntimeException("Stub!"); } + +public void connect(java.net.SocketAddress endpoint) throws java.io.IOException { throw new RuntimeException("Stub!"); } + +public void connect(java.net.SocketAddress endpoint, int timeout) throws java.io.IOException { throw new RuntimeException("Stub!"); } + +public void bind(java.net.SocketAddress bindpoint) throws java.io.IOException { throw new RuntimeException("Stub!"); } + +public java.net.InetAddress getInetAddress() { throw new RuntimeException("Stub!"); } + +public java.net.InetAddress getLocalAddress() { throw new RuntimeException("Stub!"); } + +public int getPort() { throw new RuntimeException("Stub!"); } + +public int getLocalPort() { throw new RuntimeException("Stub!"); } + +public java.net.SocketAddress getRemoteSocketAddress() { throw new RuntimeException("Stub!"); } + +public java.net.SocketAddress getLocalSocketAddress() { throw new RuntimeException("Stub!"); } + +public java.nio.channels.SocketChannel getChannel() { throw new RuntimeException("Stub!"); } + +public java.io.InputStream getInputStream() throws java.io.IOException { throw new RuntimeException("Stub!"); } + +public java.io.OutputStream getOutputStream() throws java.io.IOException { throw new RuntimeException("Stub!"); } + +public void setTcpNoDelay(boolean on) throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public boolean getTcpNoDelay() throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public void setSoLinger(boolean on, int linger) throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public int getSoLinger() throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public void sendUrgentData(int data) throws java.io.IOException { throw new RuntimeException("Stub!"); } + +public void setOOBInline(boolean on) throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public boolean getOOBInline() throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public synchronized void setSoTimeout(int timeout) throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public synchronized int getSoTimeout() throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public synchronized void setSendBufferSize(int size) throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public synchronized int getSendBufferSize() throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public synchronized void setReceiveBufferSize(int size) throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public synchronized int getReceiveBufferSize() throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public void setKeepAlive(boolean on) throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public boolean getKeepAlive() throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public void setTrafficClass(int tc) throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public int getTrafficClass() throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public void setReuseAddress(boolean on) throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public boolean getReuseAddress() throws java.net.SocketException { throw new RuntimeException("Stub!"); } + +public synchronized void close() throws java.io.IOException { throw new RuntimeException("Stub!"); } + +public void shutdownInput() throws java.io.IOException { throw new RuntimeException("Stub!"); } + +public void shutdownOutput() throws java.io.IOException { throw new RuntimeException("Stub!"); } + +public java.lang.String toString() { throw new RuntimeException("Stub!"); } + +public boolean isConnected() { throw new RuntimeException("Stub!"); } + +public boolean isBound() { throw new RuntimeException("Stub!"); } + +public boolean isClosed() { throw new RuntimeException("Stub!"); } + +public boolean isInputShutdown() { throw new RuntimeException("Stub!"); } + +public boolean isOutputShutdown() { throw new RuntimeException("Stub!"); } + +public static synchronized void setSocketImplFactory(java.net.SocketImplFactory fac) throws java.io.IOException { throw new RuntimeException("Stub!"); } + +public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) { throw new RuntimeException("Stub!"); } + [email protected] [email protected] +public java.io.FileDescriptor getFileDescriptor$() { throw new RuntimeException("Stub!"); } +} +
diff --git a/java/net/Socket.java b/java/net/Socket.java new file mode 100644 index 0000000..bae1d1c --- /dev/null +++ b/java/net/Socket.java
@@ -0,0 +1,1783 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.FileDescriptor; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.nio.channels.SocketChannel; +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; +import java.security.PrivilegedAction; + +/** + * This class implements client sockets (also called just + * "sockets"). A socket is an endpoint for communication + * between two machines. + * <p> + * The actual work of the socket is performed by an instance of the + * {@code SocketImpl} class. An application, by changing + * the socket factory that creates the socket implementation, + * can configure itself to create sockets appropriate to the local + * firewall. + * + * @author unascribed + * @see java.net.Socket#setSocketImplFactory(java.net.SocketImplFactory) + * @see java.net.SocketImpl + * @see java.nio.channels.SocketChannel + * @since JDK1.0 + */ +public +class Socket implements java.io.Closeable { + /** + * Various states of this socket. + */ + private boolean created = false; + private boolean bound = false; + private boolean connected = false; + private boolean closed = false; + private Object closeLock = new Object(); + private boolean shutIn = false; + private boolean shutOut = false; + + /** + * The implementation of this Socket. + */ + SocketImpl impl; + + /** + * Are we using an older SocketImpl? + */ + private boolean oldImpl = false; + + /** + * Creates an unconnected socket, with the + * system-default type of SocketImpl. + * + * @since JDK1.1 + * @revised 1.4 + */ + public Socket() { + setImpl(); + } + + /** + * Creates an unconnected socket, specifying the type of proxy, if any, + * that should be used regardless of any other settings. + * <P> + * If there is a security manager, its {@code checkConnect} method + * is called with the proxy host address and port number + * as its arguments. This could result in a SecurityException. + * <P> + * Examples: + * <UL> <LI>{@code Socket s = new Socket(Proxy.NO_PROXY);} will create + * a plain socket ignoring any other proxy configuration.</LI> + * <LI>{@code Socket s = new Socket(new Proxy(Proxy.Type.SOCKS, new InetSocketAddress("socks.mydom.com", 1080)));} + * will create a socket connecting through the specified SOCKS proxy + * server.</LI> + * </UL> + * + * @param proxy a {@link java.net.Proxy Proxy} object specifying what kind + * of proxying should be used. + * @throws IllegalArgumentException if the proxy is of an invalid type + * or {@code null}. + * @throws SecurityException if a security manager is present and + * permission to connect to the proxy is + * denied. + * @see java.net.ProxySelector + * @see java.net.Proxy + * + * @since 1.5 + */ + public Socket(Proxy proxy) { + // Create a copy of Proxy as a security measure + if (proxy == null) { + throw new IllegalArgumentException("Invalid Proxy"); + } + Proxy p = proxy == Proxy.NO_PROXY ? Proxy.NO_PROXY + : sun.net.ApplicationProxy.create(proxy); + Proxy.Type type = p.type(); + // Android-changed: Removed HTTP proxy support. + // if (type == Proxy.Type.SOCKS || type == Proxy.Type.HTTP) { + if (type == Proxy.Type.SOCKS) { + SecurityManager security = System.getSecurityManager(); + InetSocketAddress epoint = (InetSocketAddress) p.address(); + if (epoint.getAddress() != null) { + checkAddress (epoint.getAddress(), "Socket"); + } + if (security != null) { + if (epoint.isUnresolved()) + epoint = new InetSocketAddress(epoint.getHostName(), epoint.getPort()); + if (epoint.isUnresolved()) + security.checkConnect(epoint.getHostName(), epoint.getPort()); + else + security.checkConnect(epoint.getAddress().getHostAddress(), + epoint.getPort()); + } + // Android-changed: Removed HTTP proxy support. + // impl = type == Proxy.Type.SOCKS ? new SocksSocketImpl(p) + // : new HttpConnectSocketImpl(p); + impl = new SocksSocketImpl(p); + impl.setSocket(this); + } else { + if (p == Proxy.NO_PROXY) { + if (factory == null) { + impl = new PlainSocketImpl(); + impl.setSocket(this); + } else + setImpl(); + } else + throw new IllegalArgumentException("Invalid Proxy"); + } + } + + /** + * Creates an unconnected Socket with a user-specified + * SocketImpl. + * <P> + * @param impl an instance of a <B>SocketImpl</B> + * the subclass wishes to use on the Socket. + * + * @exception SocketException if there is an error in the underlying protocol, + * such as a TCP error. + * @since JDK1.1 + */ + protected Socket(SocketImpl impl) throws SocketException { + this.impl = impl; + if (impl != null) { + checkOldImpl(); + this.impl.setSocket(this); + } + } + + /** + * Creates a stream socket and connects it to the specified port + * number on the named host. + * <p> + * If the specified host is {@code null} it is the equivalent of + * specifying the address as + * {@link java.net.InetAddress#getByName InetAddress.getByName}{@code (null)}. + * In other words, it is equivalent to specifying an address of the + * loopback interface. </p> + * <p> + * If the application has specified a server socket factory, that + * factory's {@code createSocketImpl} method is called to create + * the actual socket implementation. Otherwise a "plain" socket is created. + * <p> + * If there is a security manager, its + * {@code checkConnect} method is called + * with the host address and {@code port} + * as its arguments. This could result in a SecurityException. + * + * @param host the host name, or {@code null} for the loopback address. + * @param port the port number. + * + * @exception UnknownHostException if the IP address of + * the host could not be determined. + * + * @exception IOException if an I/O error occurs when creating the socket. + * @exception SecurityException if a security manager exists and its + * {@code checkConnect} method doesn't allow the operation. + * @exception IllegalArgumentException if the port parameter is outside + * the specified range of valid port values, which is between + * 0 and 65535, inclusive. + * @see java.net.Socket#setSocketImplFactory(java.net.SocketImplFactory) + * @see java.net.SocketImpl + * @see java.net.SocketImplFactory#createSocketImpl() + * @see SecurityManager#checkConnect + */ + public Socket(String host, int port) + throws UnknownHostException, IOException + { + // Android-changed: App compat. Socket ctor should try all addresses. http://b/30007735 + this(InetAddress.getAllByName(host), port, (SocketAddress) null, true); + } + + /** + * Creates a stream socket and connects it to the specified port + * number at the specified IP address. + * <p> + * If the application has specified a socket factory, that factory's + * {@code createSocketImpl} method is called to create the + * actual socket implementation. Otherwise a "plain" socket is created. + * <p> + * If there is a security manager, its + * {@code checkConnect} method is called + * with the host address and {@code port} + * as its arguments. This could result in a SecurityException. + * + * @param address the IP address. + * @param port the port number. + * @exception IOException if an I/O error occurs when creating the socket. + * @exception SecurityException if a security manager exists and its + * {@code checkConnect} method doesn't allow the operation. + * @exception IllegalArgumentException if the port parameter is outside + * the specified range of valid port values, which is between + * 0 and 65535, inclusive. + * @exception NullPointerException if {@code address} is null. + * @see java.net.Socket#setSocketImplFactory(java.net.SocketImplFactory) + * @see java.net.SocketImpl + * @see java.net.SocketImplFactory#createSocketImpl() + * @see SecurityManager#checkConnect + */ + public Socket(InetAddress address, int port) throws IOException { + // Android-changed: App compat. Socket ctor should try all addresses. http://b/30007735 + this(nonNullAddress(address), port, (SocketAddress) null, true); + } + + /** + * Creates a socket and connects it to the specified remote host on + * the specified remote port. The Socket will also bind() to the local + * address and port supplied. + * <p> + * If the specified host is {@code null} it is the equivalent of + * specifying the address as + * {@link java.net.InetAddress#getByName InetAddress.getByName}{@code (null)}. + * In other words, it is equivalent to specifying an address of the + * loopback interface. </p> + * <p> + * A local port number of {@code zero} will let the system pick up a + * free port in the {@code bind} operation.</p> + * <p> + * If there is a security manager, its + * {@code checkConnect} method is called + * with the host address and {@code port} + * as its arguments. This could result in a SecurityException. + * + * @param host the name of the remote host, or {@code null} for the loopback address. + * @param port the remote port + * @param localAddr the local address the socket is bound to, or + * {@code null} for the {@code anyLocal} address. + * @param localPort the local port the socket is bound to, or + * {@code zero} for a system selected free port. + * @exception IOException if an I/O error occurs when creating the socket. + * @exception SecurityException if a security manager exists and its + * {@code checkConnect} method doesn't allow the connection + * to the destination, or if its {@code checkListen} method + * doesn't allow the bind to the local port. + * @exception IllegalArgumentException if the port parameter or localPort + * parameter is outside the specified range of valid port values, + * which is between 0 and 65535, inclusive. + * @see SecurityManager#checkConnect + * @since JDK1.1 + */ + public Socket(String host, int port, InetAddress localAddr, + int localPort) throws IOException { + // Android-changed: App compat. Socket ctor should try all addresses. http://b/30007735 + this(InetAddress.getAllByName(host), port, + new InetSocketAddress(localAddr, localPort), true); + } + + /** + * Creates a socket and connects it to the specified remote address on + * the specified remote port. The Socket will also bind() to the local + * address and port supplied. + * <p> + * If the specified local address is {@code null} it is the equivalent of + * specifying the address as the AnyLocal address + * (see {@link java.net.InetAddress#isAnyLocalAddress InetAddress.isAnyLocalAddress}{@code ()}). + * <p> + * A local port number of {@code zero} will let the system pick up a + * free port in the {@code bind} operation.</p> + * <p> + * If there is a security manager, its + * {@code checkConnect} method is called + * with the host address and {@code port} + * as its arguments. This could result in a SecurityException. + * + * @param address the remote address + * @param port the remote port + * @param localAddr the local address the socket is bound to, or + * {@code null} for the {@code anyLocal} address. + * @param localPort the local port the socket is bound to or + * {@code zero} for a system selected free port. + * @exception IOException if an I/O error occurs when creating the socket. + * @exception SecurityException if a security manager exists and its + * {@code checkConnect} method doesn't allow the connection + * to the destination, or if its {@code checkListen} method + * doesn't allow the bind to the local port. + * @exception IllegalArgumentException if the port parameter or localPort + * parameter is outside the specified range of valid port values, + * which is between 0 and 65535, inclusive. + * @exception NullPointerException if {@code address} is null. + * @see SecurityManager#checkConnect + * @since JDK1.1 + */ + public Socket(InetAddress address, int port, InetAddress localAddr, + int localPort) throws IOException { + // Android-changed: App compat. Socket ctor should try all addresses. http://b/30007735 + this(nonNullAddress(address), port, + new InetSocketAddress(localAddr, localPort), true); + } + + /** + * Creates a stream socket and connects it to the specified port + * number on the named host. + * <p> + * If the specified host is {@code null} it is the equivalent of + * specifying the address as + * {@link java.net.InetAddress#getByName InetAddress.getByName}{@code (null)}. + * In other words, it is equivalent to specifying an address of the + * loopback interface. </p> + * <p> + * If the stream argument is {@code true}, this creates a + * stream socket. If the stream argument is {@code false}, it + * creates a datagram socket. + * <p> + * If the application has specified a server socket factory, that + * factory's {@code createSocketImpl} method is called to create + * the actual socket implementation. Otherwise a "plain" socket is created. + * <p> + * If there is a security manager, its + * {@code checkConnect} method is called + * with the host address and {@code port} + * as its arguments. This could result in a SecurityException. + * <p> + * If a UDP socket is used, TCP/IP related socket options will not apply. + * + * @param host the host name, or {@code null} for the loopback address. + * @param port the port number. + * @param stream a {@code boolean} indicating whether this is + * a stream socket or a datagram socket. + * @exception IOException if an I/O error occurs when creating the socket. + * @exception SecurityException if a security manager exists and its + * {@code checkConnect} method doesn't allow the operation. + * @exception IllegalArgumentException if the port parameter is outside + * the specified range of valid port values, which is between + * 0 and 65535, inclusive. + * @see java.net.Socket#setSocketImplFactory(java.net.SocketImplFactory) + * @see java.net.SocketImpl + * @see java.net.SocketImplFactory#createSocketImpl() + * @see SecurityManager#checkConnect + * @deprecated Use DatagramSocket instead for UDP transport. + */ + @Deprecated + public Socket(String host, int port, boolean stream) throws IOException { + // Android-changed: App compat. Socket ctor should try all addresses. http://b/30007735 + this(InetAddress.getAllByName(host), port, (SocketAddress) null, stream); + } + + /** + * Creates a socket and connects it to the specified port number at + * the specified IP address. + * <p> + * If the stream argument is {@code true}, this creates a + * stream socket. If the stream argument is {@code false}, it + * creates a datagram socket. + * <p> + * If the application has specified a server socket factory, that + * factory's {@code createSocketImpl} method is called to create + * the actual socket implementation. Otherwise a "plain" socket is created. + * + * <p>If there is a security manager, its + * {@code checkConnect} method is called + * with {@code host.getHostAddress()} and {@code port} + * as its arguments. This could result in a SecurityException. + * <p> + * If UDP socket is used, TCP/IP related socket options will not apply. + * + * @param host the IP address. + * @param port the port number. + * @param stream if {@code true}, create a stream socket; + * otherwise, create a datagram socket. + * @exception IOException if an I/O error occurs when creating the socket. + * @exception SecurityException if a security manager exists and its + * {@code checkConnect} method doesn't allow the operation. + * @exception IllegalArgumentException if the port parameter is outside + * the specified range of valid port values, which is between + * 0 and 65535, inclusive. + * @exception NullPointerException if {@code host} is null. + * @see java.net.Socket#setSocketImplFactory(java.net.SocketImplFactory) + * @see java.net.SocketImpl + * @see java.net.SocketImplFactory#createSocketImpl() + * @see SecurityManager#checkConnect + * @deprecated Use DatagramSocket instead for UDP transport. + */ + @Deprecated + public Socket(InetAddress host, int port, boolean stream) throws IOException { + // Android-changed: App compat. Socket ctor should try all addresses. http://b/30007735 + this(nonNullAddress(host), port, new InetSocketAddress(0), stream); + } + + // BEGIN Android-changed: App compat. Socket ctor should try all addresses. http://b/30007735 + private static InetAddress[] nonNullAddress(InetAddress address) { + // backward compatibility + if (address == null) + throw new NullPointerException(); + + return new InetAddress[] { address }; + } + + private Socket(InetAddress[] addresses, int port, SocketAddress localAddr, + boolean stream) throws IOException { + if (addresses == null || addresses.length == 0) { + throw new SocketException("Impossible: empty address list"); + } + + for (int i = 0; i < addresses.length; i++) { + setImpl(); + try { + InetSocketAddress address = new InetSocketAddress(addresses[i], port); + createImpl(stream); + if (localAddr != null) { + bind(localAddr); + } + connect(address); + break; + } catch (IOException | IllegalArgumentException | SecurityException e) { + try { + // Android-changed: Let ctor call impl.close() instead of overridable close(). + // Subclasses may not expect a call to close() coming from this constructor. + impl.close(); + closed = true; + } catch (IOException ce) { + e.addSuppressed(ce); + } + + // Only stop on the last address. + if (i == addresses.length - 1) { + throw e; + } + } + + // Discard the connection state and try again. + impl = null; + created = false; + bound = false; + closed = false; + } + } + // END Android-changed: App compat. Socket ctor should try all addresses. http://b/30007735 + + /** + * Creates the socket implementation. + * + * @param stream a {@code boolean} value : {@code true} for a TCP socket, + * {@code false} for UDP. + * @throws IOException if creation fails + * @since 1.4 + */ + void createImpl(boolean stream) throws SocketException { + if (impl == null) + setImpl(); + try { + impl.create(stream); + created = true; + } catch (IOException e) { + throw new SocketException(e.getMessage()); + } + } + + private void checkOldImpl() { + if (impl == null) + return; + // SocketImpl.connect() is a protected method, therefore we need to use + // getDeclaredMethod, therefore we need permission to access the member + + oldImpl = AccessController.doPrivileged + (new PrivilegedAction<Boolean>() { + public Boolean run() { + Class<?> clazz = impl.getClass(); + while (true) { + try { + clazz.getDeclaredMethod("connect", SocketAddress.class, int.class); + return Boolean.FALSE; + } catch (NoSuchMethodException e) { + clazz = clazz.getSuperclass(); + // java.net.SocketImpl class will always have this abstract method. + // If we have not found it by now in the hierarchy then it does not + // exist, we are an old style impl. + if (clazz.equals(java.net.SocketImpl.class)) { + return Boolean.TRUE; + } + } + } + } + }); + } + + /** + * Sets impl to the system-default type of SocketImpl. + * @since 1.4 + */ + void setImpl() { + if (factory != null) { + impl = factory.createSocketImpl(); + checkOldImpl(); + } else { + // No need to do a checkOldImpl() here, we know it's an up to date + // SocketImpl! + impl = new SocksSocketImpl(); + } + if (impl != null) + impl.setSocket(this); + } + + + /** + * Get the {@code SocketImpl} attached to this socket, creating + * it if necessary. + * + * @return the {@code SocketImpl} attached to that ServerSocket. + * @throws SocketException if creation fails + * @since 1.4 + */ + SocketImpl getImpl() throws SocketException { + if (!created) + createImpl(true); + return impl; + } + + /** + * Connects this socket to the server. + * + * @param endpoint the {@code SocketAddress} + * @throws IOException if an error occurs during the connection + * @throws java.nio.channels.IllegalBlockingModeException + * if this socket has an associated channel, + * and the channel is in non-blocking mode + * @throws IllegalArgumentException if endpoint is null or is a + * SocketAddress subclass not supported by this socket + * @since 1.4 + * @spec JSR-51 + */ + public void connect(SocketAddress endpoint) throws IOException { + connect(endpoint, 0); + } + + /** + * Connects this socket to the server with a specified timeout value. + * A timeout of zero is interpreted as an infinite timeout. The connection + * will then block until established or an error occurs. + * + * @param endpoint the {@code SocketAddress} + * @param timeout the timeout value to be used in milliseconds. + * @throws IOException if an error occurs during the connection + * @throws SocketTimeoutException if timeout expires before connecting + * @throws java.nio.channels.IllegalBlockingModeException + * if this socket has an associated channel, + * and the channel is in non-blocking mode + * @throws IllegalArgumentException if endpoint is null or is a + * SocketAddress subclass not supported by this socket + * @since 1.4 + * @spec JSR-51 + */ + public void connect(SocketAddress endpoint, int timeout) throws IOException { + if (endpoint == null) + throw new IllegalArgumentException("connect: The address can't be null"); + + if (timeout < 0) + throw new IllegalArgumentException("connect: timeout can't be negative"); + + if (isClosed()) + throw new SocketException("Socket is closed"); + + if (!oldImpl && isConnected()) + throw new SocketException("already connected"); + + if (!(endpoint instanceof InetSocketAddress)) + throw new IllegalArgumentException("Unsupported address type"); + + InetSocketAddress epoint = (InetSocketAddress) endpoint; + InetAddress addr = epoint.getAddress (); + int port = epoint.getPort(); + checkAddress(addr, "connect"); + + SecurityManager security = System.getSecurityManager(); + if (security != null) { + if (epoint.isUnresolved()) + security.checkConnect(epoint.getHostName(), port); + else + security.checkConnect(addr.getHostAddress(), port); + } + if (!created) + createImpl(true); + if (!oldImpl) + impl.connect(epoint, timeout); + else if (timeout == 0) { + if (epoint.isUnresolved()) + impl.connect(addr.getHostName(), port); + else + impl.connect(addr, port); + } else + throw new UnsupportedOperationException("SocketImpl.connect(addr, timeout)"); + connected = true; + /* + * If the socket was not bound before the connect, it is now because + * the kernel will have picked an ephemeral port & a local address + */ + bound = true; + } + + /** + * Binds the socket to a local address. + * <P> + * If the address is {@code null}, then the system will pick up + * an ephemeral port and a valid local address to bind the socket. + * + * @param bindpoint the {@code SocketAddress} to bind to + * @throws IOException if the bind operation fails, or if the socket + * is already bound. + * @throws IllegalArgumentException if bindpoint is a + * SocketAddress subclass not supported by this socket + * @throws SecurityException if a security manager exists and its + * {@code checkListen} method doesn't allow the bind + * to the local port. + * + * @since 1.4 + * @see #isBound + */ + public void bind(SocketAddress bindpoint) throws IOException { + if (isClosed()) + throw new SocketException("Socket is closed"); + if (!oldImpl && isBound()) + throw new SocketException("Already bound"); + + if (bindpoint != null && (!(bindpoint instanceof InetSocketAddress))) + throw new IllegalArgumentException("Unsupported address type"); + InetSocketAddress epoint = (InetSocketAddress) bindpoint; + if (epoint != null && epoint.isUnresolved()) + throw new SocketException("Unresolved address"); + if (epoint == null) { + epoint = new InetSocketAddress(0); + } + InetAddress addr = epoint.getAddress(); + int port = epoint.getPort(); + checkAddress (addr, "bind"); + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkListen(port); + } + getImpl().bind (addr, port); + bound = true; + } + + private void checkAddress (InetAddress addr, String op) { + if (addr == null) { + return; + } + if (!(addr instanceof Inet4Address || addr instanceof Inet6Address)) { + throw new IllegalArgumentException(op + ": invalid address type"); + } + } + + /** + * set the flags after an accept() call. + */ + final void postAccept() { + connected = true; + created = true; + bound = true; + } + + void setCreated() { + created = true; + } + + void setBound() { + bound = true; + } + + void setConnected() { + connected = true; + } + + /** + * Returns the address to which the socket is connected. + * <p> + * If the socket was connected prior to being {@link #close closed}, + * then this method will continue to return the connected address + * after the socket is closed. + * + * @return the remote IP address to which this socket is connected, + * or {@code null} if the socket is not connected. + */ + public InetAddress getInetAddress() { + if (!isConnected()) + return null; + try { + return getImpl().getInetAddress(); + } catch (SocketException e) { + } + return null; + } + + /** + * Gets the local address to which the socket is bound. + * <p> + * If there is a security manager set, its {@code checkConnect} method is + * called with the local address and {@code -1} as its arguments to see + * if the operation is allowed. If the operation is not allowed, + * the {@link InetAddress#getLoopbackAddress loopback} address is returned. + * + * @return the local address to which the socket is bound, + * the loopback address if denied by the security manager, or + * the wildcard address if the socket is closed or not bound yet. + * @since JDK1.1 + * + * @see SecurityManager#checkConnect + */ + public InetAddress getLocalAddress() { + // This is for backward compatibility + if (!isBound()) + return InetAddress.anyLocalAddress(); + InetAddress in = null; + try { + in = (InetAddress) getImpl().getOption(SocketOptions.SO_BINDADDR); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkConnect(in.getHostAddress(), -1); + if (in.isAnyLocalAddress()) { + in = InetAddress.anyLocalAddress(); + } + } catch (SecurityException e) { + in = InetAddress.getLoopbackAddress(); + } catch (Exception e) { + in = InetAddress.anyLocalAddress(); // "0.0.0.0" + } + return in; + } + + /** + * Returns the remote port number to which this socket is connected. + * <p> + * If the socket was connected prior to being {@link #close closed}, + * then this method will continue to return the connected port number + * after the socket is closed. + * + * @return the remote port number to which this socket is connected, or + * 0 if the socket is not connected yet. + */ + public int getPort() { + if (!isConnected()) + return 0; + try { + return getImpl().getPort(); + } catch (SocketException e) { + // Shouldn't happen as we're connected + } + return -1; + } + + /** + * Returns the local port number to which this socket is bound. + * <p> + * If the socket was bound prior to being {@link #close closed}, + * then this method will continue to return the local port number + * after the socket is closed. + * + * @return the local port number to which this socket is bound or -1 + * if the socket is not bound yet. + */ + public int getLocalPort() { + if (!isBound()) + return -1; + try { + return getImpl().getLocalPort(); + } catch(SocketException e) { + // shouldn't happen as we're bound + } + return -1; + } + + /** + * Returns the address of the endpoint this socket is connected to, or + * {@code null} if it is unconnected. + * <p> + * If the socket was connected prior to being {@link #close closed}, + * then this method will continue to return the connected address + * after the socket is closed. + * + + * @return a {@code SocketAddress} representing the remote endpoint of this + * socket, or {@code null} if it is not connected yet. + * @see #getInetAddress() + * @see #getPort() + * @see #connect(SocketAddress, int) + * @see #connect(SocketAddress) + * @since 1.4 + */ + public SocketAddress getRemoteSocketAddress() { + if (!isConnected()) + return null; + return new InetSocketAddress(getInetAddress(), getPort()); + } + + /** + * Returns the address of the endpoint this socket is bound to. + * <p> + * If a socket bound to an endpoint represented by an + * {@code InetSocketAddress } is {@link #close closed}, + * then this method will continue to return an {@code InetSocketAddress} + * after the socket is closed. In that case the returned + * {@code InetSocketAddress}'s address is the + * {@link InetAddress#isAnyLocalAddress wildcard} address + * and its port is the local port that it was bound to. + * <p> + * If there is a security manager set, its {@code checkConnect} method is + * called with the local address and {@code -1} as its arguments to see + * if the operation is allowed. If the operation is not allowed, + * a {@code SocketAddress} representing the + * {@link InetAddress#getLoopbackAddress loopback} address and the local + * port to which this socket is bound is returned. + * + * @return a {@code SocketAddress} representing the local endpoint of + * this socket, or a {@code SocketAddress} representing the + * loopback address if denied by the security manager, or + * {@code null} if the socket is not bound yet. + * + * @see #getLocalAddress() + * @see #getLocalPort() + * @see #bind(SocketAddress) + * @see SecurityManager#checkConnect + * @since 1.4 + */ + + public SocketAddress getLocalSocketAddress() { + if (!isBound()) + return null; + return new InetSocketAddress(getLocalAddress(), getLocalPort()); + } + + /** + * Returns the unique {@link java.nio.channels.SocketChannel SocketChannel} + * object associated with this socket, if any. + * + * <p> A socket will have a channel if, and only if, the channel itself was + * created via the {@link java.nio.channels.SocketChannel#open + * SocketChannel.open} or {@link + * java.nio.channels.ServerSocketChannel#accept ServerSocketChannel.accept} + * methods. + * + * @return the socket channel associated with this socket, + * or {@code null} if this socket was not created + * for a channel + * + * @since 1.4 + * @spec JSR-51 + */ + public SocketChannel getChannel() { + return null; + } + + /** + * Returns an input stream for this socket. + * + * <p> If this socket has an associated channel then the resulting input + * stream delegates all of its operations to the channel. If the channel + * is in non-blocking mode then the input stream's {@code read} operations + * will throw an {@link java.nio.channels.IllegalBlockingModeException}. + * + * <p>Under abnormal conditions the underlying connection may be + * broken by the remote host or the network software (for example + * a connection reset in the case of TCP connections). When a + * broken connection is detected by the network software the + * following applies to the returned input stream :- + * + * <ul> + * + * <li><p>The network software may discard bytes that are buffered + * by the socket. Bytes that aren't discarded by the network + * software can be read using {@link java.io.InputStream#read read}. + * + * <li><p>If there are no bytes buffered on the socket, or all + * buffered bytes have been consumed by + * {@link java.io.InputStream#read read}, then all subsequent + * calls to {@link java.io.InputStream#read read} will throw an + * {@link java.io.IOException IOException}. + * + * <li><p>If there are no bytes buffered on the socket, and the + * socket has not been closed using {@link #close close}, then + * {@link java.io.InputStream#available available} will + * return {@code 0}. + * + * </ul> + * + * <p> Closing the returned {@link java.io.InputStream InputStream} + * will close the associated socket. + * + * @return an input stream for reading bytes from this socket. + * @exception IOException if an I/O error occurs when creating the + * input stream, the socket is closed, the socket is + * not connected, or the socket input has been shutdown + * using {@link #shutdownInput()} + * + * @revised 1.4 + * @spec JSR-51 + */ + public InputStream getInputStream() throws IOException { + if (isClosed()) + throw new SocketException("Socket is closed"); + if (!isConnected()) + throw new SocketException("Socket is not connected"); + if (isInputShutdown()) + throw new SocketException("Socket input is shutdown"); + final Socket s = this; + InputStream is = null; + try { + is = AccessController.doPrivileged( + new PrivilegedExceptionAction<InputStream>() { + public InputStream run() throws IOException { + return impl.getInputStream(); + } + }); + } catch (java.security.PrivilegedActionException e) { + throw (IOException) e.getException(); + } + return is; + } + + /** + * Returns an output stream for this socket. + * + * <p> If this socket has an associated channel then the resulting output + * stream delegates all of its operations to the channel. If the channel + * is in non-blocking mode then the output stream's {@code write} + * operations will throw an {@link + * java.nio.channels.IllegalBlockingModeException}. + * + * <p> Closing the returned {@link java.io.OutputStream OutputStream} + * will close the associated socket. + * + * @return an output stream for writing bytes to this socket. + * @exception IOException if an I/O error occurs when creating the + * output stream or if the socket is not connected. + * @revised 1.4 + * @spec JSR-51 + */ + public OutputStream getOutputStream() throws IOException { + if (isClosed()) + throw new SocketException("Socket is closed"); + if (!isConnected()) + throw new SocketException("Socket is not connected"); + if (isOutputShutdown()) + throw new SocketException("Socket output is shutdown"); + final Socket s = this; + OutputStream os = null; + try { + os = AccessController.doPrivileged( + new PrivilegedExceptionAction<OutputStream>() { + public OutputStream run() throws IOException { + return impl.getOutputStream(); + } + }); + } catch (java.security.PrivilegedActionException e) { + throw (IOException) e.getException(); + } + return os; + } + + /** + * Enable/disable {@link SocketOptions#TCP_NODELAY TCP_NODELAY} + * (disable/enable Nagle's algorithm). + * + * @param on {@code true} to enable TCP_NODELAY, + * {@code false} to disable. + * + * @exception SocketException if there is an error + * in the underlying protocol, such as a TCP error. + * + * @since JDK1.1 + * + * @see #getTcpNoDelay() + */ + public void setTcpNoDelay(boolean on) throws SocketException { + if (isClosed()) + throw new SocketException("Socket is closed"); + getImpl().setOption(SocketOptions.TCP_NODELAY, Boolean.valueOf(on)); + } + + /** + * Tests if {@link SocketOptions#TCP_NODELAY TCP_NODELAY} is enabled. + * + * @return a {@code boolean} indicating whether or not + * {@link SocketOptions#TCP_NODELAY TCP_NODELAY} is enabled. + * @exception SocketException if there is an error + * in the underlying protocol, such as a TCP error. + * @since JDK1.1 + * @see #setTcpNoDelay(boolean) + */ + public boolean getTcpNoDelay() throws SocketException { + if (isClosed()) + throw new SocketException("Socket is closed"); + return ((Boolean) getImpl().getOption(SocketOptions.TCP_NODELAY)).booleanValue(); + } + + /** + * Enable/disable {@link SocketOptions#SO_LINGER SO_LINGER} with the + * specified linger time in seconds. The maximum timeout value is platform + * specific. + * + * The setting only affects socket close. + * + * @param on whether or not to linger on. + * @param linger how long to linger for, if on is true. + * @exception SocketException if there is an error + * in the underlying protocol, such as a TCP error. + * @exception IllegalArgumentException if the linger value is negative. + * @since JDK1.1 + * @see #getSoLinger() + */ + public void setSoLinger(boolean on, int linger) throws SocketException { + if (isClosed()) + throw new SocketException("Socket is closed"); + if (!on) { + getImpl().setOption(SocketOptions.SO_LINGER, new Boolean(on)); + } else { + if (linger < 0) { + throw new IllegalArgumentException("invalid value for SO_LINGER"); + } + if (linger > 65535) + linger = 65535; + getImpl().setOption(SocketOptions.SO_LINGER, new Integer(linger)); + } + } + + /** + * Returns setting for {@link SocketOptions#SO_LINGER SO_LINGER}. + * -1 returns implies that the + * option is disabled. + * + * The setting only affects socket close. + * + * @return the setting for {@link SocketOptions#SO_LINGER SO_LINGER}. + * @exception SocketException if there is an error + * in the underlying protocol, such as a TCP error. + * @since JDK1.1 + * @see #setSoLinger(boolean, int) + */ + public int getSoLinger() throws SocketException { + if (isClosed()) + throw new SocketException("Socket is closed"); + Object o = getImpl().getOption(SocketOptions.SO_LINGER); + if (o instanceof Integer) { + return ((Integer) o).intValue(); + } else { + return -1; + } + } + + /** + * Send one byte of urgent data on the socket. The byte to be sent is the lowest eight + * bits of the data parameter. The urgent byte is + * sent after any preceding writes to the socket OutputStream + * and before any future writes to the OutputStream. + * @param data The byte of data to send + * @exception IOException if there is an error + * sending the data. + * @since 1.4 + */ + public void sendUrgentData (int data) throws IOException { + // Android-changed: If the socket is closed, sendUrgentData should not create a new impl. + // Fail early to avoid leaking resources. + // http://b/31818400 + if (isClosed()) { + throw new SocketException("Socket is closed"); + } + + if (!getImpl().supportsUrgentData ()) { + throw new SocketException ("Urgent data not supported"); + } + getImpl().sendUrgentData (data); + } + + /** + * Enable/disable {@link SocketOptions#SO_OOBINLINE SO_OOBINLINE} + * (receipt of TCP urgent data) + * + * By default, this option is disabled and TCP urgent data received on a + * socket is silently discarded. If the user wishes to receive urgent data, then + * this option must be enabled. When enabled, urgent data is received + * inline with normal data. + * <p> + * Note, only limited support is provided for handling incoming urgent + * data. In particular, no notification of incoming urgent data is provided + * and there is no capability to distinguish between normal data and urgent + * data unless provided by a higher level protocol. + * + * @param on {@code true} to enable + * {@link SocketOptions#SO_OOBINLINE SO_OOBINLINE}, + * {@code false} to disable. + * + * @exception SocketException if there is an error + * in the underlying protocol, such as a TCP error. + * + * @since 1.4 + * + * @see #getOOBInline() + */ + public void setOOBInline(boolean on) throws SocketException { + if (isClosed()) + throw new SocketException("Socket is closed"); + getImpl().setOption(SocketOptions.SO_OOBINLINE, Boolean.valueOf(on)); + } + + /** + * Tests if {@link SocketOptions#SO_OOBINLINE SO_OOBINLINE} is enabled. + * + * @return a {@code boolean} indicating whether or not + * {@link SocketOptions#SO_OOBINLINE SO_OOBINLINE}is enabled. + * + * @exception SocketException if there is an error + * in the underlying protocol, such as a TCP error. + * @since 1.4 + * @see #setOOBInline(boolean) + */ + public boolean getOOBInline() throws SocketException { + if (isClosed()) + throw new SocketException("Socket is closed"); + return ((Boolean) getImpl().getOption(SocketOptions.SO_OOBINLINE)).booleanValue(); + } + + /** + * Enable/disable {@link SocketOptions#SO_TIMEOUT SO_TIMEOUT} + * with the specified timeout, in milliseconds. With this option set + * to a non-zero timeout, a read() call on the InputStream associated with + * this Socket will block for only this amount of time. If the timeout + * expires, a <B>java.net.SocketTimeoutException</B> is raised, though the + * Socket is still valid. The option <B>must</B> be enabled + * prior to entering the blocking operation to have effect. The + * timeout must be {@code > 0}. + * A timeout of zero is interpreted as an infinite timeout. + * + * @param timeout the specified timeout, in milliseconds. + * @exception SocketException if there is an error + * in the underlying protocol, such as a TCP error. + * @since JDK 1.1 + * @see #getSoTimeout() + */ + public synchronized void setSoTimeout(int timeout) throws SocketException { + if (isClosed()) + throw new SocketException("Socket is closed"); + if (timeout < 0) + throw new IllegalArgumentException("timeout can't be negative"); + + getImpl().setOption(SocketOptions.SO_TIMEOUT, new Integer(timeout)); + } + + /** + * Returns setting for {@link SocketOptions#SO_TIMEOUT SO_TIMEOUT}. + * 0 returns implies that the option is disabled (i.e., timeout of infinity). + * + * @return the setting for {@link SocketOptions#SO_TIMEOUT SO_TIMEOUT} + * @exception SocketException if there is an error + * in the underlying protocol, such as a TCP error. + * + * @since JDK1.1 + * @see #setSoTimeout(int) + */ + public synchronized int getSoTimeout() throws SocketException { + if (isClosed()) + throw new SocketException("Socket is closed"); + Object o = getImpl().getOption(SocketOptions.SO_TIMEOUT); + /* extra type safety */ + if (o instanceof Integer) { + return ((Integer) o).intValue(); + } else { + return 0; + } + } + + /** + * Sets the {@link SocketOptions#SO_SNDBUF SO_SNDBUF} option to the + * specified value for this {@code Socket}. + * The {@link SocketOptions#SO_SNDBUF SO_SNDBUF} option is used by the + * platform's networking code as a hint for the size to set the underlying + * network I/O buffers. + * + * <p>Because {@link SocketOptions#SO_SNDBUF SO_SNDBUF} is a hint, + * applications that want to verify what size the buffers were set to + * should call {@link #getSendBufferSize()}. + * + * @exception SocketException if there is an error + * in the underlying protocol, such as a TCP error. + * + * @param size the size to which to set the send buffer + * size. This value must be greater than 0. + * + * @exception IllegalArgumentException if the + * value is 0 or is negative. + * + * @see #getSendBufferSize() + * @since 1.2 + */ + public synchronized void setSendBufferSize(int size) + throws SocketException{ + if (!(size > 0)) { + throw new IllegalArgumentException("negative send size"); + } + if (isClosed()) + throw new SocketException("Socket is closed"); + getImpl().setOption(SocketOptions.SO_SNDBUF, new Integer(size)); + } + + /** + * Get value of the {@link SocketOptions#SO_SNDBUF SO_SNDBUF} option + * for this {@code Socket}, that is the buffer size used by the platform + * for output on this {@code Socket}. + * @return the value of the {@link SocketOptions#SO_SNDBUF SO_SNDBUF} + * option for this {@code Socket}. + * + * @exception SocketException if there is an error + * in the underlying protocol, such as a TCP error. + * + * @see #setSendBufferSize(int) + * @since 1.2 + */ + public synchronized int getSendBufferSize() throws SocketException { + if (isClosed()) + throw new SocketException("Socket is closed"); + int result = 0; + Object o = getImpl().getOption(SocketOptions.SO_SNDBUF); + if (o instanceof Integer) { + result = ((Integer)o).intValue(); + } + return result; + } + + /** + * Sets the {@link SocketOptions#SO_RCVBUF SO_RCVBUF} option to the + * specified value for this {@code Socket}. The + * {@link SocketOptions#SO_RCVBUF SO_RCVBUF} option is + * used by the platform's networking code as a hint for the size to set + * the underlying network I/O buffers. + * + * <p>Increasing the receive buffer size can increase the performance of + * network I/O for high-volume connection, while decreasing it can + * help reduce the backlog of incoming data. + * + * <p>Because {@link SocketOptions#SO_RCVBUF SO_RCVBUF} is a hint, + * applications that want to verify what size the buffers were set to + * should call {@link #getReceiveBufferSize()}. + * + * <p>The value of {@link SocketOptions#SO_RCVBUF SO_RCVBUF} is also used + * to set the TCP receive window that is advertized to the remote peer. + * Generally, the window size can be modified at any time when a socket is + * connected. However, if a receive window larger than 64K is required then + * this must be requested <B>before</B> the socket is connected to the + * remote peer. There are two cases to be aware of: + * <ol> + * <li>For sockets accepted from a ServerSocket, this must be done by calling + * {@link ServerSocket#setReceiveBufferSize(int)} before the ServerSocket + * is bound to a local address.<p></li> + * <li>For client sockets, setReceiveBufferSize() must be called before + * connecting the socket to its remote peer.</li></ol> + * @param size the size to which to set the receive buffer + * size. This value must be greater than 0. + * + * @exception IllegalArgumentException if the value is 0 or is + * negative. + * + * @exception SocketException if there is an error + * in the underlying protocol, such as a TCP error. + * + * @see #getReceiveBufferSize() + * @see ServerSocket#setReceiveBufferSize(int) + * @since 1.2 + */ + public synchronized void setReceiveBufferSize(int size) + throws SocketException{ + if (size <= 0) { + throw new IllegalArgumentException("invalid receive size"); + } + if (isClosed()) + throw new SocketException("Socket is closed"); + getImpl().setOption(SocketOptions.SO_RCVBUF, new Integer(size)); + } + + /** + * Gets the value of the {@link SocketOptions#SO_RCVBUF SO_RCVBUF} option + * for this {@code Socket}, that is the buffer size used by the platform + * for input on this {@code Socket}. + * + * @return the value of the {@link SocketOptions#SO_RCVBUF SO_RCVBUF} + * option for this {@code Socket}. + * @exception SocketException if there is an error + * in the underlying protocol, such as a TCP error. + * @see #setReceiveBufferSize(int) + * @since 1.2 + */ + public synchronized int getReceiveBufferSize() + throws SocketException{ + if (isClosed()) + throw new SocketException("Socket is closed"); + int result = 0; + Object o = getImpl().getOption(SocketOptions.SO_RCVBUF); + if (o instanceof Integer) { + result = ((Integer)o).intValue(); + } + return result; + } + + /** + * Enable/disable {@link SocketOptions#SO_KEEPALIVE SO_KEEPALIVE}. + * + * @param on whether or not to have socket keep alive turned on. + * @exception SocketException if there is an error + * in the underlying protocol, such as a TCP error. + * @since 1.3 + * @see #getKeepAlive() + */ + public void setKeepAlive(boolean on) throws SocketException { + if (isClosed()) + throw new SocketException("Socket is closed"); + getImpl().setOption(SocketOptions.SO_KEEPALIVE, Boolean.valueOf(on)); + } + + /** + * Tests if {@link SocketOptions#SO_KEEPALIVE SO_KEEPALIVE} is enabled. + * + * @return a {@code boolean} indicating whether or not + * {@link SocketOptions#SO_KEEPALIVE SO_KEEPALIVE} is enabled. + * @exception SocketException if there is an error + * in the underlying protocol, such as a TCP error. + * @since 1.3 + * @see #setKeepAlive(boolean) + */ + public boolean getKeepAlive() throws SocketException { + if (isClosed()) + throw new SocketException("Socket is closed"); + return ((Boolean) getImpl().getOption(SocketOptions.SO_KEEPALIVE)).booleanValue(); + } + + /** + * Sets traffic class or type-of-service octet in the IP + * header for packets sent from this Socket. + * As the underlying network implementation may ignore this + * value applications should consider it a hint. + * + * <P> The tc <B>must</B> be in the range {@code 0 <= tc <= + * 255} or an IllegalArgumentException will be thrown. + * <p>Notes: + * <p>For Internet Protocol v4 the value consists of an + * {@code integer}, the least significant 8 bits of which + * represent the value of the TOS octet in IP packets sent by + * the socket. + * RFC 1349 defines the TOS values as follows: + * + * <UL> + * <LI><CODE>IPTOS_LOWCOST (0x02)</CODE></LI> + * <LI><CODE>IPTOS_RELIABILITY (0x04)</CODE></LI> + * <LI><CODE>IPTOS_THROUGHPUT (0x08)</CODE></LI> + * <LI><CODE>IPTOS_LOWDELAY (0x10)</CODE></LI> + * </UL> + * The last low order bit is always ignored as this + * corresponds to the MBZ (must be zero) bit. + * <p> + * Setting bits in the precedence field may result in a + * SocketException indicating that the operation is not + * permitted. + * <p> + * As RFC 1122 section 4.2.4.2 indicates, a compliant TCP + * implementation should, but is not required to, let application + * change the TOS field during the lifetime of a connection. + * So whether the type-of-service field can be changed after the + * TCP connection has been established depends on the implementation + * in the underlying platform. Applications should not assume that + * they can change the TOS field after the connection. + * <p> + * For Internet Protocol v6 {@code tc} is the value that + * would be placed into the sin6_flowinfo field of the IP header. + * + * @param tc an {@code int} value for the bitset. + * @throws SocketException if there is an error setting the + * traffic class or type-of-service + * @since 1.4 + * @see #getTrafficClass + * @see SocketOptions#IP_TOS + */ + public void setTrafficClass(int tc) throws SocketException { + if (tc < 0 || tc > 255) + throw new IllegalArgumentException("tc is not in range 0 -- 255"); + + if (isClosed()) + throw new SocketException("Socket is closed"); + try { + getImpl().setOption(SocketOptions.IP_TOS, tc); + } catch (SocketException se) { + // not supported if socket already connected + // Solaris returns error in such cases + if(!isConnected()) + throw se; + } + } + + /** + * Gets traffic class or type-of-service in the IP header + * for packets sent from this Socket + * <p> + * As the underlying network implementation may ignore the + * traffic class or type-of-service set using {@link #setTrafficClass(int)} + * this method may return a different value than was previously + * set using the {@link #setTrafficClass(int)} method on this Socket. + * + * @return the traffic class or type-of-service already set + * @throws SocketException if there is an error obtaining the + * traffic class or type-of-service value. + * @since 1.4 + * @see #setTrafficClass(int) + * @see SocketOptions#IP_TOS + */ + public int getTrafficClass() throws SocketException { + // Android-changed: throw SocketException if the socket is already closed. http://b/31818400 + if (isClosed()) { + throw new SocketException("Socket is closed"); + } + + return ((Integer) (getImpl().getOption(SocketOptions.IP_TOS))).intValue(); + } + + /** + * Enable/disable the {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} + * socket option. + * <p> + * When a TCP connection is closed the connection may remain + * in a timeout state for a period of time after the connection + * is closed (typically known as the {@code TIME_WAIT} state + * or {@code 2MSL} wait state). + * For applications using a well known socket address or port + * it may not be possible to bind a socket to the required + * {@code SocketAddress} if there is a connection in the + * timeout state involving the socket address or port. + * <p> + * Enabling {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} + * prior to binding the socket using {@link #bind(SocketAddress)} allows + * the socket to be bound even though a previous connection is in a timeout + * state. + * <p> + * When a {@code Socket} is created the initial setting + * of {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} is disabled. + * <p> + * The behaviour when {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} is + * enabled or disabled after a socket is bound (See {@link #isBound()}) + * is not defined. + * + * @param on whether to enable or disable the socket option + * @exception SocketException if an error occurs enabling or + * disabling the {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} + * socket option, or the socket is closed. + * @since 1.4 + * @see #getReuseAddress() + * @see #bind(SocketAddress) + * @see #isClosed() + * @see #isBound() + */ + public void setReuseAddress(boolean on) throws SocketException { + if (isClosed()) + throw new SocketException("Socket is closed"); + getImpl().setOption(SocketOptions.SO_REUSEADDR, Boolean.valueOf(on)); + } + + /** + * Tests if {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} is enabled. + * + * @return a {@code boolean} indicating whether or not + * {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} is enabled. + * @exception SocketException if there is an error + * in the underlying protocol, such as a TCP error. + * @since 1.4 + * @see #setReuseAddress(boolean) + */ + public boolean getReuseAddress() throws SocketException { + if (isClosed()) + throw new SocketException("Socket is closed"); + return ((Boolean) (getImpl().getOption(SocketOptions.SO_REUSEADDR))).booleanValue(); + } + + /** + * Closes this socket. + * <p> + * Any thread currently blocked in an I/O operation upon this socket + * will throw a {@link SocketException}. + * <p> + * Once a socket has been closed, it is not available for further networking + * use (i.e. can't be reconnected or rebound). A new socket needs to be + * created. + * + * <p> Closing this socket will also close the socket's + * {@link java.io.InputStream InputStream} and + * {@link java.io.OutputStream OutputStream}. + * + * <p> If this socket has an associated channel then the channel is closed + * as well. + * + * @exception IOException if an I/O error occurs when closing this socket. + * @revised 1.4 + * @spec JSR-51 + * @see #isClosed + */ + public synchronized void close() throws IOException { + synchronized(closeLock) { + if (isClosed()) + return; + if (created) + impl.close(); + closed = true; + } + } + + /** + * Places the input stream for this socket at "end of stream". + * Any data sent to the input stream side of the socket is acknowledged + * and then silently discarded. + * <p> + * If you read from a socket input stream after invoking this method on the + * socket, the stream's {@code available} method will return 0, and its + * {@code read} methods will return {@code -1} (end of stream). + * + * @exception IOException if an I/O error occurs when shutting down this + * socket. + * + * @since 1.3 + * @see java.net.Socket#shutdownOutput() + * @see java.net.Socket#close() + * @see java.net.Socket#setSoLinger(boolean, int) + * @see #isInputShutdown + */ + public void shutdownInput() throws IOException + { + if (isClosed()) + throw new SocketException("Socket is closed"); + if (!isConnected()) + throw new SocketException("Socket is not connected"); + if (isInputShutdown()) + throw new SocketException("Socket input is already shutdown"); + getImpl().shutdownInput(); + shutIn = true; + } + + /** + * Disables the output stream for this socket. + * For a TCP socket, any previously written data will be sent + * followed by TCP's normal connection termination sequence. + * + * If you write to a socket output stream after invoking + * shutdownOutput() on the socket, the stream will throw + * an IOException. + * + * @exception IOException if an I/O error occurs when shutting down this + * socket. + * + * @since 1.3 + * @see java.net.Socket#shutdownInput() + * @see java.net.Socket#close() + * @see java.net.Socket#setSoLinger(boolean, int) + * @see #isOutputShutdown + */ + public void shutdownOutput() throws IOException + { + if (isClosed()) + throw new SocketException("Socket is closed"); + if (!isConnected()) + throw new SocketException("Socket is not connected"); + if (isOutputShutdown()) + throw new SocketException("Socket output is already shutdown"); + getImpl().shutdownOutput(); + shutOut = true; + } + + /** + * Converts this socket to a {@code String}. + * + * @return a string representation of this socket. + */ + public String toString() { + try { + // Android-changed: change localport to localPort, and addr to address. + if (isConnected()) + return "Socket[address=" + getImpl().getInetAddress() + + ",port=" + getImpl().getPort() + + ",localPort=" + getImpl().getLocalPort() + "]"; + } catch (SocketException e) { + } + return "Socket[unconnected]"; + } + + /** + * Returns the connection state of the socket. + * <p> + * Note: Closing a socket doesn't clear its connection state, which means + * this method will return {@code true} for a closed socket + * (see {@link #isClosed()}) if it was successfuly connected prior + * to being closed. + * + * @return true if the socket was successfuly connected to a server + * @since 1.4 + */ + public boolean isConnected() { + // Before 1.3 Sockets were always connected during creation + return connected || oldImpl; + } + + /** + * Returns the binding state of the socket. + * <p> + * Note: Closing a socket doesn't clear its binding state, which means + * this method will return {@code true} for a closed socket + * (see {@link #isClosed()}) if it was successfuly bound prior + * to being closed. + * + * @return true if the socket was successfuly bound to an address + * @since 1.4 + * @see #bind + */ + public boolean isBound() { + // Before 1.3 Sockets were always bound during creation + return bound || oldImpl; + } + + /** + * Returns the closed state of the socket. + * + * @return true if the socket has been closed + * @since 1.4 + * @see #close + */ + public boolean isClosed() { + synchronized(closeLock) { + return closed; + } + } + + /** + * Returns whether the read-half of the socket connection is closed. + * + * @return true if the input of the socket has been shutdown + * @since 1.4 + * @see #shutdownInput + */ + public boolean isInputShutdown() { + return shutIn; + } + + /** + * Returns whether the write-half of the socket connection is closed. + * + * @return true if the output of the socket has been shutdown + * @since 1.4 + * @see #shutdownOutput + */ + public boolean isOutputShutdown() { + return shutOut; + } + + /** + * The factory for all client sockets. + */ + private static SocketImplFactory factory = null; + + /** + * Sets the client socket implementation factory for the + * application. The factory can be specified only once. + * <p> + * When an application creates a new client socket, the socket + * implementation factory's {@code createSocketImpl} method is + * called to create the actual socket implementation. + * <p> + * Passing {@code null} to the method is a no-op unless the factory + * was already set. + * <p>If there is a security manager, this method first calls + * the security manager's {@code checkSetFactory} method + * to ensure the operation is allowed. + * This could result in a SecurityException. + * + * @param fac the desired factory. + * @exception IOException if an I/O error occurs when setting the + * socket factory. + * @exception SocketException if the factory is already defined. + * @exception SecurityException if a security manager exists and its + * {@code checkSetFactory} method doesn't allow the operation. + * @see java.net.SocketImplFactory#createSocketImpl() + * @see SecurityManager#checkSetFactory + */ + public static synchronized void setSocketImplFactory(SocketImplFactory fac) + throws IOException + { + if (factory != null) { + throw new SocketException("factory already defined"); + } + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkSetFactory(); + } + factory = fac; + } + + /** + * Sets performance preferences for this socket. + * + * <p> Sockets use the TCP/IP protocol by default. Some implementations + * may offer alternative protocols which have different performance + * characteristics than TCP/IP. This method allows the application to + * express its own preferences as to how these tradeoffs should be made + * when the implementation chooses from the available protocols. + * + * <p> Performance preferences are described by three integers + * whose values indicate the relative importance of short connection time, + * low latency, and high bandwidth. The absolute values of the integers + * are irrelevant; in order to choose a protocol the values are simply + * compared, with larger values indicating stronger preferences. Negative + * values represent a lower priority than positive values. If the + * application prefers short connection time over both low latency and high + * bandwidth, for example, then it could invoke this method with the values + * {@code (1, 0, 0)}. If the application prefers high bandwidth above low + * latency, and low latency above short connection time, then it could + * invoke this method with the values {@code (0, 1, 2)}. + * + * <p> Invoking this method after this socket has been connected + * will have no effect. + * + * @param connectionTime + * An {@code int} expressing the relative importance of a short + * connection time + * + * @param latency + * An {@code int} expressing the relative importance of low + * latency + * + * @param bandwidth + * An {@code int} expressing the relative importance of high + * bandwidth + * + * @since 1.5 + */ + public void setPerformancePreferences(int connectionTime, + int latency, + int bandwidth) + { + /* Not implemented yet */ + } + + // Android-added: getFileDescriptor$() method for testing and internal use. + /** + * @hide internal use only + */ + public FileDescriptor getFileDescriptor$() { + return impl.getFileDescriptor(); + } +}
diff --git a/java/net/SocketAddress.java b/java/net/SocketAddress.java new file mode 100644 index 0000000..cfb014b --- /dev/null +++ b/java/net/SocketAddress.java
@@ -0,0 +1,45 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.net; + + +/** + * + * This class represents a Socket Address with no protocol attachment. + * As an abstract class, it is meant to be subclassed with a specific, + * protocol dependent, implementation. + * <p> + * It provides an immutable object used by sockets for binding, connecting, or + * as returned values. + * + * @see java.net.Socket + * @see java.net.ServerSocket + * @since 1.4 + */ +public abstract class SocketAddress implements java.io.Serializable { + + static final long serialVersionUID = 5215720748342549866L; + +}
diff --git a/java/net/SocketException.java b/java/net/SocketException.java new file mode 100644 index 0000000..64ae771 --- /dev/null +++ b/java/net/SocketException.java
@@ -0,0 +1,68 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.IOException; + +/** + * Thrown to indicate that there is an error creating or accessing a Socket. + * + * @author Jonathan Payne + * @since JDK1.0 + */ +public +class SocketException extends IOException { + private static final long serialVersionUID = -5935874303556886934L; + + /** + * Constructs a new {@code SocketException} with the + * specified detail message. + * + * @param msg the detail message. + */ + public SocketException(String msg) { + super(msg); + } + + /** + * Constructs a new {@code SocketException} with no detail message. + */ + public SocketException() { + } + + // BEGIN Android-added: SocketException ctor with cause for internal use. + /** @hide */ + public SocketException(Throwable cause) { + super(cause); + } + + /** @hide */ + public SocketException(String msg, Throwable cause) { + super(msg, cause); + } + // END Android-added: SocketException ctor with cause for internal use. +}
diff --git a/java/net/SocketImpl.annotated.java b/java/net/SocketImpl.annotated.java new file mode 100644 index 0000000..2146d2f --- /dev/null +++ b/java/net/SocketImpl.annotated.java
@@ -0,0 +1,90 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package java.net; + +import java.io.IOException; + +@SuppressWarnings({"unchecked", "deprecation", "all"}) +public abstract class SocketImpl implements java.net.SocketOptions { + +public SocketImpl() { throw new RuntimeException("Stub!"); } + +protected abstract void create(boolean stream) throws java.io.IOException; + +protected abstract void connect(java.lang.String host, int port) throws java.io.IOException; + +protected abstract void connect(java.net.InetAddress address, int port) throws java.io.IOException; + +protected abstract void connect(java.net.SocketAddress address, int timeout) throws java.io.IOException; + +protected abstract void bind(java.net.InetAddress host, int port) throws java.io.IOException; + +protected abstract void listen(int backlog) throws java.io.IOException; + +protected abstract void accept(java.net.SocketImpl s) throws java.io.IOException; + +protected abstract java.io.InputStream getInputStream() throws java.io.IOException; + +protected abstract java.io.OutputStream getOutputStream() throws java.io.IOException; + +protected abstract int available() throws java.io.IOException; + +protected abstract void close() throws java.io.IOException; + +protected void shutdownInput() throws java.io.IOException { throw new RuntimeException("Stub!"); } + +protected void shutdownOutput() throws java.io.IOException { throw new RuntimeException("Stub!"); } + +protected java.io.FileDescriptor getFileDescriptor() { throw new RuntimeException("Stub!"); } + [email protected] +public java.io.FileDescriptor getFD$() { throw new RuntimeException("Stub!"); } + +protected java.net.InetAddress getInetAddress() { throw new RuntimeException("Stub!"); } + +protected int getPort() { throw new RuntimeException("Stub!"); } + +protected boolean supportsUrgentData() { throw new RuntimeException("Stub!"); } + +protected abstract void sendUrgentData(int data) throws java.io.IOException; + +protected int getLocalPort() { throw new RuntimeException("Stub!"); } + +public java.lang.String toString() { throw new RuntimeException("Stub!"); } + +protected void setPerformancePreferences(int connectionTime, int latency, int bandwidth) { throw new RuntimeException("Stub!"); } + +protected java.net.InetAddress address; + +protected java.io.FileDescriptor fd; + +protected int localport; + +protected int port; +} +
diff --git a/java/net/SocketImpl.java b/java/net/SocketImpl.java new file mode 100644 index 0000000..ade2630 --- /dev/null +++ b/java/net/SocketImpl.java
@@ -0,0 +1,407 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.FileDescriptor; + +/** + * The abstract class {@code SocketImpl} is a common superclass + * of all classes that actually implement sockets. It is used to + * create both client and server sockets. + * <p> + * A "plain" socket implements these methods exactly as + * described, without attempting to go through a firewall or proxy. + * + * @author unascribed + * @since JDK1.0 + */ +public abstract class SocketImpl implements SocketOptions { + /** + * The actual Socket object. + */ + Socket socket = null; + ServerSocket serverSocket = null; + + /** + * The file descriptor object for this socket. + */ + protected FileDescriptor fd; + + /** + * The IP address of the remote end of this socket. + */ + protected InetAddress address; + + /** + * The port number on the remote host to which this socket is connected. + */ + protected int port; + + /** + * The local port number to which this socket is connected. + */ + protected int localport; + + /** + * Creates either a stream or a datagram socket. + * + * @param stream if {@code true}, create a stream socket; + * otherwise, create a datagram socket. + * @exception IOException if an I/O error occurs while creating the + * socket. + */ + protected abstract void create(boolean stream) throws IOException; + + /** + * Connects this socket to the specified port on the named host. + * + * @param host the name of the remote host. + * @param port the port number. + * @exception IOException if an I/O error occurs when connecting to the + * remote host. + */ + protected abstract void connect(String host, int port) throws IOException; + + /** + * Connects this socket to the specified port number on the specified host. + * + * @param address the IP address of the remote host. + * @param port the port number. + * @exception IOException if an I/O error occurs when attempting a + * connection. + */ + protected abstract void connect(InetAddress address, int port) throws IOException; + + /** + * Connects this socket to the specified port number on the specified host. + * A timeout of zero is interpreted as an infinite timeout. The connection + * will then block until established or an error occurs. + * + * @param address the Socket address of the remote host. + * @param timeout the timeout value, in milliseconds, or zero for no timeout. + * @exception IOException if an I/O error occurs when attempting a + * connection. + * @since 1.4 + */ + protected abstract void connect(SocketAddress address, int timeout) throws IOException; + + /** + * Binds this socket to the specified local IP address and port number. + * + * @param host an IP address that belongs to a local interface. + * @param port the port number. + * @exception IOException if an I/O error occurs when binding this socket. + */ + protected abstract void bind(InetAddress host, int port) throws IOException; + + /** + * Sets the maximum queue length for incoming connection indications + * (a request to connect) to the {@code count} argument. If a + * connection indication arrives when the queue is full, the + * connection is refused. + * + * @param backlog the maximum length of the queue. + * @exception IOException if an I/O error occurs when creating the queue. + */ + protected abstract void listen(int backlog) throws IOException; + + /** + * Accepts a connection. + * + * @param s the accepted connection. + * @exception IOException if an I/O error occurs when accepting the + * connection. + */ + protected abstract void accept(SocketImpl s) throws IOException; + + /** + * Returns an input stream for this socket. + * + * @return a stream for reading from this socket. + * @exception IOException if an I/O error occurs when creating the + * input stream. + */ + protected abstract InputStream getInputStream() throws IOException; + + /** + * Returns an output stream for this socket. + * + * @return an output stream for writing to this socket. + * @exception IOException if an I/O error occurs when creating the + * output stream. + */ + protected abstract OutputStream getOutputStream() throws IOException; + + /** + * Returns the number of bytes that can be read from this socket + * without blocking. + * + * @return the number of bytes that can be read from this socket + * without blocking. + * @exception IOException if an I/O error occurs when determining the + * number of bytes available. + */ + protected abstract int available() throws IOException; + + /** + * Closes this socket. + * + * @exception IOException if an I/O error occurs when closing this socket. + */ + protected abstract void close() throws IOException; + + /** + * Places the input stream for this socket at "end of stream". + * Any data sent to this socket is acknowledged and then + * silently discarded. + * + * If you read from a socket input stream after invoking this method on the + * socket, the stream's {@code available} method will return 0, and its + * {@code read} methods will return {@code -1} (end of stream). + * + * @exception IOException if an I/O error occurs when shutting down this + * socket. + * @see java.net.Socket#shutdownOutput() + * @see java.net.Socket#close() + * @see java.net.Socket#setSoLinger(boolean, int) + * @since 1.3 + */ + protected void shutdownInput() throws IOException { + throw new IOException("Method not implemented!"); + } + + /** + * Disables the output stream for this socket. + * For a TCP socket, any previously written data will be sent + * followed by TCP's normal connection termination sequence. + * + * If you write to a socket output stream after invoking + * shutdownOutput() on the socket, the stream will throw + * an IOException. + * + * @exception IOException if an I/O error occurs when shutting down this + * socket. + * @see java.net.Socket#shutdownInput() + * @see java.net.Socket#close() + * @see java.net.Socket#setSoLinger(boolean, int) + * @since 1.3 + */ + protected void shutdownOutput() throws IOException { + throw new IOException("Method not implemented!"); + } + + /** + * Returns the value of this socket's {@code fd} field. + * + * @return the value of this socket's {@code fd} field. + * @see java.net.SocketImpl#fd + */ + protected FileDescriptor getFileDescriptor() { + return fd; + } + + // Android-added: getFD$() for testing. + /** + * @hide used by java.nio tests + */ + public FileDescriptor getFD$() { + return fd; + } + + /** + * Returns the value of this socket's {@code address} field. + * + * @return the value of this socket's {@code address} field. + * @see java.net.SocketImpl#address + */ + protected InetAddress getInetAddress() { + return address; + } + + /** + * Returns the value of this socket's {@code port} field. + * + * @return the value of this socket's {@code port} field. + * @see java.net.SocketImpl#port + */ + protected int getPort() { + return port; + } + + /** + * Returns whether or not this SocketImpl supports sending + * urgent data. By default, false is returned + * unless the method is overridden in a sub-class + * + * @return true if urgent data supported + * @see java.net.SocketImpl#address + * @since 1.4 + */ + protected boolean supportsUrgentData () { + return false; // must be overridden in sub-class + } + + /** + * Send one byte of urgent data on the socket. + * The byte to be sent is the low eight bits of the parameter + * @param data The byte of data to send + * @exception IOException if there is an error + * sending the data. + * @since 1.4 + */ + protected abstract void sendUrgentData (int data) throws IOException; + + /** + * Returns the value of this socket's {@code localport} field. + * + * @return the value of this socket's {@code localport} field. + * @see java.net.SocketImpl#localport + */ + protected int getLocalPort() { + return localport; + } + + void setSocket(Socket soc) { + this.socket = soc; + } + + Socket getSocket() { + return socket; + } + + void setServerSocket(ServerSocket soc) { + this.serverSocket = soc; + } + + ServerSocket getServerSocket() { + return serverSocket; + } + + /** + * Returns the address and port of this socket as a {@code String}. + * + * @return a string representation of this socket. + */ + public String toString() { + return "Socket[addr=" + getInetAddress() + + ",port=" + getPort() + ",localport=" + getLocalPort() + "]"; + } + + void reset() throws IOException { + address = null; + port = 0; + localport = 0; + } + + /** + * Sets performance preferences for this socket. + * + * <p> Sockets use the TCP/IP protocol by default. Some implementations + * may offer alternative protocols which have different performance + * characteristics than TCP/IP. This method allows the application to + * express its own preferences as to how these tradeoffs should be made + * when the implementation chooses from the available protocols. + * + * <p> Performance preferences are described by three integers + * whose values indicate the relative importance of short connection time, + * low latency, and high bandwidth. The absolute values of the integers + * are irrelevant; in order to choose a protocol the values are simply + * compared, with larger values indicating stronger preferences. Negative + * values represent a lower priority than positive values. If the + * application prefers short connection time over both low latency and high + * bandwidth, for example, then it could invoke this method with the values + * {@code (1, 0, 0)}. If the application prefers high bandwidth above low + * latency, and low latency above short connection time, then it could + * invoke this method with the values {@code (0, 1, 2)}. + * + * By default, this method does nothing, unless it is overridden in a + * a sub-class. + * + * @param connectionTime + * An {@code int} expressing the relative importance of a short + * connection time + * + * @param latency + * An {@code int} expressing the relative importance of low + * latency + * + * @param bandwidth + * An {@code int} expressing the relative importance of high + * bandwidth + * + * @since 1.5 + */ + protected void setPerformancePreferences(int connectionTime, + int latency, + int bandwidth) + { + /* Not implemented yet */ + } + + <T> void setOption(SocketOption<T> name, T value) throws IOException { + if (name == StandardSocketOptions.SO_KEEPALIVE) { + setOption(SocketOptions.SO_KEEPALIVE, value); + } else if (name == StandardSocketOptions.SO_SNDBUF) { + setOption(SocketOptions.SO_SNDBUF, value); + } else if (name == StandardSocketOptions.SO_RCVBUF) { + setOption(SocketOptions.SO_RCVBUF, value); + } else if (name == StandardSocketOptions.SO_REUSEADDR) { + setOption(SocketOptions.SO_REUSEADDR, value); + } else if (name == StandardSocketOptions.SO_LINGER) { + setOption(SocketOptions.SO_LINGER, value); + } else if (name == StandardSocketOptions.IP_TOS) { + setOption(SocketOptions.IP_TOS, value); + } else if (name == StandardSocketOptions.TCP_NODELAY) { + setOption(SocketOptions.TCP_NODELAY, value); + } else { + throw new UnsupportedOperationException("unsupported option"); + } + } + + <T> T getOption(SocketOption<T> name) throws IOException { + if (name == StandardSocketOptions.SO_KEEPALIVE) { + return (T)getOption(SocketOptions.SO_KEEPALIVE); + } else if (name == StandardSocketOptions.SO_SNDBUF) { + return (T)getOption(SocketOptions.SO_SNDBUF); + } else if (name == StandardSocketOptions.SO_RCVBUF) { + return (T)getOption(SocketOptions.SO_RCVBUF); + } else if (name == StandardSocketOptions.SO_REUSEADDR) { + return (T)getOption(SocketOptions.SO_REUSEADDR); + } else if (name == StandardSocketOptions.SO_LINGER) { + return (T)getOption(SocketOptions.SO_LINGER); + } else if (name == StandardSocketOptions.IP_TOS) { + return (T)getOption(SocketOptions.IP_TOS); + } else if (name == StandardSocketOptions.TCP_NODELAY) { + return (T)getOption(SocketOptions.TCP_NODELAY); + } else { + throw new UnsupportedOperationException("unsupported option"); + } + } +}
diff --git a/java/net/SocketImplFactory.java b/java/net/SocketImplFactory.java new file mode 100644 index 0000000..7aa6363 --- /dev/null +++ b/java/net/SocketImplFactory.java
@@ -0,0 +1,48 @@ +/* + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +/** + * This interface defines a factory for socket implementations. It + * is used by the classes {@code Socket} and + * {@code ServerSocket} to create actual socket + * implementations. + * + * @author Arthur van Hoff + * @see java.net.Socket + * @see java.net.ServerSocket + * @since JDK1.0 + */ +public +interface SocketImplFactory { + /** + * Creates a new {@code SocketImpl} instance. + * + * @return a new instance of {@code SocketImpl}. + * @see java.net.SocketImpl + */ + SocketImpl createSocketImpl(); +}
diff --git a/java/net/SocketInputStream.java b/java/net/SocketInputStream.java new file mode 100644 index 0000000..8d0e0c5 --- /dev/null +++ b/java/net/SocketInputStream.java
@@ -0,0 +1,305 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 1995, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.channels.FileChannel; + +import dalvik.system.BlockGuard; +import sun.net.ConnectionResetException; + +/** + * This stream extends FileInputStream to implement a + * SocketInputStream. Note that this class should <b>NOT</b> be + * public. + * + * @author Jonathan Payne + * @author Arthur van Hoff + */ +class SocketInputStream extends FileInputStream +{ + // Android-removed: Android doesn't need to call native init. + // static { + // init(); + //} + + private boolean eof; + private AbstractPlainSocketImpl impl = null; + private byte temp[]; + private Socket socket = null; + + /** + * Creates a new SocketInputStream. Can only be called + * by a Socket. This method needs to hang on to the owner Socket so + * that the fd will not be closed. + * @param impl the implemented socket input stream + */ + SocketInputStream(AbstractPlainSocketImpl impl) throws IOException { + super(impl.getFileDescriptor()); + this.impl = impl; + socket = impl.getSocket(); + } + + /** + * Returns the unique {@link java.nio.channels.FileChannel FileChannel} + * object associated with this file input stream.</p> + * + * The {@code getChannel} method of {@code SocketInputStream} + * returns {@code null} since it is a socket based stream.</p> + * + * @return the file channel associated with this file input stream + * + * @since 1.4 + * @spec JSR-51 + */ + public final FileChannel getChannel() { + return null; + } + + /** + * Reads into an array of bytes at the specified offset using + * the received socket primitive. + * @param fd the FileDescriptor + * @param b the buffer into which the data is read + * @param off the start offset of the data + * @param len the maximum number of bytes read + * @param timeout the read timeout in ms + * @return the actual number of bytes read, -1 is + * returned when the end of the stream is reached. + * @exception IOException If an I/O error has occurred. + */ + private native int socketRead0(FileDescriptor fd, + byte b[], int off, int len, + int timeout) + throws IOException; + + // wrap native call to allow instrumentation + /** + * Reads into an array of bytes at the specified offset using + * the received socket primitive. + * @param fd the FileDescriptor + * @param b the buffer into which the data is read + * @param off the start offset of the data + * @param len the maximum number of bytes read + * @param timeout the read timeout in ms + * @return the actual number of bytes read, -1 is + * returned when the end of the stream is reached. + * @exception IOException If an I/O error has occurred. + */ + private int socketRead(FileDescriptor fd, + byte b[], int off, int len, + int timeout) + throws IOException { + return socketRead0(fd, b, off, len, timeout); + } + + /** + * Reads into a byte array data from the socket. + * @param b the buffer into which the data is read + * @return the actual number of bytes read, -1 is + * returned when the end of the stream is reached. + * @exception IOException If an I/O error has occurred. + */ + public int read(byte b[]) throws IOException { + return read(b, 0, b.length); + } + + /** + * Reads into a byte array <i>b</i> at offset <i>off</i>, + * <i>length</i> bytes of data. + * @param b the buffer into which the data is read + * @param off the start offset of the data + * @param length the maximum number of bytes read + * @return the actual number of bytes read, -1 is + * returned when the end of the stream is reached. + * @exception IOException If an I/O error has occurred. + */ + public int read(byte b[], int off, int length) throws IOException { + return read(b, off, length, impl.getTimeout()); + } + + int read(byte b[], int off, int length, int timeout) throws IOException { + int n; + + // EOF already encountered + if (eof) { + return -1; + } + + // connection reset + if (impl.isConnectionReset()) { + throw new SocketException("Connection reset"); + } + + // bounds check + if (length <= 0 || off < 0 || length > b.length - off) { + if (length == 0) { + return 0; + } + throw new ArrayIndexOutOfBoundsException("length == " + length + + " off == " + off + " buffer length == " + b.length); + } + + boolean gotReset = false; + + // acquire file descriptor and do the read + FileDescriptor fd = impl.acquireFD(); + try { + // Android-added: Check BlockGuard policy in read(). + BlockGuard.getThreadPolicy().onNetwork(); + n = socketRead(fd, b, off, length, timeout); + if (n > 0) { + return n; + } + } catch (ConnectionResetException rstExc) { + gotReset = true; + } finally { + impl.releaseFD(); + } + + /* + * We receive a "connection reset" but there may be bytes still + * buffered on the socket + */ + if (gotReset) { + impl.setConnectionResetPending(); + impl.acquireFD(); + try { + n = socketRead(fd, b, off, length, timeout); + if (n > 0) { + return n; + } + } catch (ConnectionResetException rstExc) { + } finally { + impl.releaseFD(); + } + } + + /* + * If we get here we are at EOF, the socket has been closed, + * or the connection has been reset. + */ + if (impl.isClosedOrPending()) { + throw new SocketException("Socket closed"); + } + if (impl.isConnectionResetPending()) { + impl.setConnectionReset(); + } + if (impl.isConnectionReset()) { + throw new SocketException("Connection reset"); + } + eof = true; + return -1; + } + + /** + * Reads a single byte from the socket. + */ + public int read() throws IOException { + if (eof) { + return -1; + } + temp = new byte[1]; + int n = read(temp, 0, 1); + if (n <= 0) { + return -1; + } + return temp[0] & 0xff; + } + + /** + * Skips n bytes of input. + * @param numbytes the number of bytes to skip + * @return the actual number of bytes skipped. + * @exception IOException If an I/O error has occurred. + */ + public long skip(long numbytes) throws IOException { + if (numbytes <= 0) { + return 0; + } + long n = numbytes; + int buflen = (int) Math.min(1024, n); + byte data[] = new byte[buflen]; + while (n > 0) { + int r = read(data, 0, (int) Math.min((long) buflen, n)); + if (r < 0) { + break; + } + n -= r; + } + return numbytes - n; + } + + /** + * Returns the number of bytes that can be read without blocking. + * @return the number of immediately available bytes + */ + public int available() throws IOException { + // Android-changed: Bug fix, if eof == true, we must indicate that we + // have 0 bytes available. + if (eof) { + return 0; + } else { + return impl.available(); + } + } + + /** + * Closes the stream. + */ + private boolean closing = false; + public void close() throws IOException { + // Prevent recursion. See BugId 4484411 + if (closing) + return; + closing = true; + if (socket != null) { + if (!socket.isClosed()) + socket.close(); + } else + impl.close(); + closing = false; + } + + void setEOF(boolean eof) { + this.eof = eof; + } + + /** + * Overrides finalize, the fd is closed by the Socket. + */ + protected void finalize() {} + + // Android-removed: Android doesn't need native init. + /* + * Perform class load-time initializations. + * + private native static void init(); + */ +}
diff --git a/java/net/SocketOption.java b/java/net/SocketOption.java new file mode 100644 index 0000000..2ccf57f --- /dev/null +++ b/java/net/SocketOption.java
@@ -0,0 +1,59 @@ +/* + * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +/** + * A socket option associated with a socket. + * + * <p> In the {@link java.nio.channels channels} package, the {@link + * java.nio.channels.NetworkChannel} interface defines the {@link + * java.nio.channels.NetworkChannel#setOption(SocketOption,Object) setOption} + * and {@link java.nio.channels.NetworkChannel#getOption(SocketOption) getOption} + * methods to set and query the channel's socket options. + * + * @param <T> The type of the socket option value. + * + * @since 1.7 + * + * @see StandardSocketOptions + */ + +public interface SocketOption<T> { + + /** + * Returns the name of the socket option. + * + * @return the name of the socket option + */ + String name(); + + /** + * Returns the type of the socket option value. + * + * @return the type of the socket option value + */ + Class<T> type(); +}
diff --git a/java/net/SocketOptions.java b/java/net/SocketOptions.java new file mode 100644 index 0000000..7e1b0fc --- /dev/null +++ b/java/net/SocketOptions.java
@@ -0,0 +1,331 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.lang.annotation.Native; + +/** + * Interface of methods to get/set socket options. This interface is + * implemented by: <B>SocketImpl</B> and <B>DatagramSocketImpl</B>. + * Subclasses of these should override the methods + * of this interface in order to support their own options. + * <P> + * The methods and constants which specify options in this interface are + * for implementation only. If you're not subclassing SocketImpl or + * DatagramSocketImpl, <B>you won't use these directly.</B> There are + * type-safe methods to get/set each of these options in Socket, ServerSocket, + * DatagramSocket and MulticastSocket. + * <P> + * @author David Brown + */ + + +public interface SocketOptions { + + /** + * Enable/disable the option specified by <I>optID</I>. If the option + * is to be enabled, and it takes an option-specific "value", this is + * passed in <I>value</I>. The actual type of value is option-specific, + * and it is an error to pass something that isn't of the expected type: + * <BR><PRE> + * SocketImpl s; + * ... + * s.setOption(SO_LINGER, new Integer(10)); + * // OK - set SO_LINGER w/ timeout of 10 sec. + * s.setOption(SO_LINGER, new Double(10)); + * // ERROR - expects java.lang.Integer + *</PRE> + * If the requested option is binary, it can be set using this method by + * a java.lang.Boolean: + * <BR><PRE> + * s.setOption(TCP_NODELAY, new Boolean(true)); + * // OK - enables TCP_NODELAY, a binary option + * </PRE> + * <BR> + * Any option can be disabled using this method with a Boolean(false): + * <BR><PRE> + * s.setOption(TCP_NODELAY, new Boolean(false)); + * // OK - disables TCP_NODELAY + * s.setOption(SO_LINGER, new Boolean(false)); + * // OK - disables SO_LINGER + * </PRE> + * <BR> + * For an option that has a notion of on and off, and requires + * a non-boolean parameter, setting its value to anything other than + * <I>Boolean(false)</I> implicitly enables it. + * <BR> + * Throws SocketException if the option is unrecognized, + * the socket is closed, or some low-level error occurred + * <BR> + * @param optID identifies the option + * @param value the parameter of the socket option + * @throws SocketException if the option is unrecognized, + * the socket is closed, or some low-level error occurred + * @see #getOption(int) + */ + public void + setOption(int optID, Object value) throws SocketException; + + /** + * Fetch the value of an option. + * Binary options will return java.lang.Boolean(true) + * if enabled, java.lang.Boolean(false) if disabled, e.g.: + * <BR><PRE> + * SocketImpl s; + * ... + * Boolean noDelay = (Boolean)(s.getOption(TCP_NODELAY)); + * if (noDelay.booleanValue()) { + * // true if TCP_NODELAY is enabled... + * ... + * } + * </PRE> + * <P> + * For options that take a particular type as a parameter, + * getOption(int) will return the parameter's value, else + * it will return java.lang.Boolean(false): + * <PRE> + * Object o = s.getOption(SO_LINGER); + * if (o instanceof Integer) { + * System.out.print("Linger time is " + ((Integer)o).intValue()); + * } else { + * // the true type of o is java.lang.Boolean(false); + * } + * </PRE> + * + * @param optID an {@code int} identifying the option to fetch + * @return the value of the option + * @throws SocketException if the socket is closed + * @throws SocketException if <I>optID</I> is unknown along the + * protocol stack (including the SocketImpl) + * @see #setOption(int, java.lang.Object) + */ + public Object getOption(int optID) throws SocketException; + + /** + * The java-supported BSD-style options. + */ + + /** + * Disable Nagle's algorithm for this connection. Written data + * to the network is not buffered pending acknowledgement of + * previously written data. + *<P> + * Valid for TCP only: SocketImpl. + * + * @see Socket#setTcpNoDelay + * @see Socket#getTcpNoDelay + */ + + @Native public final static int TCP_NODELAY = 0x0001; + + /** + * Fetch the local address binding of a socket (this option cannot + * be "set" only "gotten", since sockets are bound at creation time, + * and so the locally bound address cannot be changed). The default local + * address of a socket is INADDR_ANY, meaning any local address on a + * multi-homed host. A multi-homed host can use this option to accept + * connections to only one of its addresses (in the case of a + * ServerSocket or DatagramSocket), or to specify its return address + * to the peer (for a Socket or DatagramSocket). The parameter of + * this option is an InetAddress. + * <P> + * This option <B>must</B> be specified in the constructor. + * <P> + * Valid for: SocketImpl, DatagramSocketImpl + * + * @see Socket#getLocalAddress + * @see DatagramSocket#getLocalAddress + */ + + @Native public final static int SO_BINDADDR = 0x000F; + + /** Sets SO_REUSEADDR for a socket. This is used only for MulticastSockets + * in java, and it is set by default for MulticastSockets. + * <P> + * Valid for: DatagramSocketImpl + */ + + @Native public final static int SO_REUSEADDR = 0x04; + + /** + * Sets SO_BROADCAST for a socket. This option enables and disables + * the ability of the process to send broadcast messages. It is supported + * for only datagram sockets and only on networks that support + * the concept of a broadcast message (e.g. Ethernet, token ring, etc.), + * and it is set by default for DatagramSockets. + * @since 1.4 + */ + + @Native public final static int SO_BROADCAST = 0x0020; + + /** Set which outgoing interface on which to send multicast packets. + * Useful on hosts with multiple network interfaces, where applications + * want to use other than the system default. Takes/returns an InetAddress. + * <P> + * Valid for Multicast: DatagramSocketImpl + * + * @see MulticastSocket#setInterface(InetAddress) + * @see MulticastSocket#getInterface() + */ + + @Native public final static int IP_MULTICAST_IF = 0x10; + + /** Same as above. This option is introduced so that the behaviour + * with IP_MULTICAST_IF will be kept the same as before, while + * this new option can support setting outgoing interfaces with either + * IPv4 and IPv6 addresses. + * + * NOTE: make sure there is no conflict with this + * @see MulticastSocket#setNetworkInterface(NetworkInterface) + * @see MulticastSocket#getNetworkInterface() + * @since 1.4 + */ + @Native public final static int IP_MULTICAST_IF2 = 0x1f; + + /** + * This option enables or disables local loopback of multicast datagrams. + * This option is enabled by default for Multicast Sockets. + * @since 1.4 + */ + + @Native public final static int IP_MULTICAST_LOOP = 0x12; + + /** + * This option sets the type-of-service or traffic class field + * in the IP header for a TCP or UDP socket. + * @since 1.4 + */ + + @Native public final static int IP_TOS = 0x3; + + /** + * Specify a linger-on-close timeout. This option disables/enables + * immediate return from a <B>close()</B> of a TCP Socket. Enabling + * this option with a non-zero Integer <I>timeout</I> means that a + * <B>close()</B> will block pending the transmission and acknowledgement + * of all data written to the peer, at which point the socket is closed + * <I>gracefully</I>. Upon reaching the linger timeout, the socket is + * closed <I>forcefully</I>, with a TCP RST. Enabling the option with a + * timeout of zero does a forceful close immediately. If the specified + * timeout value exceeds 65,535 it will be reduced to 65,535. + * <P> + * Valid only for TCP: SocketImpl + * + * @see Socket#setSoLinger + * @see Socket#getSoLinger + */ + @Native public final static int SO_LINGER = 0x0080; + + /** Set a timeout on blocking Socket operations: + * <PRE> + * ServerSocket.accept(); + * SocketInputStream.read(); + * DatagramSocket.receive(); + * </PRE> + * + * <P> The option must be set prior to entering a blocking + * operation to take effect. If the timeout expires and the + * operation would continue to block, + * <B>java.io.InterruptedIOException</B> is raised. The Socket is + * not closed in this case. + * + * <P> Valid for all sockets: SocketImpl, DatagramSocketImpl + * + * @see Socket#setSoTimeout + * @see ServerSocket#setSoTimeout + * @see DatagramSocket#setSoTimeout + */ + @Native public final static int SO_TIMEOUT = 0x1006; + + /** + * Set a hint the size of the underlying buffers used by the + * platform for outgoing network I/O. When used in set, this is a + * suggestion to the kernel from the application about the size of + * buffers to use for the data to be sent over the socket. When + * used in get, this must return the size of the buffer actually + * used by the platform when sending out data on this socket. + * + * Valid for all sockets: SocketImpl, DatagramSocketImpl + * + * @see Socket#setSendBufferSize + * @see Socket#getSendBufferSize + * @see DatagramSocket#setSendBufferSize + * @see DatagramSocket#getSendBufferSize + */ + @Native public final static int SO_SNDBUF = 0x1001; + + /** + * Set a hint the size of the underlying buffers used by the + * platform for incoming network I/O. When used in set, this is a + * suggestion to the kernel from the application about the size of + * buffers to use for the data to be received over the + * socket. When used in get, this must return the size of the + * buffer actually used by the platform when receiving in data on + * this socket. + * + * Valid for all sockets: SocketImpl, DatagramSocketImpl + * + * @see Socket#setReceiveBufferSize + * @see Socket#getReceiveBufferSize + * @see DatagramSocket#setReceiveBufferSize + * @see DatagramSocket#getReceiveBufferSize + */ + @Native public final static int SO_RCVBUF = 0x1002; + + /** + * When the keepalive option is set for a TCP socket and no data + * has been exchanged across the socket in either direction for + * 2 hours (NOTE: the actual value is implementation dependent), + * TCP automatically sends a keepalive probe to the peer. This probe is a + * TCP segment to which the peer must respond. + * One of three responses is expected: + * 1. The peer responds with the expected ACK. The application is not + * notified (since everything is OK). TCP will send another probe + * following another 2 hours of inactivity. + * 2. The peer responds with an RST, which tells the local TCP that + * the peer host has crashed and rebooted. The socket is closed. + * 3. There is no response from the peer. The socket is closed. + * + * The purpose of this option is to detect if the peer host crashes. + * + * Valid only for TCP socket: SocketImpl + * + * @see Socket#setKeepAlive + * @see Socket#getKeepAlive + */ + @Native public final static int SO_KEEPALIVE = 0x0008; + + /** + * When the OOBINLINE option is set, any TCP urgent data received on + * the socket will be received through the socket input stream. + * When the option is disabled (which is the default) urgent data + * is silently discarded. + * + * @see Socket#setOOBInline + * @see Socket#getOOBInline + */ + @Native public final static int SO_OOBINLINE = 0x1003; +}
diff --git a/java/net/SocketOutputStream.java b/java/net/SocketOutputStream.java new file mode 100644 index 0000000..9e8e792 --- /dev/null +++ b/java/net/SocketOutputStream.java
@@ -0,0 +1,192 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 1995, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.channels.FileChannel; + +import dalvik.system.BlockGuard; + +/** + * This stream extends FileOutputStream to implement a + * SocketOutputStream. Note that this class should <b>NOT</b> be + * public. + * + * @author Jonathan Payne + * @author Arthur van Hoff + */ +class SocketOutputStream extends FileOutputStream +{ + // Android-removed: Android doesn't need to call native init. + // static { + // init(); + //} + + private AbstractPlainSocketImpl impl = null; + private byte temp[] = new byte[1]; + private Socket socket = null; + + /** + * Creates a new SocketOutputStream. Can only be called + * by a Socket. This method needs to hang on to the owner Socket so + * that the fd will not be closed. + * @param impl the socket output stream inplemented + */ + SocketOutputStream(AbstractPlainSocketImpl impl) throws IOException { + super(impl.getFileDescriptor()); + this.impl = impl; + socket = impl.getSocket(); + } + + /** + * Returns the unique {@link java.nio.channels.FileChannel FileChannel} + * object associated with this file output stream. </p> + * + * The {@code getChannel} method of {@code SocketOutputStream} + * returns {@code null} since it is a socket based stream.</p> + * + * @return the file channel associated with this file output stream + * + * @since 1.4 + * @spec JSR-51 + */ + public final FileChannel getChannel() { + return null; + } + + /** + * Writes to the socket. + * @param fd the FileDescriptor + * @param b the data to be written + * @param off the start offset in the data + * @param len the number of bytes that are written + * @exception IOException If an I/O error has occurred. + */ + private native void socketWrite0(FileDescriptor fd, byte[] b, int off, + int len) throws IOException; + + /** + * Writes to the socket with appropriate locking of the + * FileDescriptor. + * @param b the data to be written + * @param off the start offset in the data + * @param len the number of bytes that are written + * @exception IOException If an I/O error has occurred. + */ + private void socketWrite(byte b[], int off, int len) throws IOException { + + + if (len <= 0 || off < 0 || len > b.length - off) { + if (len == 0) { + return; + } + throw new ArrayIndexOutOfBoundsException("len == " + len + + " off == " + off + " buffer length == " + b.length); + } + + FileDescriptor fd = impl.acquireFD(); + try { + // Android-added: Check BlockGuard policy in socketWrite. + BlockGuard.getThreadPolicy().onNetwork(); + socketWrite0(fd, b, off, len); + } catch (SocketException se) { + if (se instanceof sun.net.ConnectionResetException) { + impl.setConnectionResetPending(); + se = new SocketException("Connection reset"); + } + if (impl.isClosedOrPending()) { + throw new SocketException("Socket closed"); + } else { + throw se; + } + } finally { + impl.releaseFD(); + } + } + + /** + * Writes a byte to the socket. + * @param b the data to be written + * @exception IOException If an I/O error has occurred. + */ + public void write(int b) throws IOException { + temp[0] = (byte)b; + socketWrite(temp, 0, 1); + } + + /** + * Writes the contents of the buffer <i>b</i> to the socket. + * @param b the data to be written + * @exception SocketException If an I/O error has occurred. + */ + public void write(byte b[]) throws IOException { + socketWrite(b, 0, b.length); + } + + /** + * Writes <i>length</i> bytes from buffer <i>b</i> starting at + * offset <i>len</i>. + * @param b the data to be written + * @param off the start offset in the data + * @param len the number of bytes that are written + * @exception SocketException If an I/O error has occurred. + */ + public void write(byte b[], int off, int len) throws IOException { + socketWrite(b, off, len); + } + + /** + * Closes the stream. + */ + private boolean closing = false; + public void close() throws IOException { + // Prevent recursion. See BugId 4484411 + if (closing) + return; + closing = true; + if (socket != null) { + if (!socket.isClosed()) + socket.close(); + } else + impl.close(); + closing = false; + } + + /** + * Overrides finalize, the fd is closed by the Socket. + */ + protected void finalize() {} + + // Android-removed: Android doesn't need native init. + /* + * Perform class load-time initializations. + * + private native static void init(); + */ +}
diff --git a/java/net/SocketPermission.java b/java/net/SocketPermission.java new file mode 100644 index 0000000..2195ecc --- /dev/null +++ b/java/net/SocketPermission.java
@@ -0,0 +1,46 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.security.Permission; + + +// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager. +// See comments in java.lang.SecurityManager for details. +/** + * Legacy security code; do not use. + */ + +public final class SocketPermission extends Permission +implements java.io.Serializable +{ + + public SocketPermission(String host, String action) { super(""); } + + public boolean implies(Permission p) { return true; } + + public String getActions() { return null; } +}
diff --git a/java/net/SocketSecrets.java b/java/net/SocketSecrets.java new file mode 100644 index 0000000..9772906 --- /dev/null +++ b/java/net/SocketSecrets.java
@@ -0,0 +1,70 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.IOException; + +class SocketSecrets { + + /* accessed by reflection from jdk.net.Sockets */ + + /* obj must be a Socket or ServerSocket */ + + private static <T> void setOption(Object obj, SocketOption<T> name, T value) throws IOException { + SocketImpl impl; + + if (obj instanceof Socket) { + impl = ((Socket)obj).getImpl(); + } else if (obj instanceof ServerSocket) { + impl = ((ServerSocket)obj).getImpl(); + } else { + throw new IllegalArgumentException(); + } + impl.setOption(name, value); + } + + private static <T> T getOption(Object obj, SocketOption<T> name) throws IOException { + SocketImpl impl; + + if (obj instanceof Socket) { + impl = ((Socket)obj).getImpl(); + } else if (obj instanceof ServerSocket) { + impl = ((ServerSocket)obj).getImpl(); + } else { + throw new IllegalArgumentException(); + } + return impl.getOption(name); + } + + private static <T> void setOption(DatagramSocket s, SocketOption<T> name, T value) throws IOException { + s.getImpl().setOption(name, value); + } + + private static <T> T getOption(DatagramSocket s, SocketOption<T> name) throws IOException { + return s.getImpl().getOption(name); + } + +}
diff --git a/java/net/SocketTimeoutException.java b/java/net/SocketTimeoutException.java new file mode 100644 index 0000000..5daf600 --- /dev/null +++ b/java/net/SocketTimeoutException.java
@@ -0,0 +1,51 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 2000, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +/** + * Signals that a timeout has occurred on a socket read or accept. + * + * @since 1.4 + */ + +public class SocketTimeoutException extends java.io.InterruptedIOException { + private static final long serialVersionUID = -8846654841826352300L; + + /** + * Constructs a new SocketTimeoutException with a detail + * message. + * @param msg the detail message + */ + public SocketTimeoutException(String msg) { + super(msg); + } + + /** + * Construct a new SocketTimeoutException with no detailed message. + */ + public SocketTimeoutException() {} +}
diff --git a/java/net/SocksConsts.java b/java/net/SocksConsts.java new file mode 100644 index 0000000..df47aa7 --- /dev/null +++ b/java/net/SocksConsts.java
@@ -0,0 +1,58 @@ +/* + * Copyright (c) 2000, 2001, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.net; + +/** + * Constants used by the SOCKS protocol implementation. + */ + +interface SocksConsts { + static final int PROTO_VERS4 = 4; + static final int PROTO_VERS = 5; + static final int DEFAULT_PORT = 1080; + + static final int NO_AUTH = 0; + static final int GSSAPI = 1; + static final int USER_PASSW = 2; + static final int NO_METHODS = -1; + + static final int CONNECT = 1; + static final int BIND = 2; + static final int UDP_ASSOC = 3; + + static final int IPV4 = 1; + static final int DOMAIN_NAME = 3; + static final int IPV6 = 4; + + static final int REQUEST_OK = 0; + static final int GENERAL_FAILURE = 1; + static final int NOT_ALLOWED = 2; + static final int NET_UNREACHABLE = 3; + static final int HOST_UNREACHABLE = 4; + static final int CONN_REFUSED = 5; + static final int TTL_EXPIRED = 6; + static final int CMD_NOT_SUPPORTED = 7; + static final int ADDR_TYPE_NOT_SUP = 8; +}
diff --git a/java/net/SocksSocketImpl.java b/java/net/SocksSocketImpl.java new file mode 100644 index 0000000..0d9d8f5 --- /dev/null +++ b/java/net/SocksSocketImpl.java
@@ -0,0 +1,1100 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.net; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.BufferedOutputStream; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.PrivilegedExceptionAction; +import sun.net.SocksProxy; +import sun.net.www.ParseUtil; +/* import org.ietf.jgss.*; */ + +/** + * SOCKS (V4 & V5) TCP socket implementation (RFC 1928). + * This is a subclass of PlainSocketImpl. + * Note this class should <b>NOT</b> be public. + */ + +class SocksSocketImpl extends PlainSocketImpl implements SocksConsts { + private String server = null; + private int serverPort = DEFAULT_PORT; + private InetSocketAddress external_address; + private boolean useV4 = false; + private Socket cmdsock = null; + private InputStream cmdIn = null; + private OutputStream cmdOut = null; + /* true if the Proxy has been set programatically */ + private boolean applicationSetProxy; /* false */ + + + SocksSocketImpl() { + // Nothing needed + } + + SocksSocketImpl(String server, int port) { + this.server = server; + this.serverPort = (port == -1 ? DEFAULT_PORT : port); + } + + SocksSocketImpl(Proxy proxy) { + SocketAddress a = proxy.address(); + if (a instanceof InetSocketAddress) { + InetSocketAddress ad = (InetSocketAddress) a; + // Use getHostString() to avoid reverse lookups + server = ad.getHostString(); + serverPort = ad.getPort(); + } + } + + void setV4() { + useV4 = true; + } + + private synchronized void privilegedConnect(final String host, + final int port, + final int timeout) + throws IOException + { + try { + AccessController.doPrivileged( + new java.security.PrivilegedExceptionAction<Void>() { + public Void run() throws IOException { + superConnectServer(host, port, timeout); + cmdIn = getInputStream(); + cmdOut = getOutputStream(); + return null; + } + }); + } catch (java.security.PrivilegedActionException pae) { + throw (IOException) pae.getException(); + } + } + + private void superConnectServer(String host, int port, + int timeout) throws IOException { + super.connect(new InetSocketAddress(host, port), timeout); + } + + private static int remainingMillis(long deadlineMillis) throws IOException { + if (deadlineMillis == 0L) + return 0; + + final long remaining = deadlineMillis - System.currentTimeMillis(); + if (remaining > 0) + return (int) remaining; + + throw new SocketTimeoutException(); + } + + private int readSocksReply(InputStream in, byte[] data) throws IOException { + return readSocksReply(in, data, 0L); + } + + private int readSocksReply(InputStream in, byte[] data, long deadlineMillis) throws IOException { + int len = data.length; + int received = 0; + for (int attempts = 0; received < len && attempts < 3; attempts++) { + int count; + try { + count = ((SocketInputStream)in).read(data, received, len - received, remainingMillis(deadlineMillis)); + } catch (SocketTimeoutException e) { + throw new SocketTimeoutException("Connect timed out"); + } + if (count < 0) + throw new SocketException("Malformed reply from SOCKS server"); + received += count; + } + return received; + } + + /** + * Provides the authentication machanism required by the proxy. + */ + private boolean authenticate(byte method, InputStream in, + BufferedOutputStream out) throws IOException { + return authenticate(method, in, out, 0L); + } + + private boolean authenticate(byte method, InputStream in, + BufferedOutputStream out, + long deadlineMillis) throws IOException { + // No Authentication required. We're done then! + if (method == NO_AUTH) + return true; + /** + * User/Password authentication. Try, in that order : + * - The application provided Authenticator, if any + * - the user.name & no password (backward compatibility behavior). + */ + if (method == USER_PASSW) { + String userName; + String password = null; + final InetAddress addr = InetAddress.getByName(server); + PasswordAuthentication pw = + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction<PasswordAuthentication>() { + public PasswordAuthentication run() { + return Authenticator.requestPasswordAuthentication( + server, addr, serverPort, "SOCKS5", "SOCKS authentication", null); + } + }); + if (pw != null) { + userName = pw.getUserName(); + password = new String(pw.getPassword()); + } else { + userName = java.security.AccessController.doPrivileged( + new sun.security.action.GetPropertyAction("user.name")); + } + if (userName == null) + return false; + out.write(1); + out.write(userName.length()); + try { + out.write(userName.getBytes("ISO-8859-1")); + } catch (java.io.UnsupportedEncodingException uee) { + assert false; + } + if (password != null) { + out.write(password.length()); + try { + out.write(password.getBytes("ISO-8859-1")); + } catch (java.io.UnsupportedEncodingException uee) { + assert false; + } + } else + out.write(0); + out.flush(); + byte[] data = new byte[2]; + int i = readSocksReply(in, data, deadlineMillis); + if (i != 2 || data[1] != 0) { + /* RFC 1929 specifies that the connection MUST be closed if + authentication fails */ + out.close(); + in.close(); + return false; + } + /* Authentication succeeded */ + return true; + } + /** + * GSSAPI authentication mechanism. + * Unfortunately the RFC seems out of sync with the Reference + * implementation. I'll leave this in for future completion. + */ +// if (method == GSSAPI) { +// try { +// GSSManager manager = GSSManager.getInstance(); +// GSSName name = manager.createName("SERVICE:socks@"+server, +// null); +// GSSContext context = manager.createContext(name, null, null, +// GSSContext.DEFAULT_LIFETIME); +// context.requestMutualAuth(true); +// context.requestReplayDet(true); +// context.requestSequenceDet(true); +// context.requestCredDeleg(true); +// byte []inToken = new byte[0]; +// while (!context.isEstablished()) { +// byte[] outToken +// = context.initSecContext(inToken, 0, inToken.length); +// // send the output token if generated +// if (outToken != null) { +// out.write(1); +// out.write(1); +// out.writeShort(outToken.length); +// out.write(outToken); +// out.flush(); +// data = new byte[2]; +// i = readSocksReply(in, data, deadlineMillis); +// if (i != 2 || data[1] == 0xff) { +// in.close(); +// out.close(); +// return false; +// } +// i = readSocksReply(in, data, deadlineMillis); +// int len = 0; +// len = ((int)data[0] & 0xff) << 8; +// len += data[1]; +// data = new byte[len]; +// i = readSocksReply(in, data, deadlineMillis); +// if (i == len) +// return true; +// in.close(); +// out.close(); +// } +// } +// } catch (GSSException e) { +// /* RFC 1961 states that if Context initialisation fails the connection +// MUST be closed */ +// e.printStackTrace(); +// in.close(); +// out.close(); +// } +// } + return false; + } + + private void connectV4(InputStream in, OutputStream out, + InetSocketAddress endpoint, + long deadlineMillis) throws IOException { + if (!(endpoint.getAddress() instanceof Inet4Address)) { + throw new SocketException("SOCKS V4 requires IPv4 only addresses"); + } + out.write(PROTO_VERS4); + out.write(CONNECT); + out.write((endpoint.getPort() >> 8) & 0xff); + out.write((endpoint.getPort() >> 0) & 0xff); + out.write(endpoint.getAddress().getAddress()); + String userName = getUserName(); + try { + out.write(userName.getBytes("ISO-8859-1")); + } catch (java.io.UnsupportedEncodingException uee) { + assert false; + } + out.write(0); + out.flush(); + byte[] data = new byte[8]; + int n = readSocksReply(in, data, deadlineMillis); + if (n != 8) + throw new SocketException("Reply from SOCKS server has bad length: " + n); + if (data[0] != 0 && data[0] != 4) + throw new SocketException("Reply from SOCKS server has bad version"); + SocketException ex = null; + switch (data[1]) { + case 90: + // Success! + external_address = endpoint; + break; + case 91: + ex = new SocketException("SOCKS request rejected"); + break; + case 92: + ex = new SocketException("SOCKS server couldn't reach destination"); + break; + case 93: + ex = new SocketException("SOCKS authentication failed"); + break; + default: + ex = new SocketException("Reply from SOCKS server contains bad status"); + break; + } + if (ex != null) { + in.close(); + out.close(); + throw ex; + } + } + + /** + * Connects the Socks Socket to the specified endpoint. It will first + * connect to the SOCKS proxy and negotiate the access. If the proxy + * grants the connections, then the connect is successful and all + * further traffic will go to the "real" endpoint. + * + * @param endpoint the {@code SocketAddress} to connect to. + * @param timeout the timeout value in milliseconds + * @throws IOException if the connection can't be established. + * @throws SecurityException if there is a security manager and it + * doesn't allow the connection + * @throws IllegalArgumentException if endpoint is null or a + * SocketAddress subclass not supported by this socket + */ + @Override + protected void connect(SocketAddress endpoint, int timeout) throws IOException { + final long deadlineMillis; + + if (timeout == 0) { + deadlineMillis = 0L; + } else { + long finish = System.currentTimeMillis() + timeout; + deadlineMillis = finish < 0 ? Long.MAX_VALUE : finish; + } + + SecurityManager security = System.getSecurityManager(); + if (endpoint == null || !(endpoint instanceof InetSocketAddress)) + throw new IllegalArgumentException("Unsupported address type"); + InetSocketAddress epoint = (InetSocketAddress) endpoint; + if (security != null) { + if (epoint.isUnresolved()) + security.checkConnect(epoint.getHostName(), + epoint.getPort()); + else + security.checkConnect(epoint.getAddress().getHostAddress(), + epoint.getPort()); + } + if (server == null) { + // Android-removed: Logic to establish proxy connection based on default ProxySelector. + // Removed code that tried to establish proxy connection if ProxySelector#getDefault() + // is not null. This was never the case in previous Android releases, was causing + // issues and therefore was removed. + /* + // This is the general case + // server is not null only when the socket was created with a + // specified proxy in which case it does bypass the ProxySelector + ProxySelector sel = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction<ProxySelector>() { + public ProxySelector run() { + return ProxySelector.getDefault(); + } + }); + if (sel == null) { + /* + * No default proxySelector --> direct connection + * + super.connect(epoint, remainingMillis(deadlineMillis)); + return; + } + URI uri; + // Use getHostString() to avoid reverse lookups + String host = epoint.getHostString(); + // IPv6 litteral? + if (epoint.getAddress() instanceof Inet6Address && + (!host.startsWith("[")) && (host.indexOf(":") >= 0)) { + host = "[" + host + "]"; + } + try { + uri = new URI("socket://" + ParseUtil.encodePath(host) + ":"+ epoint.getPort()); + } catch (URISyntaxException e) { + // This shouldn't happen + assert false : e; + uri = null; + } + Proxy p = null; + IOException savedExc = null; + java.util.Iterator<Proxy> iProxy = null; + iProxy = sel.select(uri).iterator(); + if (iProxy == null || !(iProxy.hasNext())) { + super.connect(epoint, remainingMillis(deadlineMillis)); + return; + } + while (iProxy.hasNext()) { + p = iProxy.next(); + if (p == null || p.type() != Proxy.Type.SOCKS) { + super.connect(epoint, remainingMillis(deadlineMillis)); + return; + } + + if (!(p.address() instanceof InetSocketAddress)) + throw new SocketException("Unknown address type for proxy: " + p); + // Use getHostString() to avoid reverse lookups + server = ((InetSocketAddress) p.address()).getHostString(); + serverPort = ((InetSocketAddress) p.address()).getPort(); + if (p instanceof SocksProxy) { + if (((SocksProxy)p).protocolVersion() == 4) { + useV4 = true; + } + } + + // Connects to the SOCKS server + try { + privilegedConnect(server, serverPort, remainingMillis(deadlineMillis)); + // Worked, let's get outta here + break; + } catch (IOException e) { + // Ooops, let's notify the ProxySelector + sel.connectFailed(uri,p.address(),e); + server = null; + serverPort = -1; + savedExc = e; + // Will continue the while loop and try the next proxy + } + } + + /* + * If server is still null at this point, none of the proxy + * worked + * + if (server == null) { + throw new SocketException("Can't connect to SOCKS proxy:" + + savedExc.getMessage()); + } + */ + super.connect(epoint, remainingMillis(deadlineMillis)); + return; + } else { + // Connects to the SOCKS server + try { + privilegedConnect(server, serverPort, remainingMillis(deadlineMillis)); + } catch (IOException e) { + throw new SocketException(e.getMessage()); + } + } + + // cmdIn & cmdOut were initialized during the privilegedConnect() call + BufferedOutputStream out = new BufferedOutputStream(cmdOut, 512); + InputStream in = cmdIn; + + if (useV4) { + // SOCKS Protocol version 4 doesn't know how to deal with + // DOMAIN type of addresses (unresolved addresses here) + if (epoint.isUnresolved()) + throw new UnknownHostException(epoint.toString()); + connectV4(in, out, epoint, deadlineMillis); + return; + } + + // This is SOCKS V5 + out.write(PROTO_VERS); + out.write(2); + out.write(NO_AUTH); + out.write(USER_PASSW); + out.flush(); + byte[] data = new byte[2]; + int i = readSocksReply(in, data, deadlineMillis); + if (i != 2 || ((int)data[0]) != PROTO_VERS) { + // Maybe it's not a V5 sever after all + // Let's try V4 before we give up + // SOCKS Protocol version 4 doesn't know how to deal with + // DOMAIN type of addresses (unresolved addresses here) + if (epoint.isUnresolved()) + throw new UnknownHostException(epoint.toString()); + connectV4(in, out, epoint, deadlineMillis); + return; + } + if (((int)data[1]) == NO_METHODS) + throw new SocketException("SOCKS : No acceptable methods"); + if (!authenticate(data[1], in, out, deadlineMillis)) { + throw new SocketException("SOCKS : authentication failed"); + } + out.write(PROTO_VERS); + out.write(CONNECT); + out.write(0); + /* Test for IPV4/IPV6/Unresolved */ + if (epoint.isUnresolved()) { + out.write(DOMAIN_NAME); + out.write(epoint.getHostName().length()); + try { + out.write(epoint.getHostName().getBytes("ISO-8859-1")); + } catch (java.io.UnsupportedEncodingException uee) { + assert false; + } + out.write((epoint.getPort() >> 8) & 0xff); + out.write((epoint.getPort() >> 0) & 0xff); + } else if (epoint.getAddress() instanceof Inet6Address) { + out.write(IPV6); + out.write(epoint.getAddress().getAddress()); + out.write((epoint.getPort() >> 8) & 0xff); + out.write((epoint.getPort() >> 0) & 0xff); + } else { + out.write(IPV4); + out.write(epoint.getAddress().getAddress()); + out.write((epoint.getPort() >> 8) & 0xff); + out.write((epoint.getPort() >> 0) & 0xff); + } + out.flush(); + data = new byte[4]; + i = readSocksReply(in, data, deadlineMillis); + if (i != 4) + throw new SocketException("Reply from SOCKS server has bad length"); + SocketException ex = null; + int len; + byte[] addr; + switch (data[1]) { + case REQUEST_OK: + // success! + switch(data[3]) { + case IPV4: + addr = new byte[4]; + i = readSocksReply(in, addr, deadlineMillis); + if (i != 4) + throw new SocketException("Reply from SOCKS server badly formatted"); + data = new byte[2]; + i = readSocksReply(in, data, deadlineMillis); + if (i != 2) + throw new SocketException("Reply from SOCKS server badly formatted"); + break; + case DOMAIN_NAME: + len = data[1]; + byte[] host = new byte[len]; + i = readSocksReply(in, host, deadlineMillis); + if (i != len) + throw new SocketException("Reply from SOCKS server badly formatted"); + data = new byte[2]; + i = readSocksReply(in, data, deadlineMillis); + if (i != 2) + throw new SocketException("Reply from SOCKS server badly formatted"); + break; + case IPV6: + len = data[1]; + addr = new byte[len]; + i = readSocksReply(in, addr, deadlineMillis); + if (i != len) + throw new SocketException("Reply from SOCKS server badly formatted"); + data = new byte[2]; + i = readSocksReply(in, data, deadlineMillis); + if (i != 2) + throw new SocketException("Reply from SOCKS server badly formatted"); + break; + default: + ex = new SocketException("Reply from SOCKS server contains wrong code"); + break; + } + break; + case GENERAL_FAILURE: + ex = new SocketException("SOCKS server general failure"); + break; + case NOT_ALLOWED: + ex = new SocketException("SOCKS: Connection not allowed by ruleset"); + break; + case NET_UNREACHABLE: + ex = new SocketException("SOCKS: Network unreachable"); + break; + case HOST_UNREACHABLE: + ex = new SocketException("SOCKS: Host unreachable"); + break; + case CONN_REFUSED: + ex = new SocketException("SOCKS: Connection refused"); + break; + case TTL_EXPIRED: + ex = new SocketException("SOCKS: TTL expired"); + break; + case CMD_NOT_SUPPORTED: + ex = new SocketException("SOCKS: Command not supported"); + break; + case ADDR_TYPE_NOT_SUP: + ex = new SocketException("SOCKS: address type not supported"); + break; + } + if (ex != null) { + in.close(); + out.close(); + throw ex; + } + external_address = epoint; + } + + // Android-removed: Dead code. bindV4, socksBind, acceptFrom methods. + /* + private void bindV4(InputStream in, OutputStream out, + InetAddress baddr, + int lport) throws IOException { + if (!(baddr instanceof Inet4Address)) { + throw new SocketException("SOCKS V4 requires IPv4 only addresses"); + } + super.bind(baddr, lport); + byte[] addr1 = baddr.getAddress(); + /* Test for AnyLocal * + InetAddress naddr = baddr; + if (naddr.isAnyLocalAddress()) { + naddr = AccessController.doPrivileged( + new PrivilegedAction<InetAddress>() { + public InetAddress run() { + return cmdsock.getLocalAddress(); + + } + }); + addr1 = naddr.getAddress(); + } + out.write(PROTO_VERS4); + out.write(BIND); + out.write((super.getLocalPort() >> 8) & 0xff); + out.write((super.getLocalPort() >> 0) & 0xff); + out.write(addr1); + String userName = getUserName(); + try { + out.write(userName.getBytes("ISO-8859-1")); + } catch (java.io.UnsupportedEncodingException uee) { + assert false; + } + out.write(0); + out.flush(); + byte[] data = new byte[8]; + int n = readSocksReply(in, data); + if (n != 8) + throw new SocketException("Reply from SOCKS server has bad length: " + n); + if (data[0] != 0 && data[0] != 4) + throw new SocketException("Reply from SOCKS server has bad version"); + SocketException ex = null; + switch (data[1]) { + case 90: + // Success! + external_address = new InetSocketAddress(baddr, lport); + break; + case 91: + ex = new SocketException("SOCKS request rejected"); + break; + case 92: + ex = new SocketException("SOCKS server couldn't reach destination"); + break; + case 93: + ex = new SocketException("SOCKS authentication failed"); + break; + default: + ex = new SocketException("Reply from SOCKS server contains bad status"); + break; + } + if (ex != null) { + in.close(); + out.close(); + throw ex; + } + + } + + /** + * Sends the Bind request to the SOCKS proxy. In the SOCKS protocol, bind + * means "accept incoming connection from", so the SocketAddress is the + * the one of the host we do accept connection from. + * + * @param saddr the Socket address of the remote host. + * @exception IOException if an I/O error occurs when binding this socket. + * + protected synchronized void socksBind(InetSocketAddress saddr) throws IOException { + if (socket != null) { + // this is a client socket, not a server socket, don't + // call the SOCKS proxy for a bind! + return; + } + + // Connects to the SOCKS server + + if (server == null) { + // This is the general case + // server is not null only when the socket was created with a + // specified proxy in which case it does bypass the ProxySelector + ProxySelector sel = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction<ProxySelector>() { + public ProxySelector run() { + return ProxySelector.getDefault(); + } + }); + if (sel == null) { + /* + * No default proxySelector --> direct connection + * + return; + } + URI uri; + // Use getHostString() to avoid reverse lookups + String host = saddr.getHostString(); + // IPv6 litteral? + if (saddr.getAddress() instanceof Inet6Address && + (!host.startsWith("[")) && (host.indexOf(":") >= 0)) { + host = "[" + host + "]"; + } + try { + uri = new URI("serversocket://" + ParseUtil.encodePath(host) + ":"+ saddr.getPort()); + } catch (URISyntaxException e) { + // This shouldn't happen + assert false : e; + uri = null; + } + Proxy p = null; + Exception savedExc = null; + java.util.Iterator<Proxy> iProxy = null; + iProxy = sel.select(uri).iterator(); + if (iProxy == null || !(iProxy.hasNext())) { + return; + } + while (iProxy.hasNext()) { + p = iProxy.next(); + if (p == null || p.type() != Proxy.Type.SOCKS) { + return; + } + + if (!(p.address() instanceof InetSocketAddress)) + throw new SocketException("Unknown address type for proxy: " + p); + // Use getHostString() to avoid reverse lookups + server = ((InetSocketAddress) p.address()).getHostString(); + serverPort = ((InetSocketAddress) p.address()).getPort(); + if (p instanceof SocksProxy) { + if (((SocksProxy)p).protocolVersion() == 4) { + useV4 = true; + } + } + + // Connects to the SOCKS server + try { + AccessController.doPrivileged( + new PrivilegedExceptionAction<Void>() { + public Void run() throws Exception { + cmdsock = new Socket(new PlainSocketImpl()); + cmdsock.connect(new InetSocketAddress(server, serverPort)); + cmdIn = cmdsock.getInputStream(); + cmdOut = cmdsock.getOutputStream(); + return null; + } + }); + } catch (Exception e) { + // Ooops, let's notify the ProxySelector + sel.connectFailed(uri,p.address(),new SocketException(e.getMessage())); + server = null; + serverPort = -1; + cmdsock = null; + savedExc = e; + // Will continue the while loop and try the next proxy + } + } + + /* + * If server is still null at this point, none of the proxy + * worked + * + if (server == null || cmdsock == null) { + throw new SocketException("Can't connect to SOCKS proxy:" + + savedExc.getMessage()); + } + } else { + try { + AccessController.doPrivileged( + new PrivilegedExceptionAction<Void>() { + public Void run() throws Exception { + cmdsock = new Socket(new PlainSocketImpl()); + cmdsock.connect(new InetSocketAddress(server, serverPort)); + cmdIn = cmdsock.getInputStream(); + cmdOut = cmdsock.getOutputStream(); + return null; + } + }); + } catch (Exception e) { + throw new SocketException(e.getMessage()); + } + } + BufferedOutputStream out = new BufferedOutputStream(cmdOut, 512); + InputStream in = cmdIn; + if (useV4) { + bindV4(in, out, saddr.getAddress(), saddr.getPort()); + return; + } + out.write(PROTO_VERS); + out.write(2); + out.write(NO_AUTH); + out.write(USER_PASSW); + out.flush(); + byte[] data = new byte[2]; + int i = readSocksReply(in, data); + if (i != 2 || ((int)data[0]) != PROTO_VERS) { + // Maybe it's not a V5 sever after all + // Let's try V4 before we give up + bindV4(in, out, saddr.getAddress(), saddr.getPort()); + return; + } + if (((int)data[1]) == NO_METHODS) + throw new SocketException("SOCKS : No acceptable methods"); + if (!authenticate(data[1], in, out)) { + throw new SocketException("SOCKS : authentication failed"); + } + // We're OK. Let's issue the BIND command. + out.write(PROTO_VERS); + out.write(BIND); + out.write(0); + int lport = saddr.getPort(); + if (saddr.isUnresolved()) { + out.write(DOMAIN_NAME); + out.write(saddr.getHostName().length()); + try { + out.write(saddr.getHostName().getBytes("ISO-8859-1")); + } catch (java.io.UnsupportedEncodingException uee) { + assert false; + } + out.write((lport >> 8) & 0xff); + out.write((lport >> 0) & 0xff); + } else if (saddr.getAddress() instanceof Inet4Address) { + byte[] addr1 = saddr.getAddress().getAddress(); + out.write(IPV4); + out.write(addr1); + out.write((lport >> 8) & 0xff); + out.write((lport >> 0) & 0xff); + out.flush(); + } else if (saddr.getAddress() instanceof Inet6Address) { + byte[] addr1 = saddr.getAddress().getAddress(); + out.write(IPV6); + out.write(addr1); + out.write((lport >> 8) & 0xff); + out.write((lport >> 0) & 0xff); + out.flush(); + } else { + cmdsock.close(); + throw new SocketException("unsupported address type : " + saddr); + } + data = new byte[4]; + i = readSocksReply(in, data); + SocketException ex = null; + int len, nport; + byte[] addr; + switch (data[1]) { + case REQUEST_OK: + // success! + switch(data[3]) { + case IPV4: + addr = new byte[4]; + i = readSocksReply(in, addr); + if (i != 4) + throw new SocketException("Reply from SOCKS server badly formatted"); + data = new byte[2]; + i = readSocksReply(in, data); + if (i != 2) + throw new SocketException("Reply from SOCKS server badly formatted"); + nport = ((int)data[0] & 0xff) << 8; + nport += ((int)data[1] & 0xff); + external_address = + new InetSocketAddress(new Inet4Address("", addr) , nport); + break; + case DOMAIN_NAME: + len = data[1]; + byte[] host = new byte[len]; + i = readSocksReply(in, host); + if (i != len) + throw new SocketException("Reply from SOCKS server badly formatted"); + data = new byte[2]; + i = readSocksReply(in, data); + if (i != 2) + throw new SocketException("Reply from SOCKS server badly formatted"); + nport = ((int)data[0] & 0xff) << 8; + nport += ((int)data[1] & 0xff); + external_address = new InetSocketAddress(new String(host), nport); + break; + case IPV6: + len = data[1]; + addr = new byte[len]; + i = readSocksReply(in, addr); + if (i != len) + throw new SocketException("Reply from SOCKS server badly formatted"); + data = new byte[2]; + i = readSocksReply(in, data); + if (i != 2) + throw new SocketException("Reply from SOCKS server badly formatted"); + nport = ((int)data[0] & 0xff) << 8; + nport += ((int)data[1] & 0xff); + external_address = + new InetSocketAddress(new Inet6Address("", addr), nport); + break; + } + break; + case GENERAL_FAILURE: + ex = new SocketException("SOCKS server general failure"); + break; + case NOT_ALLOWED: + ex = new SocketException("SOCKS: Bind not allowed by ruleset"); + break; + case NET_UNREACHABLE: + ex = new SocketException("SOCKS: Network unreachable"); + break; + case HOST_UNREACHABLE: + ex = new SocketException("SOCKS: Host unreachable"); + break; + case CONN_REFUSED: + ex = new SocketException("SOCKS: Connection refused"); + break; + case TTL_EXPIRED: + ex = new SocketException("SOCKS: TTL expired"); + break; + case CMD_NOT_SUPPORTED: + ex = new SocketException("SOCKS: Command not supported"); + break; + case ADDR_TYPE_NOT_SUP: + ex = new SocketException("SOCKS: address type not supported"); + break; + } + if (ex != null) { + in.close(); + out.close(); + cmdsock.close(); + cmdsock = null; + throw ex; + } + cmdIn = in; + cmdOut = out; + } + + /** + * Accepts a connection from a specific host. + * + * @param s the accepted connection. + * @param saddr the socket address of the host we do accept + * connection from + * @exception IOException if an I/O error occurs when accepting the + * connection. + * + protected void acceptFrom(SocketImpl s, InetSocketAddress saddr) throws IOException { + if (cmdsock == null) { + // Not a Socks ServerSocket. + return; + } + InputStream in = cmdIn; + // Sends the "SOCKS BIND" request. + socksBind(saddr); + in.read(); + int i = in.read(); + in.read(); + SocketException ex = null; + int nport; + byte[] addr; + InetSocketAddress real_end = null; + switch (i) { + case REQUEST_OK: + // success! + i = in.read(); + switch(i) { + case IPV4: + addr = new byte[4]; + readSocksReply(in, addr); + nport = in.read() << 8; + nport += in.read(); + real_end = + new InetSocketAddress(new Inet4Address("", addr) , nport); + break; + case DOMAIN_NAME: + int len = in.read(); + addr = new byte[len]; + readSocksReply(in, addr); + nport = in.read() << 8; + nport += in.read(); + real_end = new InetSocketAddress(new String(addr), nport); + break; + case IPV6: + addr = new byte[16]; + readSocksReply(in, addr); + nport = in.read() << 8; + nport += in.read(); + real_end = + new InetSocketAddress(new Inet6Address("", addr), nport); + break; + } + break; + case GENERAL_FAILURE: + ex = new SocketException("SOCKS server general failure"); + break; + case NOT_ALLOWED: + ex = new SocketException("SOCKS: Accept not allowed by ruleset"); + break; + case NET_UNREACHABLE: + ex = new SocketException("SOCKS: Network unreachable"); + break; + case HOST_UNREACHABLE: + ex = new SocketException("SOCKS: Host unreachable"); + break; + case CONN_REFUSED: + ex = new SocketException("SOCKS: Connection refused"); + break; + case TTL_EXPIRED: + ex = new SocketException("SOCKS: TTL expired"); + break; + case CMD_NOT_SUPPORTED: + ex = new SocketException("SOCKS: Command not supported"); + break; + case ADDR_TYPE_NOT_SUP: + ex = new SocketException("SOCKS: address type not supported"); + break; + } + if (ex != null) { + cmdIn.close(); + cmdOut.close(); + cmdsock.close(); + cmdsock = null; + throw ex; + } + + /** + * This is where we have to do some fancy stuff. + * The datastream from the socket "accepted" by the proxy will + * come through the cmdSocket. So we have to swap the socketImpls + * + if (s instanceof SocksSocketImpl) { + ((SocksSocketImpl)s).external_address = real_end; + } + if (s instanceof PlainSocketImpl) { + PlainSocketImpl psi = (PlainSocketImpl) s; + psi.setInputStream((SocketInputStream) in); + psi.setFileDescriptor(cmdsock.getImpl().getFileDescriptor()); + psi.setAddress(cmdsock.getImpl().getInetAddress()); + psi.setPort(cmdsock.getImpl().getPort()); + psi.setLocalPort(cmdsock.getImpl().getLocalPort()); + } else { + s.fd = cmdsock.getImpl().fd; + s.address = cmdsock.getImpl().address; + s.port = cmdsock.getImpl().port; + s.localport = cmdsock.getImpl().localport; + } + + // Need to do that so that the socket won't be closed + // when the ServerSocket is closed by the user. + // It kinds of detaches the Socket because it is now + // used elsewhere. + cmdsock = null; + } + */ + + /** + * Returns the value of this socket's {@code address} field. + * + * @return the value of this socket's {@code address} field. + * @see java.net.SocketImpl#address + */ + @Override + protected InetAddress getInetAddress() { + if (external_address != null) + return external_address.getAddress(); + else + return super.getInetAddress(); + } + + /** + * Returns the value of this socket's {@code port} field. + * + * @return the value of this socket's {@code port} field. + * @see java.net.SocketImpl#port + */ + @Override + protected int getPort() { + if (external_address != null) + return external_address.getPort(); + else + return super.getPort(); + } + + @Override + protected int getLocalPort() { + if (socket != null) + return super.getLocalPort(); + if (external_address != null) + return external_address.getPort(); + else + return super.getLocalPort(); + } + + @Override + protected void close() throws IOException { + if (cmdsock != null) + cmdsock.close(); + cmdsock = null; + super.close(); + } + + private String getUserName() { + String userName = ""; + if (applicationSetProxy) { + try { + userName = System.getProperty("user.name"); + } catch (SecurityException se) { /* swallow Exception */ } + } else { + userName = java.security.AccessController.doPrivileged( + new sun.security.action.GetPropertyAction("user.name")); + } + return userName; + } +}
diff --git a/java/net/StandardProtocolFamily.java b/java/net/StandardProtocolFamily.java new file mode 100644 index 0000000..25aaed4 --- /dev/null +++ b/java/net/StandardProtocolFamily.java
@@ -0,0 +1,45 @@ +/* + * Copyright (c) 2007, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +/** + * Defines the standard families of communication protocols. + * + * @since 1.7 + */ + +public enum StandardProtocolFamily implements ProtocolFamily { + + /** + * Internet Protocol Version 4 (IPv4) + */ + INET, + + /** + * Internet Protocol Version 6 (IPv6) + */ + INET6 +}
diff --git a/java/net/StandardSocketOptions.java b/java/net/StandardSocketOptions.java new file mode 100644 index 0000000..7fdd5f0 --- /dev/null +++ b/java/net/StandardSocketOptions.java
@@ -0,0 +1,367 @@ +/* + * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +/** + * Defines the <em>standard</em> socket options. + * + * <p> The {@link SocketOption#name name} of each socket option defined by this + * class is its field name. + * + * <p> In this release, the socket options defined here are used by {@link + * java.nio.channels.NetworkChannel network} channels in the {@link + * java.nio.channels channels} package. + * + * @since 1.7 + */ + +public final class StandardSocketOptions { + private StandardSocketOptions() { } + + // -- SOL_SOCKET -- + + /** + * Allow transmission of broadcast datagrams. + * + * <p> The value of this socket option is a {@code Boolean} that represents + * whether the option is enabled or disabled. The option is specific to + * datagram-oriented sockets sending to {@link java.net.Inet4Address IPv4} + * broadcast addresses. When the socket option is enabled then the socket + * can be used to send <em>broadcast datagrams</em>. + * + * <p> The initial value of this socket option is {@code FALSE}. The socket + * option may be enabled or disabled at any time. Some operating systems may + * require that the Java virtual machine be started with implementation + * specific privileges to enable this option or send broadcast datagrams. + * + * @see <a href="http://www.ietf.org/rfc/rfc919.txt">RFC 929: + * Broadcasting Internet Datagrams</a> + * @see DatagramSocket#setBroadcast + */ + public static final SocketOption<Boolean> SO_BROADCAST = + new StdSocketOption<Boolean>("SO_BROADCAST", Boolean.class); + + /** + * Keep connection alive. + * + * <p> The value of this socket option is a {@code Boolean} that represents + * whether the option is enabled or disabled. When the {@code SO_KEEPALIVE} + * option is enabled the operating system may use a <em>keep-alive</em> + * mechanism to periodically probe the other end of a connection when the + * connection is otherwise idle. The exact semantics of the keep alive + * mechanism is system dependent and therefore unspecified. + * + * <p> The initial value of this socket option is {@code FALSE}. The socket + * option may be enabled or disabled at any time. + * + * @see <a href="http://www.ietf.org/rfc/rfc1122.txt">RFC 1122 + * Requirements for Internet Hosts -- Communication Layers</a> + * @see Socket#setKeepAlive + */ + public static final SocketOption<Boolean> SO_KEEPALIVE = + new StdSocketOption<Boolean>("SO_KEEPALIVE", Boolean.class); + + /** + * The size of the socket send buffer. + * + * <p> The value of this socket option is an {@code Integer} that is the + * size of the socket send buffer in bytes. The socket send buffer is an + * output buffer used by the networking implementation. It may need to be + * increased for high-volume connections. The value of the socket option is + * a <em>hint</em> to the implementation to size the buffer and the actual + * size may differ. The socket option can be queried to retrieve the actual + * size. + * + * <p> For datagram-oriented sockets, the size of the send buffer may limit + * the size of the datagrams that may be sent by the socket. Whether + * datagrams larger than the buffer size are sent or discarded is system + * dependent. + * + * <p> The initial/default size of the socket send buffer and the range of + * allowable values is system dependent although a negative size is not + * allowed. An attempt to set the socket send buffer to larger than its + * maximum size causes it to be set to its maximum size. + * + * <p> An implementation allows this socket option to be set before the + * socket is bound or connected. Whether an implementation allows the + * socket send buffer to be changed after the socket is bound is system + * dependent. + * + * @see Socket#setSendBufferSize + */ + public static final SocketOption<Integer> SO_SNDBUF = + new StdSocketOption<Integer>("SO_SNDBUF", Integer.class); + + + /** + * The size of the socket receive buffer. + * + * <p> The value of this socket option is an {@code Integer} that is the + * size of the socket receive buffer in bytes. The socket receive buffer is + * an input buffer used by the networking implementation. It may need to be + * increased for high-volume connections or decreased to limit the possible + * backlog of incoming data. The value of the socket option is a + * <em>hint</em> to the implementation to size the buffer and the actual + * size may differ. + * + * <p> For datagram-oriented sockets, the size of the receive buffer may + * limit the size of the datagrams that can be received. Whether datagrams + * larger than the buffer size can be received is system dependent. + * Increasing the socket receive buffer may be important for cases where + * datagrams arrive in bursts faster than they can be processed. + * + * <p> In the case of stream-oriented sockets and the TCP/IP protocol, the + * size of the socket receive buffer may be used when advertising the size + * of the TCP receive window to the remote peer. + * + * <p> The initial/default size of the socket receive buffer and the range + * of allowable values is system dependent although a negative size is not + * allowed. An attempt to set the socket receive buffer to larger than its + * maximum size causes it to be set to its maximum size. + * + * <p> An implementation allows this socket option to be set before the + * socket is bound or connected. Whether an implementation allows the + * socket receive buffer to be changed after the socket is bound is system + * dependent. + * + * @see <a href="http://www.ietf.org/rfc/rfc1323.txt">RFC 1323: TCP + * Extensions for High Performance</a> + * @see Socket#setReceiveBufferSize + * @see ServerSocket#setReceiveBufferSize + */ + public static final SocketOption<Integer> SO_RCVBUF = + new StdSocketOption<Integer>("SO_RCVBUF", Integer.class); + + /** + * Re-use address. + * + * <p> The value of this socket option is a {@code Boolean} that represents + * whether the option is enabled or disabled. The exact semantics of this + * socket option are socket type and system dependent. + * + * <p> In the case of stream-oriented sockets, this socket option will + * usually determine whether the socket can be bound to a socket address + * when a previous connection involving that socket address is in the + * <em>TIME_WAIT</em> state. On implementations where the semantics differ, + * and the socket option is not required to be enabled in order to bind the + * socket when a previous connection is in this state, then the + * implementation may choose to ignore this option. + * + * <p> For datagram-oriented sockets the socket option is used to allow + * multiple programs bind to the same address. This option should be enabled + * when the socket is to be used for Internet Protocol (IP) multicasting. + * + * <p> An implementation allows this socket option to be set before the + * socket is bound or connected. Changing the value of this socket option + * after the socket is bound has no effect. The default value of this + * socket option is system dependent. + * + * @see <a href="http://www.ietf.org/rfc/rfc793.txt">RFC 793: Transmission + * Control Protocol</a> + * @see ServerSocket#setReuseAddress + */ + public static final SocketOption<Boolean> SO_REUSEADDR = + new StdSocketOption<Boolean>("SO_REUSEADDR", Boolean.class); + + /** + * Linger on close if data is present. + * + * <p> The value of this socket option is an {@code Integer} that controls + * the action taken when unsent data is queued on the socket and a method + * to close the socket is invoked. If the value of the socket option is zero + * or greater, then it represents a timeout value, in seconds, known as the + * <em>linger interval</em>. The linger interval is the timeout for the + * {@code close} method to block while the operating system attempts to + * transmit the unsent data or it decides that it is unable to transmit the + * data. If the value of the socket option is less than zero then the option + * is disabled. In that case the {@code close} method does not wait until + * unsent data is transmitted; if possible the operating system will transmit + * any unsent data before the connection is closed. + * + * <p> This socket option is intended for use with sockets that are configured + * in {@link java.nio.channels.SelectableChannel#isBlocking() blocking} mode + * only. The behavior of the {@code close} method when this option is + * enabled on a non-blocking socket is not defined. + * + * <p> The initial value of this socket option is a negative value, meaning + * that the option is disabled. The option may be enabled, or the linger + * interval changed, at any time. The maximum value of the linger interval + * is system dependent. Setting the linger interval to a value that is + * greater than its maximum value causes the linger interval to be set to + * its maximum value. + * + * @see Socket#setSoLinger + */ + public static final SocketOption<Integer> SO_LINGER = + new StdSocketOption<Integer>("SO_LINGER", Integer.class); + + + // -- IPPROTO_IP -- + + /** + * The Type of Service (ToS) octet in the Internet Protocol (IP) header. + * + * <p> The value of this socket option is an {@code Integer} representing + * the value of the ToS octet in IP packets sent by sockets to an {@link + * StandardProtocolFamily#INET IPv4} socket. The interpretation of the ToS + * octet is network specific and is not defined by this class. Further + * information on the ToS octet can be found in <a + * href="http://www.ietf.org/rfc/rfc1349.txt">RFC 1349</a> and <a + * href="http://www.ietf.org/rfc/rfc2474.txt">RFC 2474</a>. The value + * of the socket option is a <em>hint</em>. An implementation may ignore the + * value, or ignore specific values. + * + * <p> The initial/default value of the TOS field in the ToS octet is + * implementation specific but will typically be {@code 0}. For + * datagram-oriented sockets the option may be configured at any time after + * the socket has been bound. The new value of the octet is used when sending + * subsequent datagrams. It is system dependent whether this option can be + * queried or changed prior to binding the socket. + * + * <p> The behavior of this socket option on a stream-oriented socket, or an + * {@link StandardProtocolFamily#INET6 IPv6} socket, is not defined in this + * release. + * + * @see DatagramSocket#setTrafficClass + */ + public static final SocketOption<Integer> IP_TOS = + new StdSocketOption<Integer>("IP_TOS", Integer.class); + + /** + * The network interface for Internet Protocol (IP) multicast datagrams. + * + * <p> The value of this socket option is a {@link NetworkInterface} that + * represents the outgoing interface for multicast datagrams sent by the + * datagram-oriented socket. For {@link StandardProtocolFamily#INET6 IPv6} + * sockets then it is system dependent whether setting this option also + * sets the outgoing interface for multicast datagrams sent to IPv4 + * addresses. + * + * <p> The initial/default value of this socket option may be {@code null} + * to indicate that outgoing interface will be selected by the operating + * system, typically based on the network routing tables. An implementation + * allows this socket option to be set after the socket is bound. Whether + * the socket option can be queried or changed prior to binding the socket + * is system dependent. + * + * @see java.nio.channels.MulticastChannel + * @see MulticastSocket#setInterface + */ + public static final SocketOption<NetworkInterface> IP_MULTICAST_IF = + new StdSocketOption<NetworkInterface>("IP_MULTICAST_IF", NetworkInterface.class); + + /** + * The <em>time-to-live</em> for Internet Protocol (IP) multicast datagrams. + * + * <p> The value of this socket option is an {@code Integer} in the range + * {@code 0 <= value <= 255}. It is used to control the scope of multicast + * datagrams sent by the datagram-oriented socket. + * In the case of an {@link StandardProtocolFamily#INET IPv4} socket + * the option is the time-to-live (TTL) on multicast datagrams sent by the + * socket. Datagrams with a TTL of zero are not transmitted on the network + * but may be delivered locally. In the case of an {@link + * StandardProtocolFamily#INET6 IPv6} socket the option is the + * <em>hop limit</em> which is number of <em>hops</em> that the datagram can + * pass through before expiring on the network. For IPv6 sockets it is + * system dependent whether the option also sets the <em>time-to-live</em> + * on multicast datagrams sent to IPv4 addresses. + * + * <p> The initial/default value of the time-to-live setting is typically + * {@code 1}. An implementation allows this socket option to be set after + * the socket is bound. Whether the socket option can be queried or changed + * prior to binding the socket is system dependent. + * + * @see java.nio.channels.MulticastChannel + * @see MulticastSocket#setTimeToLive + */ + public static final SocketOption<Integer> IP_MULTICAST_TTL = + new StdSocketOption<Integer>("IP_MULTICAST_TTL", Integer.class); + + /** + * Loopback for Internet Protocol (IP) multicast datagrams. + * + * <p> The value of this socket option is a {@code Boolean} that controls + * the <em>loopback</em> of multicast datagrams. The value of the socket + * option represents if the option is enabled or disabled. + * + * <p> The exact semantics of this socket options are system dependent. + * In particular, it is system dependent whether the loopback applies to + * multicast datagrams sent from the socket or received by the socket. + * For {@link StandardProtocolFamily#INET6 IPv6} sockets then it is + * system dependent whether the option also applies to multicast datagrams + * sent to IPv4 addresses. + * + * <p> The initial/default value of this socket option is {@code TRUE}. An + * implementation allows this socket option to be set after the socket is + * bound. Whether the socket option can be queried or changed prior to + * binding the socket is system dependent. + * + * @see java.nio.channels.MulticastChannel + * @see MulticastSocket#setLoopbackMode + */ + public static final SocketOption<Boolean> IP_MULTICAST_LOOP = + new StdSocketOption<Boolean>("IP_MULTICAST_LOOP", Boolean.class); + + + // -- IPPROTO_TCP -- + + /** + * Disable the Nagle algorithm. + * + * <p> The value of this socket option is a {@code Boolean} that represents + * whether the option is enabled or disabled. The socket option is specific to + * stream-oriented sockets using the TCP/IP protocol. TCP/IP uses an algorithm + * known as <em>The Nagle Algorithm</em> to coalesce short segments and + * improve network efficiency. + * + * <p> The default value of this socket option is {@code FALSE}. The + * socket option should only be enabled in cases where it is known that the + * coalescing impacts performance. The socket option may be enabled at any + * time. In other words, the Nagle Algorithm can be disabled. Once the option + * is enabled, it is system dependent whether it can be subsequently + * disabled. If it cannot, then invoking the {@code setOption} method to + * disable the option has no effect. + * + * @see <a href="http://www.ietf.org/rfc/rfc1122.txt">RFC 1122: + * Requirements for Internet Hosts -- Communication Layers</a> + * @see Socket#setTcpNoDelay + */ + public static final SocketOption<Boolean> TCP_NODELAY = + new StdSocketOption<Boolean>("TCP_NODELAY", Boolean.class); + + + private static class StdSocketOption<T> implements SocketOption<T> { + private final String name; + private final Class<T> type; + StdSocketOption(String name, Class<T> type) { + this.name = name; + this.type = type; + } + @Override public String name() { return name; } + @Override public Class<T> type() { return type; } + @Override public String toString() { return name; } + } +}
diff --git a/java/net/URI.java b/java/net/URI.java new file mode 100644 index 0000000..ebba41b --- /dev/null +++ b/java/net/URI.java
@@ -0,0 +1,3602 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; +import java.nio.charset.CharacterCodingException; +import java.text.Normalizer; +import sun.nio.cs.ThreadLocalCoders; + +import java.lang.Character; // for javadoc +import java.lang.NullPointerException; // for javadoc + + +// Android-changed: Reformat @see links. +/** + * Represents a Uniform Resource Identifier (URI) reference. + * + * <p> Aside from some minor deviations noted below, an instance of this + * class represents a URI reference as defined by + * <a href="http://www.ietf.org/rfc/rfc2396.txt"><i>RFC 2396: Uniform + * Resource Identifiers (URI): Generic Syntax</i></a>, amended by <a + * href="http://www.ietf.org/rfc/rfc2732.txt"><i>RFC 2732: Format for + * Literal IPv6 Addresses in URLs</i></a>. The Literal IPv6 address format + * also supports scope_ids. The syntax and usage of scope_ids is described + * <a href="Inet6Address.html#scoped">here</a>. + * This class provides constructors for creating URI instances from + * their components or by parsing their string forms, methods for accessing the + * various components of an instance, and methods for normalizing, resolving, + * and relativizing URI instances. Instances of this class are immutable. + * + * + * <h3> URI syntax and components </h3> + * + * At the highest level a URI reference (hereinafter simply "URI") in string + * form has the syntax + * + * <blockquote> + * [<i>scheme</i><b>{@code :}</b>]<i>scheme-specific-part</i>[<b>{@code #}</b><i>fragment</i>] + * </blockquote> + * + * where square brackets [...] delineate optional components and the characters + * <b>{@code :}</b> and <b>{@code #}</b> stand for themselves. + * + * <p> An <i>absolute</i> URI specifies a scheme; a URI that is not absolute is + * said to be <i>relative</i>. URIs are also classified according to whether + * they are <i>opaque</i> or <i>hierarchical</i>. + * + * <p> An <i>opaque</i> URI is an absolute URI whose scheme-specific part does + * not begin with a slash character ({@code '/'}). Opaque URIs are not + * subject to further parsing. Some examples of opaque URIs are: + * + * <blockquote><table cellpadding=0 cellspacing=0 summary="layout"> + * <tr><td>{@code mailto:[email protected]}<td></tr> + * <tr><td>{@code news:comp.lang.java}<td></tr> + * <tr><td>{@code urn:isbn:096139210x}</td></tr> + * </table></blockquote> + * + * <p> A <i>hierarchical</i> URI is either an absolute URI whose + * scheme-specific part begins with a slash character, or a relative URI, that + * is, a URI that does not specify a scheme. Some examples of hierarchical + * URIs are: + * + * <blockquote> + * {@code http://java.sun.com/j2se/1.3/}<br> + * {@code docs/guide/collections/designfaq.html#28}<br> + * {@code ../../../demo/jfc/SwingSet2/src/SwingSet2.java}<br> + * {@code file:///~/calendar} + * </blockquote> + * + * <p> A hierarchical URI is subject to further parsing according to the syntax + * + * <blockquote> + * [<i>scheme</i><b>{@code :}</b>][<b>{@code //}</b><i>authority</i>][<i>path</i>][<b>{@code ?}</b><i>query</i>][<b>{@code #}</b><i>fragment</i>] + * </blockquote> + * + * where the characters <b>{@code :}</b>, <b>{@code /}</b>, + * <b>{@code ?}</b>, and <b>{@code #}</b> stand for themselves. The + * scheme-specific part of a hierarchical URI consists of the characters + * between the scheme and fragment components. + * + * <p> The authority component of a hierarchical URI is, if specified, either + * <i>server-based</i> or <i>registry-based</i>. A server-based authority + * parses according to the familiar syntax + * + * <blockquote> + * [<i>user-info</i><b>{@code @}</b>]<i>host</i>[<b>{@code :}</b><i>port</i>] + * </blockquote> + * + * where the characters <b>{@code @}</b> and <b>{@code :}</b> stand for + * themselves. Nearly all URI schemes currently in use are server-based. An + * authority component that does not parse in this way is considered to be + * registry-based. + * + * <p> The path component of a hierarchical URI is itself said to be absolute + * if it begins with a slash character ({@code '/'}); otherwise it is + * relative. The path of a hierarchical URI that is either absolute or + * specifies an authority is always absolute. + * + * <p> All told, then, a URI instance has the following nine components: + * + * <blockquote><table summary="Describes the components of a URI:scheme,scheme-specific-part,authority,user-info,host,port,path,query,fragment"> + * <tr><th><i>Component</i></th><th><i>Type</i></th></tr> + * <tr><td>scheme</td><td>{@code String}</td></tr> + * <tr><td>scheme-specific-part </td><td>{@code String}</td></tr> + * <tr><td>authority</td><td>{@code String}</td></tr> + * <tr><td>user-info</td><td>{@code String}</td></tr> + * <tr><td>host</td><td>{@code String}</td></tr> + * <tr><td>port</td><td>{@code int}</td></tr> + * <tr><td>path</td><td>{@code String}</td></tr> + * <tr><td>query</td><td>{@code String}</td></tr> + * <tr><td>fragment</td><td>{@code String}</td></tr> + * </table></blockquote> + * + * In a given instance any particular component is either <i>undefined</i> or + * <i>defined</i> with a distinct value. Undefined string components are + * represented by {@code null}, while undefined integer components are + * represented by {@code -1}. A string component may be defined to have the + * empty string as its value; this is not equivalent to that component being + * undefined. + * + * <p> Whether a particular component is or is not defined in an instance + * depends upon the type of the URI being represented. An absolute URI has a + * scheme component. An opaque URI has a scheme, a scheme-specific part, and + * possibly a fragment, but has no other components. A hierarchical URI always + * has a path (though it may be empty) and a scheme-specific-part (which at + * least contains the path), and may have any of the other components. If the + * authority component is present and is server-based then the host component + * will be defined and the user-information and port components may be defined. + * + * + * <h4> Operations on URI instances </h4> + * + * The key operations supported by this class are those of + * <i>normalization</i>, <i>resolution</i>, and <i>relativization</i>. + * + * <p> <i>Normalization</i> is the process of removing unnecessary {@code "."} + * and {@code ".."} segments from the path component of a hierarchical URI. + * Each {@code "."} segment is simply removed. A {@code ".."} segment is + * removed only if it is preceded by a non-{@code ".."} segment. + * Normalization has no effect upon opaque URIs. + * + * <p> <i>Resolution</i> is the process of resolving one URI against another, + * <i>base</i> URI. The resulting URI is constructed from components of both + * URIs in the manner specified by RFC 2396, taking components from the + * base URI for those not specified in the original. For hierarchical URIs, + * the path of the original is resolved against the path of the base and then + * normalized. The result, for example, of resolving + * + * <blockquote> + * {@code docs/guide/collections/designfaq.html#28} + * + * (1) + * </blockquote> + * + * against the base URI {@code http://java.sun.com/j2se/1.3/} is the result + * URI + * + * <blockquote> + * {@code http://java.sun.com/j2se/1.3/docs/guide/collections/designfaq.html#28} + * </blockquote> + * + * Resolving the relative URI + * + * <blockquote> + * {@code ../../../demo/jfc/SwingSet2/src/SwingSet2.java} (2) + * </blockquote> + * + * against this result yields, in turn, + * + * <blockquote> + * {@code http://java.sun.com/j2se/1.3/demo/jfc/SwingSet2/src/SwingSet2.java} + * </blockquote> + * + * Resolution of both absolute and relative URIs, and of both absolute and + * relative paths in the case of hierarchical URIs, is supported. Resolving + * the URI {@code file:///~calendar} against any other URI simply yields the + * original URI, since it is absolute. Resolving the relative URI (2) above + * against the relative base URI (1) yields the normalized, but still relative, + * URI + * + * <blockquote> + * {@code demo/jfc/SwingSet2/src/SwingSet2.java} + * </blockquote> + * + * <p> <i>Relativization</i>, finally, is the inverse of resolution: For any + * two normalized URIs <i>u</i> and <i>v</i>, + * + * <blockquote> + * <i>u</i>{@code .relativize(}<i>u</i>{@code .resolve(}<i>v</i>{@code )).equals(}<i>v</i>{@code )} and<br> + * <i>u</i>{@code .resolve(}<i>u</i>{@code .relativize(}<i>v</i>{@code )).equals(}<i>v</i>{@code )} .<br> + * </blockquote> + * + * This operation is often useful when constructing a document containing URIs + * that must be made relative to the base URI of the document wherever + * possible. For example, relativizing the URI + * + * <blockquote> + * {@code http://java.sun.com/j2se/1.3/docs/guide/index.html} + * </blockquote> + * + * against the base URI + * + * <blockquote> + * {@code http://java.sun.com/j2se/1.3} + * </blockquote> + * + * yields the relative URI {@code docs/guide/index.html}. + * + * + * <h4> Character categories </h4> + * + * RFC 2396 specifies precisely which characters are permitted in the + * various components of a URI reference. The following categories, most of + * which are taken from that specification, are used below to describe these + * constraints: + * + * <blockquote><table cellspacing=2 summary="Describes categories alpha,digit,alphanum,unreserved,punct,reserved,escaped,and other"> + * <tr><th valign=top><i>alpha</i></th> + * <td>The US-ASCII alphabetic characters, + * {@code 'A'} through {@code 'Z'} + * and {@code 'a'} through {@code 'z'}</td></tr> + * <tr><th valign=top><i>digit</i></th> + * <td>The US-ASCII decimal digit characters, + * {@code '0'} through {@code '9'}</td></tr> + * <tr><th valign=top><i>alphanum</i></th> + * <td>All <i>alpha</i> and <i>digit</i> characters</td></tr> + * <tr><th valign=top><i>unreserved</i> </th> + * <td>All <i>alphanum</i> characters together with those in the string + * {@code "_-!.~'()*"}</td></tr> + * <tr><th valign=top><i>punct</i></th> + * <td>The characters in the string {@code ",;:$&+="}</td></tr> + * <tr><th valign=top><i>reserved</i></th> + * <td>All <i>punct</i> characters together with those in the string + * {@code "?/[]@"}</td></tr> + * <tr><th valign=top><i>escaped</i></th> + * <td>Escaped octets, that is, triplets consisting of the percent + * character ({@code '%'}) followed by two hexadecimal digits + * ({@code '0'}-{@code '9'}, {@code 'A'}-{@code 'F'}, and + * {@code 'a'}-{@code 'f'})</td></tr> + * <tr><th valign=top><i>other</i></th> + * <td>The Unicode characters that are not in the US-ASCII character set, + * are not control characters (according to the {@link + * java.lang.Character#isISOControl(char) Character.isISOControl} + * method), and are not space characters (according to the {@link + * java.lang.Character#isSpaceChar(char) Character.isSpaceChar} + * method) <i>(<b>Deviation from RFC 2396</b>, which is + * limited to US-ASCII)</i></td></tr> + * </table></blockquote> + * + * <p><a name="legal-chars"></a> The set of all legal URI characters consists of + * the <i>unreserved</i>, <i>reserved</i>, <i>escaped</i>, and <i>other</i> + * characters. + * + * + * <h4> Escaped octets, quotation, encoding, and decoding </h4> + * + * RFC 2396 allows escaped octets to appear in the user-info, path, query, and + * fragment components. Escaping serves two purposes in URIs: + * + * <ul> + * + * <li><p> To <i>encode</i> non-US-ASCII characters when a URI is required to + * conform strictly to RFC 2396 by not containing any <i>other</i> + * characters. </p></li> + * + * <li><p> To <i>quote</i> characters that are otherwise illegal in a + * component. The user-info, path, query, and fragment components differ + * slightly in terms of which characters are considered legal and illegal. + * </p></li> + * + * </ul> + * + * These purposes are served in this class by three related operations: + * + * <ul> + * + * <li><p><a name="encode"></a> A character is <i>encoded</i> by replacing it + * with the sequence of escaped octets that represent that character in the + * UTF-8 character set. The Euro currency symbol ({@code '\u005Cu20AC'}), + * for example, is encoded as {@code "%E2%82%AC"}. <i>(<b>Deviation from + * RFC 2396</b>, which does not specify any particular character + * set.)</i> </p></li> + * + * <li><p><a name="quote"></a> An illegal character is <i>quoted</i> simply by + * encoding it. The space character, for example, is quoted by replacing it + * with {@code "%20"}. UTF-8 contains US-ASCII, hence for US-ASCII + * characters this transformation has exactly the effect required by + * RFC 2396. </p></li> + * + * <li><p><a name="decode"></a> + * A sequence of escaped octets is <i>decoded</i> by + * replacing it with the sequence of characters that it represents in the + * UTF-8 character set. UTF-8 contains US-ASCII, hence decoding has the + * effect of de-quoting any quoted US-ASCII characters as well as that of + * decoding any encoded non-US-ASCII characters. If a <a + * href="../nio/charset/CharsetDecoder.html#ce">decoding error</a> occurs + * when decoding the escaped octets then the erroneous octets are replaced by + * {@code '\u005CuFFFD'}, the Unicode replacement character. </p></li> + * + * </ul> + * + * These operations are exposed in the constructors and methods of this class + * as follows: + * + * <ul> + * + * <li><p> The {@linkplain #URI(java.lang.String) single-argument + * constructor} requires any illegal characters in its argument to be + * quoted and preserves any escaped octets and <i>other</i> characters that + * are present. </p></li> + * + * <li><p> The {@linkplain + * #URI(java.lang.String,java.lang.String,java.lang.String,int,java.lang.String,java.lang.String,java.lang.String) + * multi-argument constructors} quote illegal characters as + * required by the components in which they appear. The percent character + * ({@code '%'}) is always quoted by these constructors. Any <i>other</i> + * characters are preserved. </p></li> + * + * <li><p> The {@link #getRawUserInfo() getRawUserInfo}, {@link #getRawPath() + * getRawPath}, {@link #getRawQuery() getRawQuery}, {@link #getRawFragment() + * getRawFragment}, {@link #getRawAuthority() getRawAuthority}, and {@link + * #getRawSchemeSpecificPart() getRawSchemeSpecificPart} methods return the + * values of their corresponding components in raw form, without interpreting + * any escaped octets. The strings returned by these methods may contain + * both escaped octets and <i>other</i> characters, and will not contain any + * illegal characters. </p></li> + * + * <li><p> The {@link #getUserInfo() getUserInfo}, {@link #getPath() + * getPath}, {@link #getQuery() getQuery}, {@link #getFragment() + * getFragment}, {@link #getAuthority() getAuthority}, and {@link + * #getSchemeSpecificPart() getSchemeSpecificPart} methods decode any escaped + * octets in their corresponding components. The strings returned by these + * methods may contain both <i>other</i> characters and illegal characters, + * and will not contain any escaped octets. </p></li> + * + * <li><p> The {@link #toString() toString} method returns a URI string with + * all necessary quotation but which may contain <i>other</i> characters. + * </p></li> + * + * <li><p> The {@link #toASCIIString() toASCIIString} method returns a fully + * quoted and encoded URI string that does not contain any <i>other</i> + * characters. </p></li> + * + * </ul> + * + * + * <h4> Identities </h4> + * + * For any URI <i>u</i>, it is always the case that + * + * <blockquote> + * {@code new URI(}<i>u</i>{@code .toString()).equals(}<i>u</i>{@code )} . + * </blockquote> + * + * For any URI <i>u</i> that does not contain redundant syntax such as two + * slashes before an empty authority (as in {@code file:///tmp/} ) or a + * colon following a host name but no port (as in + * {@code http://java.sun.com:} ), and that does not encode characters + * except those that must be quoted, the following identities also hold: + * <pre> + * new URI(<i>u</i>.getScheme(), + * <i>u</i>.getSchemeSpecificPart(), + * <i>u</i>.getFragment()) + * .equals(<i>u</i>)</pre> + * in all cases, + * <pre> + * new URI(<i>u</i>.getScheme(), + * <i>u</i>.getUserInfo(), <i>u</i>.getAuthority(), + * <i>u</i>.getPath(), <i>u</i>.getQuery(), + * <i>u</i>.getFragment()) + * .equals(<i>u</i>)</pre> + * if <i>u</i> is hierarchical, and + * <pre> + * new URI(<i>u</i>.getScheme(), + * <i>u</i>.getUserInfo(), <i>u</i>.getHost(), <i>u</i>.getPort(), + * <i>u</i>.getPath(), <i>u</i>.getQuery(), + * <i>u</i>.getFragment()) + * .equals(<i>u</i>)</pre> + * if <i>u</i> is hierarchical and has either no authority or a server-based + * authority. + * + * + * <h4> URIs, URLs, and URNs </h4> + * + * A URI is a uniform resource <i>identifier</i> while a URL is a uniform + * resource <i>locator</i>. Hence every URL is a URI, abstractly speaking, but + * not every URI is a URL. This is because there is another subcategory of + * URIs, uniform resource <i>names</i> (URNs), which name resources but do not + * specify how to locate them. The {@code mailto}, {@code news}, and + * {@code isbn} URIs shown above are examples of URNs. + * + * <p> The conceptual distinction between URIs and URLs is reflected in the + * differences between this class and the {@link URL} class. + * + * <p> An instance of this class represents a URI reference in the syntactic + * sense defined by RFC 2396. A URI may be either absolute or relative. + * A URI string is parsed according to the generic syntax without regard to the + * scheme, if any, that it specifies. No lookup of the host, if any, is + * performed, and no scheme-dependent stream handler is constructed. Equality, + * hashing, and comparison are defined strictly in terms of the character + * content of the instance. In other words, a URI instance is little more than + * a structured string that supports the syntactic, scheme-independent + * operations of comparison, normalization, resolution, and relativization. + * + * <p> An instance of the {@link URL} class, by contrast, represents the + * syntactic components of a URL together with some of the information required + * to access the resource that it describes. A URL must be absolute, that is, + * it must always specify a scheme. A URL string is parsed according to its + * scheme. A stream handler is always established for a URL, and in fact it is + * impossible to create a URL instance for a scheme for which no handler is + * available. Equality and hashing depend upon both the scheme and the + * Internet address of the host, if any; comparison is not defined. In other + * words, a URL is a structured string that supports the syntactic operation of + * resolution as well as the network I/O operations of looking up the host and + * opening a connection to the specified resource. + * + * + * @author Mark Reinhold + * @since 1.4 + * + * @see <a href="http://www.ietf.org/rfc/rfc2279.txt">RFC 2279: UTF-8, a transformation format of ISO 10646</a> + * @see <a href="http://www.ietf.org/rfc/rfc2373.txt">RFC 2373: IPv6 Addressing Architecture</a> + * @see <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396: Uniform Resource Identifiers (URI): Generic Syntax</a> + * @see <a href="http://www.ietf.org/rfc/rfc2732.txt">RFC 2732: Format for Literal IPv6 Addresses in URLs</a> + */ + +public final class URI + implements Comparable<URI>, Serializable +{ + + // Note: Comments containing the word "ASSERT" indicate places where a + // throw of an InternalError should be replaced by an appropriate assertion + // statement once asserts are enabled in the build. + + static final long serialVersionUID = -6052424284110960213L; + + + // -- Properties and components of this instance -- + + // Components of all URIs: [<scheme>:]<scheme-specific-part>[#<fragment>] + private transient String scheme; // null ==> relative URI + private transient String fragment; + + // Hierarchical URI components: [//<authority>]<path>[?<query>] + private transient String authority; // Registry or server + + // Server-based authority: [<userInfo>@]<host>[:<port>] + private transient String userInfo; + private transient String host; // null ==> registry-based + private transient int port = -1; // -1 ==> undefined + + // Remaining components of hierarchical URIs + private transient String path; // null ==> opaque + private transient String query; + + // The remaining fields may be computed on demand + + private volatile transient String schemeSpecificPart; + private volatile transient int hash; // Zero ==> undefined + + private volatile transient String decodedUserInfo = null; + private volatile transient String decodedAuthority = null; + private volatile transient String decodedPath = null; + private volatile transient String decodedQuery = null; + private volatile transient String decodedFragment = null; + private volatile transient String decodedSchemeSpecificPart = null; + + /** + * The string form of this URI. + * + * @serial + */ + private volatile String string; // The only serializable field + + + + // -- Constructors and factories -- + + private URI() { } // Used internally + + /** + * Constructs a URI by parsing the given string. + * + * <p> This constructor parses the given string exactly as specified by the + * grammar in <a + * href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>, + * Appendix A, <b><i>except for the following deviations:</i></b> </p> + * + * <ul> + * + * <li><p> An empty authority component is permitted as long as it is + * followed by a non-empty path, a query component, or a fragment + * component. This allows the parsing of URIs such as + * {@code "file:///foo/bar"}, which seems to be the intent of + * RFC 2396 although the grammar does not permit it. If the + * authority component is empty then the user-information, host, and port + * components are undefined. </p></li> + * + * <li><p> Empty relative paths are permitted; this seems to be the + * intent of RFC 2396 although the grammar does not permit it. The + * primary consequence of this deviation is that a standalone fragment + * such as {@code "#foo"} parses as a relative URI with an empty path + * and the given fragment, and can be usefully <a + * href="#resolve-frag">resolved</a> against a base URI. + * + * <li><p> IPv4 addresses in host components are parsed rigorously, as + * specified by <a + * href="http://www.ietf.org/rfc/rfc2732.txt">RFC 2732</a>: Each + * element of a dotted-quad address must contain no more than three + * decimal digits. Each element is further constrained to have a value + * no greater than 255. </p></li> + * + * <li> <p> Hostnames in host components that comprise only a single + * domain label are permitted to start with an <i>alphanum</i> + * character. This seems to be the intent of <a + * href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a> + * section 3.2.2 although the grammar does not permit it. The + * consequence of this deviation is that the authority component of a + * hierarchical URI such as {@code s://123}, will parse as a server-based + * authority. </p></li> + * + * <li><p> IPv6 addresses are permitted for the host component. An IPv6 + * address must be enclosed in square brackets ({@code '['} and + * {@code ']'}) as specified by <a + * href="http://www.ietf.org/rfc/rfc2732.txt">RFC 2732</a>. The + * IPv6 address itself must parse according to <a + * href="http://www.ietf.org/rfc/rfc2373.txt">RFC 2373</a>. IPv6 + * addresses are further constrained to describe no more than sixteen + * bytes of address information, a constraint implicit in RFC 2373 + * but not expressible in the grammar. </p></li> + * + * <li><p> Characters in the <i>other</i> category are permitted wherever + * RFC 2396 permits <i>escaped</i> octets, that is, in the + * user-information, path, query, and fragment components, as well as in + * the authority component if the authority is registry-based. This + * allows URIs to contain Unicode characters beyond those in the US-ASCII + * character set. </p></li> + * + * </ul> + * + * @param str The string to be parsed into a URI + * + * @throws NullPointerException + * If {@code str} is {@code null} + * + * @throws URISyntaxException + * If the given string violates RFC 2396, as augmented + * by the above deviations + */ + public URI(String str) throws URISyntaxException { + new Parser(str).parse(false); + } + + /** + * Constructs a hierarchical URI from the given components. + * + * <p> If a scheme is given then the path, if also given, must either be + * empty or begin with a slash character ({@code '/'}). Otherwise a + * component of the new URI may be left undefined by passing {@code null} + * for the corresponding parameter or, in the case of the {@code port} + * parameter, by passing {@code -1}. + * + * <p> This constructor first builds a URI string from the given components + * according to the rules specified in <a + * href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>, + * section 5.2, step 7: </p> + * + * <ol> + * + * <li><p> Initially, the result string is empty. </p></li> + * + * <li><p> If a scheme is given then it is appended to the result, + * followed by a colon character ({@code ':'}). </p></li> + * + * <li><p> If user information, a host, or a port are given then the + * string {@code "//"} is appended. </p></li> + * + * <li><p> If user information is given then it is appended, followed by + * a commercial-at character ({@code '@'}). Any character not in the + * <i>unreserved</i>, <i>punct</i>, <i>escaped</i>, or <i>other</i> + * categories is <a href="#quote">quoted</a>. </p></li> + * + * <li><p> If a host is given then it is appended. If the host is a + * literal IPv6 address but is not enclosed in square brackets + * ({@code '['} and {@code ']'}) then the square brackets are added. + * </p></li> + * + * <li><p> If a port number is given then a colon character + * ({@code ':'}) is appended, followed by the port number in decimal. + * </p></li> + * + * <li><p> If a path is given then it is appended. Any character not in + * the <i>unreserved</i>, <i>punct</i>, <i>escaped</i>, or <i>other</i> + * categories, and not equal to the slash character ({@code '/'}) or the + * commercial-at character ({@code '@'}), is quoted. </p></li> + * + * <li><p> If a query is given then a question-mark character + * ({@code '?'}) is appended, followed by the query. Any character that + * is not a <a href="#legal-chars">legal URI character</a> is quoted. + * </p></li> + * + * <li><p> Finally, if a fragment is given then a hash character + * ({@code '#'}) is appended, followed by the fragment. Any character + * that is not a legal URI character is quoted. </p></li> + * + * </ol> + * + * <p> The resulting URI string is then parsed as if by invoking the {@link + * #URI(String)} constructor and then invoking the {@link + * #parseServerAuthority()} method upon the result; this may cause a {@link + * URISyntaxException} to be thrown. </p> + * + * @param scheme Scheme name + * @param userInfo User name and authorization information + * @param host Host name + * @param port Port number + * @param path Path + * @param query Query + * @param fragment Fragment + * + * @throws URISyntaxException + * If both a scheme and a path are given but the path is relative, + * if the URI string constructed from the given components violates + * RFC 2396, or if the authority component of the string is + * present but cannot be parsed as a server-based authority + */ + public URI(String scheme, + String userInfo, String host, int port, + String path, String query, String fragment) + throws URISyntaxException + { + String s = toString(scheme, null, + null, userInfo, host, port, + path, query, fragment); + checkPath(s, scheme, path); + new Parser(s).parse(true); + } + + /** + * Constructs a hierarchical URI from the given components. + * + * <p> If a scheme is given then the path, if also given, must either be + * empty or begin with a slash character ({@code '/'}). Otherwise a + * component of the new URI may be left undefined by passing {@code null} + * for the corresponding parameter. + * + * <p> This constructor first builds a URI string from the given components + * according to the rules specified in <a + * href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>, + * section 5.2, step 7: </p> + * + * <ol> + * + * <li><p> Initially, the result string is empty. </p></li> + * + * <li><p> If a scheme is given then it is appended to the result, + * followed by a colon character ({@code ':'}). </p></li> + * + * <li><p> If an authority is given then the string {@code "//"} is + * appended, followed by the authority. If the authority contains a + * literal IPv6 address then the address must be enclosed in square + * brackets ({@code '['} and {@code ']'}). Any character not in the + * <i>unreserved</i>, <i>punct</i>, <i>escaped</i>, or <i>other</i> + * categories, and not equal to the commercial-at character + * ({@code '@'}), is <a href="#quote">quoted</a>. </p></li> + * + * <li><p> If a path is given then it is appended. Any character not in + * the <i>unreserved</i>, <i>punct</i>, <i>escaped</i>, or <i>other</i> + * categories, and not equal to the slash character ({@code '/'}) or the + * commercial-at character ({@code '@'}), is quoted. </p></li> + * + * <li><p> If a query is given then a question-mark character + * ({@code '?'}) is appended, followed by the query. Any character that + * is not a <a href="#legal-chars">legal URI character</a> is quoted. + * </p></li> + * + * <li><p> Finally, if a fragment is given then a hash character + * ({@code '#'}) is appended, followed by the fragment. Any character + * that is not a legal URI character is quoted. </p></li> + * + * </ol> + * + * <p> The resulting URI string is then parsed as if by invoking the {@link + * #URI(String)} constructor and then invoking the {@link + * #parseServerAuthority()} method upon the result; this may cause a {@link + * URISyntaxException} to be thrown. </p> + * + * @param scheme Scheme name + * @param authority Authority + * @param path Path + * @param query Query + * @param fragment Fragment + * + * @throws URISyntaxException + * If both a scheme and a path are given but the path is relative, + * if the URI string constructed from the given components violates + * RFC 2396, or if the authority component of the string is + * present but cannot be parsed as a server-based authority + */ + public URI(String scheme, + String authority, + String path, String query, String fragment) + throws URISyntaxException + { + String s = toString(scheme, null, + authority, null, null, -1, + path, query, fragment); + checkPath(s, scheme, path); + new Parser(s).parse(false); + } + + /** + * Constructs a hierarchical URI from the given components. + * + * <p> A component may be left undefined by passing {@code null}. + * + * <p> This convenience constructor works as if by invoking the + * seven-argument constructor as follows: + * + * <blockquote> + * {@code new} {@link #URI(String, String, String, int, String, String, String) + * URI}{@code (scheme, null, host, -1, path, null, fragment);} + * </blockquote> + * + * @param scheme Scheme name + * @param host Host name + * @param path Path + * @param fragment Fragment + * + * @throws URISyntaxException + * If the URI string constructed from the given components + * violates RFC 2396 + */ + public URI(String scheme, String host, String path, String fragment) + throws URISyntaxException + { + this(scheme, null, host, -1, path, null, fragment); + } + + /** + * Constructs a URI from the given components. + * + * <p> A component may be left undefined by passing {@code null}. + * + * <p> This constructor first builds a URI in string form using the given + * components as follows: </p> + * + * <ol> + * + * <li><p> Initially, the result string is empty. </p></li> + * + * <li><p> If a scheme is given then it is appended to the result, + * followed by a colon character ({@code ':'}). </p></li> + * + * <li><p> If a scheme-specific part is given then it is appended. Any + * character that is not a <a href="#legal-chars">legal URI character</a> + * is <a href="#quote">quoted</a>. </p></li> + * + * <li><p> Finally, if a fragment is given then a hash character + * ({@code '#'}) is appended to the string, followed by the fragment. + * Any character that is not a legal URI character is quoted. </p></li> + * + * </ol> + * + * <p> The resulting URI string is then parsed in order to create the new + * URI instance as if by invoking the {@link #URI(String)} constructor; + * this may cause a {@link URISyntaxException} to be thrown. </p> + * + * @param scheme Scheme name + * @param ssp Scheme-specific part + * @param fragment Fragment + * + * @throws URISyntaxException + * If the URI string constructed from the given components + * violates RFC 2396 + */ + public URI(String scheme, String ssp, String fragment) + throws URISyntaxException + { + new Parser(toString(scheme, ssp, + null, null, null, -1, + null, null, fragment)) + .parse(false); + } + + /** + * Creates a URI by parsing the given string. + * + * <p> This convenience factory method works as if by invoking the {@link + * #URI(String)} constructor; any {@link URISyntaxException} thrown by the + * constructor is caught and wrapped in a new {@link + * IllegalArgumentException} object, which is then thrown. + * + * <p> This method is provided for use in situations where it is known that + * the given string is a legal URI, for example for URI constants declared + * within in a program, and so it would be considered a programming error + * for the string not to parse as such. The constructors, which throw + * {@link URISyntaxException} directly, should be used situations where a + * URI is being constructed from user input or from some other source that + * may be prone to errors. </p> + * + * @param str The string to be parsed into a URI + * @return The new URI + * + * @throws NullPointerException + * If {@code str} is {@code null} + * + * @throws IllegalArgumentException + * If the given string violates RFC 2396 + */ + public static URI create(String str) { + try { + return new URI(str); + } catch (URISyntaxException x) { + throw new IllegalArgumentException(x.getMessage(), x); + } + } + + + // -- Operations -- + + /** + * Attempts to parse this URI's authority component, if defined, into + * user-information, host, and port components. + * + * <p> If this URI's authority component has already been recognized as + * being server-based then it will already have been parsed into + * user-information, host, and port components. In this case, or if this + * URI has no authority component, this method simply returns this URI. + * + * <p> Otherwise this method attempts once more to parse the authority + * component into user-information, host, and port components, and throws + * an exception describing why the authority component could not be parsed + * in that way. + * + * <p> This method is provided because the generic URI syntax specified in + * <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a> + * cannot always distinguish a malformed server-based authority from a + * legitimate registry-based authority. It must therefore treat some + * instances of the former as instances of the latter. The authority + * component in the URI string {@code "//foo:bar"}, for example, is not a + * legal server-based authority but it is legal as a registry-based + * authority. + * + * <p> In many common situations, for example when working URIs that are + * known to be either URNs or URLs, the hierarchical URIs being used will + * always be server-based. They therefore must either be parsed as such or + * treated as an error. In these cases a statement such as + * + * <blockquote> + * {@code URI }<i>u</i>{@code = new URI(str).parseServerAuthority();} + * </blockquote> + * + * <p> can be used to ensure that <i>u</i> always refers to a URI that, if + * it has an authority component, has a server-based authority with proper + * user-information, host, and port components. Invoking this method also + * ensures that if the authority could not be parsed in that way then an + * appropriate diagnostic message can be issued based upon the exception + * that is thrown. </p> + * + * @return A URI whose authority field has been parsed + * as a server-based authority + * + * @throws URISyntaxException + * If the authority component of this URI is defined + * but cannot be parsed as a server-based authority + * according to RFC 2396 + */ + public URI parseServerAuthority() + throws URISyntaxException + { + // We could be clever and cache the error message and index from the + // exception thrown during the original parse, but that would require + // either more fields or a more-obscure representation. + if ((host != null) || (authority == null)) + return this; + defineString(); + new Parser(string).parse(true); + return this; + } + + /** + * Normalizes this URI's path. + * + * <p> If this URI is opaque, or if its path is already in normal form, + * then this URI is returned. Otherwise a new URI is constructed that is + * identical to this URI except that its path is computed by normalizing + * this URI's path in a manner consistent with <a + * href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>, + * section 5.2, step 6, sub-steps c through f; that is: + * </p> + * + * <ol> + * + * <li><p> All {@code "."} segments are removed. </p></li> + * + * <li><p> If a {@code ".."} segment is preceded by a non-{@code ".."} + * segment then both of these segments are removed. This step is + * repeated until it is no longer applicable. </p></li> + * + * <li><p> If the path is relative, and if its first segment contains a + * colon character ({@code ':'}), then a {@code "."} segment is + * prepended. This prevents a relative URI with a path such as + * {@code "a:b/c/d"} from later being re-parsed as an opaque URI with a + * scheme of {@code "a"} and a scheme-specific part of {@code "b/c/d"}. + * <b><i>(Deviation from RFC 2396)</i></b> </p></li> + * + * </ol> + * + * <p> A normalized path will begin with one or more {@code ".."} segments + * if there were insufficient non-{@code ".."} segments preceding them to + * allow their removal. A normalized path will begin with a {@code "."} + * segment if one was inserted by step 3 above. Otherwise, a normalized + * path will not contain any {@code "."} or {@code ".."} segments. </p> + * + * @return A URI equivalent to this URI, + * but whose path is in normal form + */ + public URI normalize() { + return normalize(this); + } + + /** + * Resolves the given URI against this URI. + * + * <p> If the given URI is already absolute, or if this URI is opaque, then + * the given URI is returned. + * + * <p><a name="resolve-frag"></a> If the given URI's fragment component is + * defined, its path component is empty, and its scheme, authority, and + * query components are undefined, then a URI with the given fragment but + * with all other components equal to those of this URI is returned. This + * allows a URI representing a standalone fragment reference, such as + * {@code "#foo"}, to be usefully resolved against a base URI. + * + * <p> Otherwise this method constructs a new hierarchical URI in a manner + * consistent with <a + * href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>, + * section 5.2; that is: </p> + * + * <ol> + * + * <li><p> A new URI is constructed with this URI's scheme and the given + * URI's query and fragment components. </p></li> + * + * <li><p> If the given URI has an authority component then the new URI's + * authority and path are taken from the given URI. </p></li> + * + * <li><p> Otherwise the new URI's authority component is copied from + * this URI, and its path is computed as follows: </p> + * + * <ol> + * + * <li><p> If the given URI's path is absolute then the new URI's path + * is taken from the given URI. </p></li> + * + * <li><p> Otherwise the given URI's path is relative, and so the new + * URI's path is computed by resolving the path of the given URI + * against the path of this URI. This is done by concatenating all but + * the last segment of this URI's path, if any, with the given URI's + * path and then normalizing the result as if by invoking the {@link + * #normalize() normalize} method. </p></li> + * + * </ol></li> + * + * </ol> + * + * <p> The result of this method is absolute if, and only if, either this + * URI is absolute or the given URI is absolute. </p> + * + * @param uri The URI to be resolved against this URI + * @return The resulting URI + * + * @throws NullPointerException + * If {@code uri} is {@code null} + */ + public URI resolve(URI uri) { + return resolve(this, uri); + } + + /** + * Constructs a new URI by parsing the given string and then resolving it + * against this URI. + * + * <p> This convenience method works as if invoking it were equivalent to + * evaluating the expression {@link #resolve(java.net.URI) + * resolve}{@code (URI.}{@link #create(String) create}{@code (str))}. </p> + * + * @param str The string to be parsed into a URI + * @return The resulting URI + * + * @throws NullPointerException + * If {@code str} is {@code null} + * + * @throws IllegalArgumentException + * If the given string violates RFC 2396 + */ + public URI resolve(String str) { + return resolve(URI.create(str)); + } + + /** + * Relativizes the given URI against this URI. + * + * <p> The relativization of the given URI against this URI is computed as + * follows: </p> + * + * <ol> + * + * <li><p> If either this URI or the given URI are opaque, or if the + * scheme and authority components of the two URIs are not identical, or + * if the path of this URI is not a prefix of the path of the given URI, + * then the given URI is returned. </p></li> + * + * <li><p> Otherwise a new relative hierarchical URI is constructed with + * query and fragment components taken from the given URI and with a path + * component computed by removing this URI's path from the beginning of + * the given URI's path. </p></li> + * + * </ol> + * + * @param uri The URI to be relativized against this URI + * @return The resulting URI + * + * @throws NullPointerException + * If {@code uri} is {@code null} + */ + public URI relativize(URI uri) { + return relativize(this, uri); + } + + /** + * Constructs a URL from this URI. + * + * <p> This convenience method works as if invoking it were equivalent to + * evaluating the expression {@code new URL(this.toString())} after + * first checking that this URI is absolute. </p> + * + * @return A URL constructed from this URI + * + * @throws IllegalArgumentException + * If this URL is not absolute + * + * @throws MalformedURLException + * If a protocol handler for the URL could not be found, + * or if some other error occurred while constructing the URL + */ + public URL toURL() + throws MalformedURLException { + if (!isAbsolute()) + throw new IllegalArgumentException("URI is not absolute"); + return new URL(toString()); + } + + // -- Component access methods -- + + /** + * Returns the scheme component of this URI. + * + * <p> The scheme component of a URI, if defined, only contains characters + * in the <i>alphanum</i> category and in the string {@code "-.+"}. A + * scheme always starts with an <i>alpha</i> character. <p> + * + * The scheme component of a URI cannot contain escaped octets, hence this + * method does not perform any decoding. + * + * @return The scheme component of this URI, + * or {@code null} if the scheme is undefined + */ + public String getScheme() { + return scheme; + } + + /** + * Tells whether or not this URI is absolute. + * + * <p> A URI is absolute if, and only if, it has a scheme component. </p> + * + * @return {@code true} if, and only if, this URI is absolute + */ + public boolean isAbsolute() { + return scheme != null; + } + + /** + * Tells whether or not this URI is opaque. + * + * <p> A URI is opaque if, and only if, it is absolute and its + * scheme-specific part does not begin with a slash character ('/'). + * An opaque URI has a scheme, a scheme-specific part, and possibly + * a fragment; all other components are undefined. </p> + * + * @return {@code true} if, and only if, this URI is opaque + */ + public boolean isOpaque() { + return path == null; + } + + /** + * Returns the raw scheme-specific part of this URI. The scheme-specific + * part is never undefined, though it may be empty. + * + * <p> The scheme-specific part of a URI only contains legal URI + * characters. </p> + * + * @return The raw scheme-specific part of this URI + * (never {@code null}) + */ + public String getRawSchemeSpecificPart() { + defineSchemeSpecificPart(); + return schemeSpecificPart; + } + + /** + * Returns the decoded scheme-specific part of this URI. + * + * <p> The string returned by this method is equal to that returned by the + * {@link #getRawSchemeSpecificPart() getRawSchemeSpecificPart} method + * except that all sequences of escaped octets are <a + * href="#decode">decoded</a>. </p> + * + * @return The decoded scheme-specific part of this URI + * (never {@code null}) + */ + public String getSchemeSpecificPart() { + if (decodedSchemeSpecificPart == null) + decodedSchemeSpecificPart = decode(getRawSchemeSpecificPart()); + return decodedSchemeSpecificPart; + } + + /** + * Returns the raw authority component of this URI. + * + * <p> The authority component of a URI, if defined, only contains the + * commercial-at character ({@code '@'}) and characters in the + * <i>unreserved</i>, <i>punct</i>, <i>escaped</i>, and <i>other</i> + * categories. If the authority is server-based then it is further + * constrained to have valid user-information, host, and port + * components. </p> + * + * @return The raw authority component of this URI, + * or {@code null} if the authority is undefined + */ + public String getRawAuthority() { + return authority; + } + + /** + * Returns the decoded authority component of this URI. + * + * <p> The string returned by this method is equal to that returned by the + * {@link #getRawAuthority() getRawAuthority} method except that all + * sequences of escaped octets are <a href="#decode">decoded</a>. </p> + * + * @return The decoded authority component of this URI, + * or {@code null} if the authority is undefined + */ + public String getAuthority() { + if (decodedAuthority == null) + decodedAuthority = decode(authority); + return decodedAuthority; + } + + /** + * Returns the raw user-information component of this URI. + * + * <p> The user-information component of a URI, if defined, only contains + * characters in the <i>unreserved</i>, <i>punct</i>, <i>escaped</i>, and + * <i>other</i> categories. </p> + * + * @return The raw user-information component of this URI, + * or {@code null} if the user information is undefined + */ + public String getRawUserInfo() { + return userInfo; + } + + /** + * Returns the decoded user-information component of this URI. + * + * <p> The string returned by this method is equal to that returned by the + * {@link #getRawUserInfo() getRawUserInfo} method except that all + * sequences of escaped octets are <a href="#decode">decoded</a>. </p> + * + * @return The decoded user-information component of this URI, + * or {@code null} if the user information is undefined + */ + public String getUserInfo() { + if ((decodedUserInfo == null) && (userInfo != null)) + decodedUserInfo = decode(userInfo); + return decodedUserInfo; + } + + /** + * Returns the host component of this URI. + * + * <p> The host component of a URI, if defined, will have one of the + * following forms: </p> + * + * <ul> + * + * <li><p> A domain name consisting of one or more <i>labels</i> + * separated by period characters ({@code '.'}), optionally followed by + * a period character. Each label consists of <i>alphanum</i> characters + * as well as hyphen characters ({@code '-'}), though hyphens never + * occur as the first or last characters in a label. The rightmost + * label of a domain name consisting of two or more labels, begins + * with an <i>alpha</i> character. </li> + * + * <li><p> A dotted-quad IPv4 address of the form + * <i>digit</i>{@code +.}<i>digit</i>{@code +.}<i>digit</i>{@code +.}<i>digit</i>{@code +}, + * where no <i>digit</i> sequence is longer than three characters and no + * sequence has a value larger than 255. </p></li> + * + * <li><p> An IPv6 address enclosed in square brackets ({@code '['} and + * {@code ']'}) and consisting of hexadecimal digits, colon characters + * ({@code ':'}), and possibly an embedded IPv4 address. The full + * syntax of IPv6 addresses is specified in <a + * href="http://www.ietf.org/rfc/rfc2373.txt"><i>RFC 2373: IPv6 + * Addressing Architecture</i></a>. </p></li> + * + * </ul> + * + * The host component of a URI cannot contain escaped octets, hence this + * method does not perform any decoding. + * + * @return The host component of this URI, + * or {@code null} if the host is undefined + */ + public String getHost() { + return host; + } + + /** + * Returns the port number of this URI. + * + * <p> The port component of a URI, if defined, is a non-negative + * integer. </p> + * + * @return The port component of this URI, + * or {@code -1} if the port is undefined + */ + public int getPort() { + return port; + } + + /** + * Returns the raw path component of this URI. + * + * <p> The path component of a URI, if defined, only contains the slash + * character ({@code '/'}), the commercial-at character ({@code '@'}), + * and characters in the <i>unreserved</i>, <i>punct</i>, <i>escaped</i>, + * and <i>other</i> categories. </p> + * + * @return The path component of this URI, + * or {@code null} if the path is undefined + */ + public String getRawPath() { + return path; + } + + /** + * Returns the decoded path component of this URI. + * + * <p> The string returned by this method is equal to that returned by the + * {@link #getRawPath() getRawPath} method except that all sequences of + * escaped octets are <a href="#decode">decoded</a>. </p> + * + * @return The decoded path component of this URI, + * or {@code null} if the path is undefined + */ + public String getPath() { + if ((decodedPath == null) && (path != null)) + decodedPath = decode(path); + return decodedPath; + } + + /** + * Returns the raw query component of this URI. + * + * <p> The query component of a URI, if defined, only contains legal URI + * characters. </p> + * + * @return The raw query component of this URI, + * or {@code null} if the query is undefined + */ + public String getRawQuery() { + return query; + } + + /** + * Returns the decoded query component of this URI. + * + * <p> The string returned by this method is equal to that returned by the + * {@link #getRawQuery() getRawQuery} method except that all sequences of + * escaped octets are <a href="#decode">decoded</a>. </p> + * + * @return The decoded query component of this URI, + * or {@code null} if the query is undefined + */ + public String getQuery() { + if ((decodedQuery == null) && (query != null)) + decodedQuery = decode(query); + return decodedQuery; + } + + /** + * Returns the raw fragment component of this URI. + * + * <p> The fragment component of a URI, if defined, only contains legal URI + * characters. </p> + * + * @return The raw fragment component of this URI, + * or {@code null} if the fragment is undefined + */ + public String getRawFragment() { + return fragment; + } + + /** + * Returns the decoded fragment component of this URI. + * + * <p> The string returned by this method is equal to that returned by the + * {@link #getRawFragment() getRawFragment} method except that all + * sequences of escaped octets are <a href="#decode">decoded</a>. </p> + * + * @return The decoded fragment component of this URI, + * or {@code null} if the fragment is undefined + */ + public String getFragment() { + if ((decodedFragment == null) && (fragment != null)) + decodedFragment = decode(fragment); + return decodedFragment; + } + + + // -- Equality, comparison, hash code, toString, and serialization -- + + /** + * Tests this URI for equality with another object. + * + * <p> If the given object is not a URI then this method immediately + * returns {@code false}. + * + * <p> For two URIs to be considered equal requires that either both are + * opaque or both are hierarchical. Their schemes must either both be + * undefined or else be equal without regard to case. Their fragments + * must either both be undefined or else be equal. + * + * <p> For two opaque URIs to be considered equal, their scheme-specific + * parts must be equal. + * + * <p> For two hierarchical URIs to be considered equal, their paths must + * be equal and their queries must either both be undefined or else be + * equal. Their authorities must either both be undefined, or both be + * registry-based, or both be server-based. If their authorities are + * defined and are registry-based, then they must be equal. If their + * authorities are defined and are server-based, then their hosts must be + * equal without regard to case, their port numbers must be equal, and + * their user-information components must be equal. + * + * <p> When testing the user-information, path, query, fragment, authority, + * or scheme-specific parts of two URIs for equality, the raw forms rather + * than the encoded forms of these components are compared and the + * hexadecimal digits of escaped octets are compared without regard to + * case. + * + * <p> This method satisfies the general contract of the {@link + * java.lang.Object#equals(Object) Object.equals} method. </p> + * + * @param ob The object to which this object is to be compared + * + * @return {@code true} if, and only if, the given object is a URI that + * is identical to this URI + */ + public boolean equals(Object ob) { + if (ob == this) + return true; + if (!(ob instanceof URI)) + return false; + URI that = (URI)ob; + if (this.isOpaque() != that.isOpaque()) return false; + if (!equalIgnoringCase(this.scheme, that.scheme)) return false; + if (!equal(this.fragment, that.fragment)) return false; + + // Opaque + if (this.isOpaque()) + return equal(this.schemeSpecificPart, that.schemeSpecificPart); + + // Hierarchical + if (!equal(this.path, that.path)) return false; + if (!equal(this.query, that.query)) return false; + + // Authorities + if (this.authority == that.authority) return true; + if (this.host != null) { + // Server-based + if (!equal(this.userInfo, that.userInfo)) return false; + if (!equalIgnoringCase(this.host, that.host)) return false; + if (this.port != that.port) return false; + } else if (this.authority != null) { + // Registry-based + if (!equal(this.authority, that.authority)) return false; + } else if (this.authority != that.authority) { + return false; + } + + return true; + } + + /** + * Returns a hash-code value for this URI. The hash code is based upon all + * of the URI's components, and satisfies the general contract of the + * {@link java.lang.Object#hashCode() Object.hashCode} method. + * + * @return A hash-code value for this URI + */ + public int hashCode() { + if (hash != 0) + return hash; + int h = hashIgnoringCase(0, scheme); + h = hash(h, fragment); + if (isOpaque()) { + h = hash(h, schemeSpecificPart); + } else { + h = hash(h, path); + h = hash(h, query); + if (host != null) { + h = hash(h, userInfo); + h = hashIgnoringCase(h, host); + h += 1949 * port; + } else { + h = hash(h, authority); + } + } + hash = h; + return h; + } + + /** + * Compares this URI to another object, which must be a URI. + * + * <p> When comparing corresponding components of two URIs, if one + * component is undefined but the other is defined then the first is + * considered to be less than the second. Unless otherwise noted, string + * components are ordered according to their natural, case-sensitive + * ordering as defined by the {@link java.lang.String#compareTo(Object) + * String.compareTo} method. String components that are subject to + * encoding are compared by comparing their raw forms rather than their + * encoded forms. + * + * <p> The ordering of URIs is defined as follows: </p> + * + * <ul> + * + * <li><p> Two URIs with different schemes are ordered according the + * ordering of their schemes, without regard to case. </p></li> + * + * <li><p> A hierarchical URI is considered to be less than an opaque URI + * with an identical scheme. </p></li> + * + * <li><p> Two opaque URIs with identical schemes are ordered according + * to the ordering of their scheme-specific parts. </p></li> + * + * <li><p> Two opaque URIs with identical schemes and scheme-specific + * parts are ordered according to the ordering of their + * fragments. </p></li> + * + * <li><p> Two hierarchical URIs with identical schemes are ordered + * according to the ordering of their authority components: </p> + * + * <ul> + * + * <li><p> If both authority components are server-based then the URIs + * are ordered according to their user-information components; if these + * components are identical then the URIs are ordered according to the + * ordering of their hosts, without regard to case; if the hosts are + * identical then the URIs are ordered according to the ordering of + * their ports. </p></li> + * + * <li><p> If one or both authority components are registry-based then + * the URIs are ordered according to the ordering of their authority + * components. </p></li> + * + * </ul></li> + * + * <li><p> Finally, two hierarchical URIs with identical schemes and + * authority components are ordered according to the ordering of their + * paths; if their paths are identical then they are ordered according to + * the ordering of their queries; if the queries are identical then they + * are ordered according to the order of their fragments. </p></li> + * + * </ul> + * + * <p> This method satisfies the general contract of the {@link + * java.lang.Comparable#compareTo(Object) Comparable.compareTo} + * method. </p> + * + * @param that + * The object to which this URI is to be compared + * + * @return A negative integer, zero, or a positive integer as this URI is + * less than, equal to, or greater than the given URI + * + * @throws ClassCastException + * If the given object is not a URI + */ + public int compareTo(URI that) { + int c; + + if ((c = compareIgnoringCase(this.scheme, that.scheme)) != 0) + return c; + + if (this.isOpaque()) { + if (that.isOpaque()) { + // Both opaque + if ((c = compare(this.schemeSpecificPart, + that.schemeSpecificPart)) != 0) + return c; + return compare(this.fragment, that.fragment); + } + return +1; // Opaque > hierarchical + } else if (that.isOpaque()) { + return -1; // Hierarchical < opaque + } + + // Hierarchical + if ((this.host != null) && (that.host != null)) { + // Both server-based + if ((c = compare(this.userInfo, that.userInfo)) != 0) + return c; + if ((c = compareIgnoringCase(this.host, that.host)) != 0) + return c; + if ((c = this.port - that.port) != 0) + return c; + } else { + // If one or both authorities are registry-based then we simply + // compare them in the usual, case-sensitive way. If one is + // registry-based and one is server-based then the strings are + // guaranteed to be unequal, hence the comparison will never return + // zero and the compareTo and equals methods will remain + // consistent. + if ((c = compare(this.authority, that.authority)) != 0) return c; + } + + if ((c = compare(this.path, that.path)) != 0) return c; + if ((c = compare(this.query, that.query)) != 0) return c; + return compare(this.fragment, that.fragment); + } + + /** + * Returns the content of this URI as a string. + * + * <p> If this URI was created by invoking one of the constructors in this + * class then a string equivalent to the original input string, or to the + * string computed from the originally-given components, as appropriate, is + * returned. Otherwise this URI was created by normalization, resolution, + * or relativization, and so a string is constructed from this URI's + * components according to the rules specified in <a + * href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>, + * section 5.2, step 7. </p> + * + * @return The string form of this URI + */ + public String toString() { + defineString(); + return string; + } + + /** + * Returns the content of this URI as a US-ASCII string. + * + * <p> If this URI does not contain any characters in the <i>other</i> + * category then an invocation of this method will return the same value as + * an invocation of the {@link #toString() toString} method. Otherwise + * this method works as if by invoking that method and then <a + * href="#encode">encoding</a> the result. </p> + * + * @return The string form of this URI, encoded as needed + * so that it only contains characters in the US-ASCII + * charset + */ + public String toASCIIString() { + defineString(); + return encode(string); + } + + + // -- Serialization support -- + + /** + * Saves the content of this URI to the given serial stream. + * + * <p> The only serializable field of a URI instance is its {@code string} + * field. That field is given a value, if it does not have one already, + * and then the {@link java.io.ObjectOutputStream#defaultWriteObject()} + * method of the given object-output stream is invoked. </p> + * + * @param os The object-output stream to which this object + * is to be written + */ + private void writeObject(ObjectOutputStream os) + throws IOException + { + defineString(); + os.defaultWriteObject(); // Writes the string field only + } + + /** + * Reconstitutes a URI from the given serial stream. + * + * <p> The {@link java.io.ObjectInputStream#defaultReadObject()} method is + * invoked to read the value of the {@code string} field. The result is + * then parsed in the usual way. + * + * @param is The object-input stream from which this object + * is being read + */ + private void readObject(ObjectInputStream is) + throws ClassNotFoundException, IOException + { + port = -1; // Argh + is.defaultReadObject(); + try { + new Parser(string).parse(false); + } catch (URISyntaxException x) { + IOException y = new InvalidObjectException("Invalid URI"); + y.initCause(x); + throw y; + } + } + + + // -- End of public methods -- + + + // -- Utility methods for string-field comparison and hashing -- + + // These methods return appropriate values for null string arguments, + // thereby simplifying the equals, hashCode, and compareTo methods. + // + // The case-ignoring methods should only be applied to strings whose + // characters are all known to be US-ASCII. Because of this restriction, + // these methods are faster than the similar methods in the String class. + + // US-ASCII only + private static int toLower(char c) { + if ((c >= 'A') && (c <= 'Z')) + return c + ('a' - 'A'); + return c; + } + + // US-ASCII only + private static int toUpper(char c) { + if ((c >= 'a') && (c <= 'z')) + return c - ('a' - 'A'); + return c; + } + + private static boolean equal(String s, String t) { + if (s == t) return true; + if ((s != null) && (t != null)) { + if (s.length() != t.length()) + return false; + if (s.indexOf('%') < 0) + return s.equals(t); + int n = s.length(); + for (int i = 0; i < n;) { + char c = s.charAt(i); + char d = t.charAt(i); + if (c != '%') { + if (c != d) + return false; + i++; + continue; + } + if (d != '%') + return false; + i++; + if (toLower(s.charAt(i)) != toLower(t.charAt(i))) + return false; + i++; + if (toLower(s.charAt(i)) != toLower(t.charAt(i))) + return false; + i++; + } + return true; + } + return false; + } + + // US-ASCII only + private static boolean equalIgnoringCase(String s, String t) { + if (s == t) return true; + if ((s != null) && (t != null)) { + int n = s.length(); + if (t.length() != n) + return false; + for (int i = 0; i < n; i++) { + if (toLower(s.charAt(i)) != toLower(t.charAt(i))) + return false; + } + return true; + } + return false; + } + + private static int hash(int hash, String s) { + if (s == null) return hash; + return s.indexOf('%') < 0 ? hash * 127 + s.hashCode() + : normalizedHash(hash, s); + } + + + private static int normalizedHash(int hash, String s) { + int h = 0; + for (int index = 0; index < s.length(); index++) { + char ch = s.charAt(index); + h = 31 * h + ch; + if (ch == '%') { + /* + * Process the next two encoded characters + */ + for (int i = index + 1; i < index + 3; i++) + h = 31 * h + toUpper(s.charAt(i)); + index += 2; + } + } + return hash * 127 + h; + } + + // US-ASCII only + private static int hashIgnoringCase(int hash, String s) { + if (s == null) return hash; + int h = hash; + int n = s.length(); + for (int i = 0; i < n; i++) + h = 31 * h + toLower(s.charAt(i)); + return h; + } + + private static int compare(String s, String t) { + if (s == t) return 0; + if (s != null) { + if (t != null) + return s.compareTo(t); + else + return +1; + } else { + return -1; + } + } + + // US-ASCII only + private static int compareIgnoringCase(String s, String t) { + if (s == t) return 0; + if (s != null) { + if (t != null) { + int sn = s.length(); + int tn = t.length(); + int n = sn < tn ? sn : tn; + for (int i = 0; i < n; i++) { + int c = toLower(s.charAt(i)) - toLower(t.charAt(i)); + if (c != 0) + return c; + } + return sn - tn; + } + return +1; + } else { + return -1; + } + } + + + // -- String construction -- + + // If a scheme is given then the path, if given, must be absolute + // + private static void checkPath(String s, String scheme, String path) + throws URISyntaxException + { + if (scheme != null) { + if ((path != null) + && ((path.length() > 0) && (path.charAt(0) != '/'))) + throw new URISyntaxException(s, + "Relative path in absolute URI"); + } + } + + private void appendAuthority(StringBuffer sb, + String authority, + String userInfo, + String host, + int port) + { + if (host != null) { + sb.append("//"); + if (userInfo != null) { + sb.append(quote(userInfo, L_USERINFO, H_USERINFO)); + sb.append('@'); + } + boolean needBrackets = ((host.indexOf(':') >= 0) + && !host.startsWith("[") + && !host.endsWith("]")); + if (needBrackets) sb.append('['); + sb.append(host); + if (needBrackets) sb.append(']'); + if (port != -1) { + sb.append(':'); + sb.append(port); + } + } else if (authority != null) { + sb.append("//"); + if (authority.startsWith("[")) { + // authority should (but may not) contain an embedded IPv6 address + int end = authority.indexOf("]"); + String doquote = authority, dontquote = ""; + if (end != -1 && authority.indexOf(":") != -1) { + // the authority contains an IPv6 address + if (end == authority.length()) { + dontquote = authority; + doquote = ""; + } else { + dontquote = authority.substring(0 , end + 1); + doquote = authority.substring(end + 1); + } + } + sb.append(dontquote); + sb.append(quote(doquote, + L_REG_NAME | L_SERVER, + H_REG_NAME | H_SERVER)); + } else { + sb.append(quote(authority, + L_REG_NAME | L_SERVER, + H_REG_NAME | H_SERVER)); + } + } + } + + private void appendSchemeSpecificPart(StringBuffer sb, + String opaquePart, + String authority, + String userInfo, + String host, + int port, + String path, + String query) + { + if (opaquePart != null) { + /* check if SSP begins with an IPv6 address + * because we must not quote a literal IPv6 address + */ + if (opaquePart.startsWith("//[")) { + int end = opaquePart.indexOf("]"); + if (end != -1 && opaquePart.indexOf(":")!=-1) { + String doquote, dontquote; + if (end == opaquePart.length()) { + dontquote = opaquePart; + doquote = ""; + } else { + dontquote = opaquePart.substring(0,end+1); + doquote = opaquePart.substring(end+1); + } + sb.append (dontquote); + sb.append(quote(doquote, L_URIC, H_URIC)); + } + } else { + sb.append(quote(opaquePart, L_URIC, H_URIC)); + } + } else { + appendAuthority(sb, authority, userInfo, host, port); + if (path != null) + sb.append(quote(path, L_PATH, H_PATH)); + if (query != null) { + sb.append('?'); + sb.append(quote(query, L_URIC, H_URIC)); + } + } + } + + private void appendFragment(StringBuffer sb, String fragment) { + if (fragment != null) { + sb.append('#'); + sb.append(quote(fragment, L_URIC, H_URIC)); + } + } + + private String toString(String scheme, + String opaquePart, + String authority, + String userInfo, + String host, + int port, + String path, + String query, + String fragment) + { + StringBuffer sb = new StringBuffer(); + if (scheme != null) { + sb.append(scheme); + sb.append(':'); + } + appendSchemeSpecificPart(sb, opaquePart, + authority, userInfo, host, port, + path, query); + appendFragment(sb, fragment); + return sb.toString(); + } + + private void defineSchemeSpecificPart() { + if (schemeSpecificPart != null) return; + StringBuffer sb = new StringBuffer(); + appendSchemeSpecificPart(sb, null, getAuthority(), getUserInfo(), + host, port, getPath(), getQuery()); + if (sb.length() == 0) return; + schemeSpecificPart = sb.toString(); + } + + private void defineString() { + if (string != null) return; + + StringBuffer sb = new StringBuffer(); + if (scheme != null) { + sb.append(scheme); + sb.append(':'); + } + if (isOpaque()) { + sb.append(schemeSpecificPart); + } else { + if (host != null) { + sb.append("//"); + if (userInfo != null) { + sb.append(userInfo); + sb.append('@'); + } + boolean needBrackets = ((host.indexOf(':') >= 0) + && !host.startsWith("[") + && !host.endsWith("]")); + if (needBrackets) sb.append('['); + sb.append(host); + if (needBrackets) sb.append(']'); + if (port != -1) { + sb.append(':'); + sb.append(port); + } + } else if (authority != null) { + sb.append("//"); + sb.append(authority); + } + if (path != null) + sb.append(path); + if (query != null) { + sb.append('?'); + sb.append(query); + } + } + if (fragment != null) { + sb.append('#'); + sb.append(fragment); + } + string = sb.toString(); + } + + + // -- Normalization, resolution, and relativization -- + + // RFC2396 5.2 (6) + private static String resolvePath(String base, String child, + boolean absolute) + { + int i = base.lastIndexOf('/'); + int cn = child.length(); + String path = ""; + + if (cn == 0) { + // 5.2 (6a) + if (i >= 0) + path = base.substring(0, i + 1); + } else { + StringBuffer sb = new StringBuffer(base.length() + cn); + // 5.2 (6a) + if (i >= 0) + sb.append(base.substring(0, i + 1)); + // 5.2 (6b) + sb.append(child); + path = sb.toString(); + } + + // 5.2 (6c-f) + // Android-changed: App compat. Remove leading dots when resolving path. http://b/25897693 + // String np = normalize(path); + String np = normalize(path, true); + + // 5.2 (6g): If the result is absolute but the path begins with "../", + // then we simply leave the path as-is + + return np; + } + + // RFC2396 5.2 + private static URI resolve(URI base, URI child) { + // check if child if opaque first so that NPE is thrown + // if child is null. + if (child.isOpaque() || base.isOpaque()) + return child; + + // 5.2 (2): Reference to current document (lone fragment) + if ((child.scheme == null) && (child.authority == null) + && child.path.equals("") && (child.fragment != null) + && (child.query == null)) { + if ((base.fragment != null) + && child.fragment.equals(base.fragment)) { + return base; + } + URI ru = new URI(); + ru.scheme = base.scheme; + ru.authority = base.authority; + ru.userInfo = base.userInfo; + ru.host = base.host; + ru.port = base.port; + ru.path = base.path; + ru.fragment = child.fragment; + ru.query = base.query; + return ru; + } + + // 5.2 (3): Child is absolute + if (child.scheme != null) + return child; + + URI ru = new URI(); // Resolved URI + ru.scheme = base.scheme; + ru.query = child.query; + ru.fragment = child.fragment; + + // 5.2 (4): Authority + if (child.authority == null) { + ru.authority = base.authority; + ru.host = base.host; + ru.userInfo = base.userInfo; + ru.port = base.port; + + // BEGIN Android-changed: App Compat. Handle null and empty path using RFC 3986 logic + // http://b/25897693 + if (child.path == null || child.path.isEmpty()) { + // This is an additional path from RFC 3986 RI, which fixes following RFC 2396 + // "normal" examples: + // Base: http://a/b/c/d;p?q + // "?y" = "http://a/b/c/d;p?y" + // "" = "http://a/b/c/d;p?q" + // http://b/25897693 + ru.path = base.path; + ru.query = child.query != null ? child.query : base.query; + // END Android-changed: App Compat. Handle null and empty path using RFC 3986 logic + } else if ((child.path.length() > 0) && (child.path.charAt(0) == '/')) { + // 5.2 (5): Child path is absolute + // + // Android-changed: App Compat. Remove leading dots in path. + // There is an additional step from RFC 3986 RI, requiring to remove dots for + // absolute path as well. + // http://b/25897693 + // ru.path = child.path; + ru.path = normalize(child.path, true); + } else { + // 5.2 (6): Resolve relative path + ru.path = resolvePath(base.path, child.path, base.isAbsolute()); + } + } else { + ru.authority = child.authority; + ru.host = child.host; + ru.userInfo = child.userInfo; + ru.host = child.host; + ru.port = child.port; + ru.path = child.path; + } + + // 5.2 (7): Recombine (nothing to do here) + return ru; + } + + // If the given URI's path is normal then return the URI; + // o.w., return a new URI containing the normalized path. + // + private static URI normalize(URI u) { + if (u.isOpaque() || (u.path == null) || (u.path.length() == 0)) + return u; + + String np = normalize(u.path); + if (np == u.path) + return u; + + URI v = new URI(); + v.scheme = u.scheme; + v.fragment = u.fragment; + v.authority = u.authority; + v.userInfo = u.userInfo; + v.host = u.host; + v.port = u.port; + v.path = np; + v.query = u.query; + return v; + } + + // If both URIs are hierarchical, their scheme and authority components are + // identical, and the base path is a prefix of the child's path, then + // return a relative URI that, when resolved against the base, yields the + // child; otherwise, return the child. + // + private static URI relativize(URI base, URI child) { + // check if child if opaque first so that NPE is thrown + // if child is null. + if (child.isOpaque() || base.isOpaque()) + return child; + if (!equalIgnoringCase(base.scheme, child.scheme) + || !equal(base.authority, child.authority)) + return child; + + String bp = normalize(base.path); + String cp = normalize(child.path); + if (!bp.equals(cp)) { + // Android-changed: App Compat. Interpret ambiguous base path as a file, not a directory + // Upstream would append '/' to bp if not present, interpreting it as a directory; thus, + // /a/b/c relative to /a/b would become /c, whereas Android would relativize to /b/c. + // The spec is pretty vague about this but the Android behavior is kept because several + // tests enforce it. + // if (!bp.endsWith("/")) + // bp = bp + "/"; + if (bp.indexOf('/') != -1) { + bp = bp.substring(0, bp.lastIndexOf('/') + 1); + } + + if (!cp.startsWith(bp)) + return child; + } + + URI v = new URI(); + v.path = cp.substring(bp.length()); + v.query = child.query; + v.fragment = child.fragment; + return v; + } + + + + // -- Path normalization -- + + // The following algorithm for path normalization avoids the creation of a + // string object for each segment, as well as the use of a string buffer to + // compute the final result, by using a single char array and editing it in + // place. The array is first split into segments, replacing each slash + // with '\0' and creating a segment-index array, each element of which is + // the index of the first char in the corresponding segment. We then walk + // through both arrays, removing ".", "..", and other segments as necessary + // by setting their entries in the index array to -1. Finally, the two + // arrays are used to rejoin the segments and compute the final result. + // + // This code is based upon src/solaris/native/java/io/canonicalize_md.c + + + // Check the given path to see if it might need normalization. A path + // might need normalization if it contains duplicate slashes, a "." + // segment, or a ".." segment. Return -1 if no further normalization is + // possible, otherwise return the number of segments found. + // + // This method takes a string argument rather than a char array so that + // this test can be performed without invoking path.toCharArray(). + // + static private int needsNormalization(String path) { + boolean normal = true; + int ns = 0; // Number of segments + int end = path.length() - 1; // Index of last char in path + int p = 0; // Index of next char in path + + // Skip initial slashes + while (p <= end) { + if (path.charAt(p) != '/') break; + p++; + } + if (p > 1) normal = false; + + // Scan segments + while (p <= end) { + + // Looking at "." or ".." ? + if ((path.charAt(p) == '.') + && ((p == end) + || ((path.charAt(p + 1) == '/') + || ((path.charAt(p + 1) == '.') + && ((p + 1 == end) + || (path.charAt(p + 2) == '/')))))) { + normal = false; + } + ns++; + + // Find beginning of next segment + while (p <= end) { + if (path.charAt(p++) != '/') + continue; + + // Skip redundant slashes + while (p <= end) { + if (path.charAt(p) != '/') break; + normal = false; + p++; + } + + break; + } + } + + return normal ? -1 : ns; + } + + + // Split the given path into segments, replacing slashes with nulls and + // filling in the given segment-index array. + // + // Preconditions: + // segs.length == Number of segments in path + // + // Postconditions: + // All slashes in path replaced by '\0' + // segs[i] == Index of first char in segment i (0 <= i < segs.length) + // + static private void split(char[] path, int[] segs) { + int end = path.length - 1; // Index of last char in path + int p = 0; // Index of next char in path + int i = 0; // Index of current segment + + // Skip initial slashes + while (p <= end) { + if (path[p] != '/') break; + path[p] = '\0'; + p++; + } + + while (p <= end) { + + // Note start of segment + segs[i++] = p++; + + // Find beginning of next segment + while (p <= end) { + if (path[p++] != '/') + continue; + path[p - 1] = '\0'; + + // Skip redundant slashes + while (p <= end) { + if (path[p] != '/') break; + path[p++] = '\0'; + } + break; + } + } + + if (i != segs.length) + throw new InternalError(); // ASSERT + } + + + // Join the segments in the given path according to the given segment-index + // array, ignoring those segments whose index entries have been set to -1, + // and inserting slashes as needed. Return the length of the resulting + // path. + // + // Preconditions: + // segs[i] == -1 implies segment i is to be ignored + // path computed by split, as above, with '\0' having replaced '/' + // + // Postconditions: + // path[0] .. path[return value] == Resulting path + // + static private int join(char[] path, int[] segs) { + int ns = segs.length; // Number of segments + int end = path.length - 1; // Index of last char in path + int p = 0; // Index of next path char to write + + if (path[p] == '\0') { + // Restore initial slash for absolute paths + path[p++] = '/'; + } + + for (int i = 0; i < ns; i++) { + int q = segs[i]; // Current segment + if (q == -1) + // Ignore this segment + continue; + + if (p == q) { + // We're already at this segment, so just skip to its end + while ((p <= end) && (path[p] != '\0')) + p++; + if (p <= end) { + // Preserve trailing slash + path[p++] = '/'; + } + } else if (p < q) { + // Copy q down to p + while ((q <= end) && (path[q] != '\0')) + path[p++] = path[q++]; + if (q <= end) { + // Preserve trailing slash + path[p++] = '/'; + } + } else + throw new InternalError(); // ASSERT false + } + + return p; + } + + + // Remove "." segments from the given path, and remove segment pairs + // consisting of a non-".." segment followed by a ".." segment. + // + // Android-changed: App compat. Remove leading dots when resolving path. http://b/25897693 + // private static void removeDots(char[] path, int[] segs) { + private static void removeDots(char[] path, int[] segs, boolean removeLeading) { + int ns = segs.length; + int end = path.length - 1; + + for (int i = 0; i < ns; i++) { + int dots = 0; // Number of dots found (0, 1, or 2) + + // Find next occurrence of "." or ".." + do { + int p = segs[i]; + if (path[p] == '.') { + if (p == end) { + dots = 1; + break; + } else if (path[p + 1] == '\0') { + dots = 1; + break; + } else if ((path[p + 1] == '.') + && ((p + 1 == end) + || (path[p + 2] == '\0'))) { + dots = 2; + break; + } + } + i++; + } while (i < ns); + if ((i > ns) || (dots == 0)) + break; + + if (dots == 1) { + // Remove this occurrence of "." + segs[i] = -1; + } else { + // If there is a preceding non-".." segment, remove both that + // segment and this occurrence of ".." + int j; + for (j = i - 1; j >= 0; j--) { + if (segs[j] != -1) break; + } + if (j >= 0) { + int q = segs[j]; + if (!((path[q] == '.') + && (path[q + 1] == '.') + && (path[q + 2] == '\0'))) { + segs[i] = -1; + segs[j] = -1; + } + // Android-added: App compat. Remove leading dots when resolving path. + // This is a leading ".." segment. Per RFC 3986 RI, this should be removed as + // well. This fixes RFC 2396 "abnormal" examples. + // http://b/25897693 + } else if (removeLeading) { + segs[i] = -1; + } + } + } + } + + + // DEVIATION: If the normalized path is relative, and if the first + // segment could be parsed as a scheme name, then prepend a "." segment + // + private static void maybeAddLeadingDot(char[] path, int[] segs) { + + if (path[0] == '\0') + // The path is absolute + return; + + int ns = segs.length; + int f = 0; // Index of first segment + while (f < ns) { + if (segs[f] >= 0) + break; + f++; + } + if ((f >= ns) || (f == 0)) + // The path is empty, or else the original first segment survived, + // in which case we already know that no leading "." is needed + return; + + int p = segs[f]; + while ((p < path.length) && (path[p] != ':') && (path[p] != '\0')) p++; + if (p >= path.length || path[p] == '\0') + // No colon in first segment, so no "." needed + return; + + // At this point we know that the first segment is unused, + // hence we can insert a "." segment at that position + path[0] = '.'; + path[1] = '\0'; + segs[0] = 0; + } + + + // Normalize the given path string. A normal path string has no empty + // segments (i.e., occurrences of "//"), no segments equal to ".", and no + // segments equal to ".." that are preceded by a segment not equal to "..". + // In contrast to Unix-style pathname normalization, for URI paths we + // always retain trailing slashes. + // + private static String normalize(String ps) { + // BEGIN Android-changed: App compat. Remove leading dots when resolving path. + // Controlled by the "boolean removeLeading" argument added to normalize(). + return normalize(ps, false); + } + + private static String normalize(String ps, boolean removeLeading) { + // END Android-changed: App compat. Remove leading dots when resolving path. + // Does this path need normalization? + int ns = needsNormalization(ps); // Number of segments + if (ns < 0) + // Nope -- just return it + return ps; + + char[] path = ps.toCharArray(); // Path in char-array form + + // Split path into segments + int[] segs = new int[ns]; // Segment-index array + split(path, segs); + + // Remove dots + // Android-changed: App compat. Remove leading dots when resolving path. + // removeDots(path, segs); + removeDots(path, segs, removeLeading); + + // Prevent scheme-name confusion + maybeAddLeadingDot(path, segs); + + // Join the remaining segments and return the result + String s = new String(path, 0, join(path, segs)); + if (s.equals(ps)) { + // string was already normalized + return ps; + } + return s; + } + + + + // -- Character classes for parsing -- + + // RFC2396 precisely specifies which characters in the US-ASCII charset are + // permissible in the various components of a URI reference. We here + // define a set of mask pairs to aid in enforcing these restrictions. Each + // mask pair consists of two longs, a low mask and a high mask. Taken + // together they represent a 128-bit mask, where bit i is set iff the + // character with value i is permitted. + // + // This approach is more efficient than sequentially searching arrays of + // permitted characters. It could be made still more efficient by + // precompiling the mask information so that a character's presence in a + // given mask could be determined by a single table lookup. + + // Compute the low-order mask for the characters in the given string + private static long lowMask(String chars) { + int n = chars.length(); + long m = 0; + for (int i = 0; i < n; i++) { + char c = chars.charAt(i); + if (c < 64) + m |= (1L << c); + } + return m; + } + + // Compute the high-order mask for the characters in the given string + private static long highMask(String chars) { + int n = chars.length(); + long m = 0; + for (int i = 0; i < n; i++) { + char c = chars.charAt(i); + if ((c >= 64) && (c < 128)) + m |= (1L << (c - 64)); + } + return m; + } + + // Compute a low-order mask for the characters + // between first and last, inclusive + private static long lowMask(char first, char last) { + long m = 0; + int f = Math.max(Math.min(first, 63), 0); + int l = Math.max(Math.min(last, 63), 0); + for (int i = f; i <= l; i++) + m |= 1L << i; + return m; + } + + // Compute a high-order mask for the characters + // between first and last, inclusive + private static long highMask(char first, char last) { + long m = 0; + int f = Math.max(Math.min(first, 127), 64) - 64; + int l = Math.max(Math.min(last, 127), 64) - 64; + for (int i = f; i <= l; i++) + m |= 1L << i; + return m; + } + + // Tell whether the given character is permitted by the given mask pair + private static boolean match(char c, long lowMask, long highMask) { + if (c == 0) // 0 doesn't have a slot in the mask. So, it never matches. + return false; + if (c < 64) + return ((1L << c) & lowMask) != 0; + if (c < 128) + return ((1L << (c - 64)) & highMask) != 0; + return false; + } + + // Character-class masks, in reverse order from RFC2396 because + // initializers for static fields cannot make forward references. + + // digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | + // "8" | "9" + private static final long L_DIGIT = lowMask('0', '9'); + private static final long H_DIGIT = 0L; + + // upalpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | + // "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | + // "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" + private static final long L_UPALPHA = 0L; + private static final long H_UPALPHA = highMask('A', 'Z'); + + // lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | + // "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | + // "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z" + private static final long L_LOWALPHA = 0L; + private static final long H_LOWALPHA = highMask('a', 'z'); + + // alpha = lowalpha | upalpha + private static final long L_ALPHA = L_LOWALPHA | L_UPALPHA; + private static final long H_ALPHA = H_LOWALPHA | H_UPALPHA; + + // alphanum = alpha | digit + private static final long L_ALPHANUM = L_DIGIT | L_ALPHA; + private static final long H_ALPHANUM = H_DIGIT | H_ALPHA; + + // hex = digit | "A" | "B" | "C" | "D" | "E" | "F" | + // "a" | "b" | "c" | "d" | "e" | "f" + private static final long L_HEX = L_DIGIT; + private static final long H_HEX = highMask('A', 'F') | highMask('a', 'f'); + + // mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | + // "(" | ")" + private static final long L_MARK = lowMask("-_.!~*'()"); + private static final long H_MARK = highMask("-_.!~*'()"); + + // unreserved = alphanum | mark + private static final long L_UNRESERVED = L_ALPHANUM | L_MARK; + private static final long H_UNRESERVED = H_ALPHANUM | H_MARK; + + // reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | + // "$" | "," | "[" | "]" + // Added per RFC2732: "[", "]" + private static final long L_RESERVED = lowMask(";/?:@&=+$,[]"); + private static final long H_RESERVED = highMask(";/?:@&=+$,[]"); + + // The zero'th bit is used to indicate that escape pairs and non-US-ASCII + // characters are allowed; this is handled by the scanEscape method below. + private static final long L_ESCAPED = 1L; + private static final long H_ESCAPED = 0L; + + // uric = reserved | unreserved | escaped + private static final long L_URIC = L_RESERVED | L_UNRESERVED | L_ESCAPED; + private static final long H_URIC = H_RESERVED | H_UNRESERVED | H_ESCAPED; + + // pchar = unreserved | escaped | + // ":" | "@" | "&" | "=" | "+" | "$" | "," + private static final long L_PCHAR + = L_UNRESERVED | L_ESCAPED | lowMask(":@&=+$,"); + private static final long H_PCHAR + = H_UNRESERVED | H_ESCAPED | highMask(":@&=+$,"); + + // All valid path characters + private static final long L_PATH = L_PCHAR | lowMask(";/"); + private static final long H_PATH = H_PCHAR | highMask(";/"); + + // Dash, for use in domainlabel and toplabel + private static final long L_DASH = lowMask("-"); + private static final long H_DASH = highMask("-"); + + // BEGIN Android-added: Allow underscore in hostname. + // UNDERSCORE, for use in domainlabel and toplabel + private static final long L_UNDERSCORE = lowMask("_"); + private static final long H_UNDERSCORE = highMask("_"); + // END Android-added: Allow underscore in hostname. + + // Dot, for use in hostnames + private static final long L_DOT = lowMask("."); + private static final long H_DOT = highMask("."); + + // userinfo = *( unreserved | escaped | + // ";" | ":" | "&" | "=" | "+" | "$" | "," ) + private static final long L_USERINFO + = L_UNRESERVED | L_ESCAPED | lowMask(";:&=+$,"); + private static final long H_USERINFO + = H_UNRESERVED | H_ESCAPED | highMask(";:&=+$,"); + + // reg_name = 1*( unreserved | escaped | "$" | "," | + // ";" | ":" | "@" | "&" | "=" | "+" ) + private static final long L_REG_NAME + = L_UNRESERVED | L_ESCAPED | lowMask("$,;:@&=+"); + private static final long H_REG_NAME + = H_UNRESERVED | H_ESCAPED | highMask("$,;:@&=+"); + + // All valid characters for server-based authorities + private static final long L_SERVER + = L_USERINFO | L_ALPHANUM | L_DASH | lowMask(".:@[]"); + private static final long H_SERVER + = H_USERINFO | H_ALPHANUM | H_DASH | highMask(".:@[]"); + + // Special case of server authority that represents an IPv6 address + // In this case, a % does not signify an escape sequence + private static final long L_SERVER_PERCENT + = L_SERVER | lowMask("%"); + private static final long H_SERVER_PERCENT + = H_SERVER | highMask("%"); + private static final long L_LEFT_BRACKET = lowMask("["); + private static final long H_LEFT_BRACKET = highMask("["); + + // scheme = alpha *( alpha | digit | "+" | "-" | "." ) + private static final long L_SCHEME = L_ALPHA | L_DIGIT | lowMask("+-."); + private static final long H_SCHEME = H_ALPHA | H_DIGIT | highMask("+-."); + + // uric_no_slash = unreserved | escaped | ";" | "?" | ":" | "@" | + // "&" | "=" | "+" | "$" | "," + private static final long L_URIC_NO_SLASH + = L_UNRESERVED | L_ESCAPED | lowMask(";?:@&=+$,"); + private static final long H_URIC_NO_SLASH + = H_UNRESERVED | H_ESCAPED | highMask(";?:@&=+$,"); + + + // -- Escaping and encoding -- + + private final static char[] hexDigits = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + }; + + private static void appendEscape(StringBuffer sb, byte b) { + sb.append('%'); + sb.append(hexDigits[(b >> 4) & 0x0f]); + sb.append(hexDigits[(b >> 0) & 0x0f]); + } + + private static void appendEncoded(StringBuffer sb, char c) { + ByteBuffer bb = null; + try { + bb = ThreadLocalCoders.encoderFor("UTF-8") + .encode(CharBuffer.wrap("" + c)); + } catch (CharacterCodingException x) { + assert false; + } + while (bb.hasRemaining()) { + int b = bb.get() & 0xff; + if (b >= 0x80) + appendEscape(sb, (byte)b); + else + sb.append((char)b); + } + } + + // Quote any characters in s that are not permitted + // by the given mask pair + // + private static String quote(String s, long lowMask, long highMask) { + int n = s.length(); + StringBuffer sb = null; + boolean allowNonASCII = ((lowMask & L_ESCAPED) != 0); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c < '\u0080') { + if (!match(c, lowMask, highMask)) { + if (sb == null) { + sb = new StringBuffer(); + sb.append(s.substring(0, i)); + } + appendEscape(sb, (byte)c); + } else { + if (sb != null) + sb.append(c); + } + } else if (allowNonASCII + && (Character.isSpaceChar(c) + || Character.isISOControl(c))) { + if (sb == null) { + sb = new StringBuffer(); + sb.append(s.substring(0, i)); + } + appendEncoded(sb, c); + } else { + if (sb != null) + sb.append(c); + } + } + return (sb == null) ? s : sb.toString(); + } + + // Encodes all characters >= \u0080 into escaped, normalized UTF-8 octets, + // assuming that s is otherwise legal + // + private static String encode(String s) { + int n = s.length(); + if (n == 0) + return s; + + // First check whether we actually need to encode + for (int i = 0;;) { + if (s.charAt(i) >= '\u0080') + break; + if (++i >= n) + return s; + } + + String ns = Normalizer.normalize(s, Normalizer.Form.NFC); + ByteBuffer bb = null; + try { + bb = ThreadLocalCoders.encoderFor("UTF-8") + .encode(CharBuffer.wrap(ns)); + } catch (CharacterCodingException x) { + assert false; + } + + StringBuffer sb = new StringBuffer(); + while (bb.hasRemaining()) { + int b = bb.get() & 0xff; + if (b >= 0x80) + appendEscape(sb, (byte)b); + else + sb.append((char)b); + } + return sb.toString(); + } + + private static int decode(char c) { + if ((c >= '0') && (c <= '9')) + return c - '0'; + if ((c >= 'a') && (c <= 'f')) + return c - 'a' + 10; + if ((c >= 'A') && (c <= 'F')) + return c - 'A' + 10; + assert false; + return -1; + } + + private static byte decode(char c1, char c2) { + return (byte)( ((decode(c1) & 0xf) << 4) + | ((decode(c2) & 0xf) << 0)); + } + + // Evaluates all escapes in s, applying UTF-8 decoding if needed. Assumes + // that escapes are well-formed syntactically, i.e., of the form %XX. If a + // sequence of escaped octets is not valid UTF-8 then the erroneous octets + // are replaced with '\uFFFD'. + // Exception: any "%" found between "[]" is left alone. It is an IPv6 literal + // with a scope_id + // + private static String decode(String s) { + if (s == null) + return s; + int n = s.length(); + if (n == 0) + return s; + if (s.indexOf('%') < 0) + return s; + + StringBuffer sb = new StringBuffer(n); + ByteBuffer bb = ByteBuffer.allocate(n); + CharBuffer cb = CharBuffer.allocate(n); + CharsetDecoder dec = ThreadLocalCoders.decoderFor("UTF-8") + .onMalformedInput(CodingErrorAction.REPLACE) + .onUnmappableCharacter(CodingErrorAction.REPLACE); + + // This is not horribly efficient, but it will do for now + char c = s.charAt(0); + boolean betweenBrackets = false; + + for (int i = 0; i < n;) { + assert c == s.charAt(i); // Loop invariant + if (c == '[') { + betweenBrackets = true; + } else if (betweenBrackets && c == ']') { + betweenBrackets = false; + } + if (c != '%' || betweenBrackets) { + sb.append(c); + if (++i >= n) + break; + c = s.charAt(i); + continue; + } + bb.clear(); + int ui = i; + for (;;) { + assert (n - i >= 2); + bb.put(decode(s.charAt(++i), s.charAt(++i))); + if (++i >= n) + break; + c = s.charAt(i); + if (c != '%') + break; + } + bb.flip(); + cb.clear(); + dec.reset(); + CoderResult cr = dec.decode(bb, cb, true); + assert cr.isUnderflow(); + cr = dec.flush(cb); + assert cr.isUnderflow(); + sb.append(cb.flip().toString()); + } + + return sb.toString(); + } + + + // -- Parsing -- + + // For convenience we wrap the input URI string in a new instance of the + // following internal class. This saves always having to pass the input + // string as an argument to each internal scan/parse method. + + private class Parser { + + private String input; // URI input string + private boolean requireServerAuthority = false; + + Parser(String s) { + input = s; + string = s; + } + + // -- Methods for throwing URISyntaxException in various ways -- + + private void fail(String reason) throws URISyntaxException { + throw new URISyntaxException(input, reason); + } + + private void fail(String reason, int p) throws URISyntaxException { + throw new URISyntaxException(input, reason, p); + } + + private void failExpecting(String expected, int p) + throws URISyntaxException + { + fail("Expected " + expected, p); + } + + private void failExpecting(String expected, String prior, int p) + throws URISyntaxException + { + fail("Expected " + expected + " following " + prior, p); + } + + + // -- Simple access to the input string -- + + // Return a substring of the input string + // + private String substring(int start, int end) { + return input.substring(start, end); + } + + // Return the char at position p, + // assuming that p < input.length() + // + private char charAt(int p) { + return input.charAt(p); + } + + // Tells whether start < end and, if so, whether charAt(start) == c + // + private boolean at(int start, int end, char c) { + return (start < end) && (charAt(start) == c); + } + + // Tells whether start + s.length() < end and, if so, + // whether the chars at the start position match s exactly + // + private boolean at(int start, int end, String s) { + int p = start; + int sn = s.length(); + if (sn > end - p) + return false; + int i = 0; + while (i < sn) { + if (charAt(p++) != s.charAt(i)) { + break; + } + i++; + } + return (i == sn); + } + + + // -- Scanning -- + + // The various scan and parse methods that follow use a uniform + // convention of taking the current start position and end index as + // their first two arguments. The start is inclusive while the end is + // exclusive, just as in the String class, i.e., a start/end pair + // denotes the left-open interval [start, end) of the input string. + // + // These methods never proceed past the end position. They may return + // -1 to indicate outright failure, but more often they simply return + // the position of the first char after the last char scanned. Thus + // a typical idiom is + // + // int p = start; + // int q = scan(p, end, ...); + // if (q > p) + // // We scanned something + // ...; + // else if (q == p) + // // We scanned nothing + // ...; + // else if (q == -1) + // // Something went wrong + // ...; + + + // Scan a specific char: If the char at the given start position is + // equal to c, return the index of the next char; otherwise, return the + // start position. + // + private int scan(int start, int end, char c) { + if ((start < end) && (charAt(start) == c)) + return start + 1; + return start; + } + + // Scan forward from the given start position. Stop at the first char + // in the err string (in which case -1 is returned), or the first char + // in the stop string (in which case the index of the preceding char is + // returned), or the end of the input string (in which case the length + // of the input string is returned). May return the start position if + // nothing matches. + // + private int scan(int start, int end, String err, String stop) { + int p = start; + while (p < end) { + char c = charAt(p); + if (err.indexOf(c) >= 0) + return -1; + if (stop.indexOf(c) >= 0) + break; + p++; + } + return p; + } + + // Scan a potential escape sequence, starting at the given position, + // with the given first char (i.e., charAt(start) == c). + // + // This method assumes that if escapes are allowed then visible + // non-US-ASCII chars are also allowed. + // + private int scanEscape(int start, int n, char first) + throws URISyntaxException + { + int p = start; + char c = first; + if (c == '%') { + // Process escape pair + if ((p + 3 <= n) + && match(charAt(p + 1), L_HEX, H_HEX) + && match(charAt(p + 2), L_HEX, H_HEX)) { + return p + 3; + } + fail("Malformed escape pair", p); + } else if ((c > 128) + && !Character.isSpaceChar(c) + && !Character.isISOControl(c)) { + // Allow unescaped but visible non-US-ASCII chars + return p + 1; + } + return p; + } + + // Scan chars that match the given mask pair + // + private int scan(int start, int n, long lowMask, long highMask) + throws URISyntaxException + { + int p = start; + while (p < n) { + char c = charAt(p); + if (match(c, lowMask, highMask)) { + p++; + continue; + } + if ((lowMask & L_ESCAPED) != 0) { + int q = scanEscape(p, n, c); + if (q > p) { + p = q; + continue; + } + } + break; + } + return p; + } + + // Check that each of the chars in [start, end) matches the given mask + // + private void checkChars(int start, int end, + long lowMask, long highMask, + String what) + throws URISyntaxException + { + int p = scan(start, end, lowMask, highMask); + if (p < end) + fail("Illegal character in " + what, p); + } + + // Check that the char at position p matches the given mask + // + private void checkChar(int p, + long lowMask, long highMask, + String what) + throws URISyntaxException + { + checkChars(p, p + 1, lowMask, highMask, what); + } + + + // -- Parsing -- + + // [<scheme>:]<scheme-specific-part>[#<fragment>] + // + void parse(boolean rsa) throws URISyntaxException { + requireServerAuthority = rsa; + int ssp; // Start of scheme-specific part + int n = input.length(); + int p = scan(0, n, "/?#", ":"); + if ((p >= 0) && at(p, n, ':')) { + if (p == 0) + failExpecting("scheme name", 0); + checkChar(0, L_ALPHA, H_ALPHA, "scheme name"); + checkChars(1, p, L_SCHEME, H_SCHEME, "scheme name"); + scheme = substring(0, p); + p++; // Skip ':' + ssp = p; + if (at(p, n, '/')) { + p = parseHierarchical(p, n); + } else { + int q = scan(p, n, "", "#"); + if (q <= p) + failExpecting("scheme-specific part", p); + checkChars(p, q, L_URIC, H_URIC, "opaque part"); + p = q; + } + } else { + ssp = 0; + p = parseHierarchical(0, n); + } + schemeSpecificPart = substring(ssp, p); + if (at(p, n, '#')) { + checkChars(p + 1, n, L_URIC, H_URIC, "fragment"); + fragment = substring(p + 1, n); + p = n; + } + if (p < n) + fail("end of URI", p); + } + + // [//authority]<path>[?<query>] + // + // DEVIATION from RFC2396: We allow an empty authority component as + // long as it's followed by a non-empty path, query component, or + // fragment component. This is so that URIs such as "file:///foo/bar" + // will parse. This seems to be the intent of RFC2396, though the + // grammar does not permit it. If the authority is empty then the + // userInfo, host, and port components are undefined. + // + // DEVIATION from RFC2396: We allow empty relative paths. This seems + // to be the intent of RFC2396, but the grammar does not permit it. + // The primary consequence of this deviation is that "#f" parses as a + // relative URI with an empty path. + // + private int parseHierarchical(int start, int n) + throws URISyntaxException + { + int p = start; + if (at(p, n, '/') && at(p + 1, n, '/')) { + p += 2; + int q = scan(p, n, "", "/?#"); + if (q > p) { + p = parseAuthority(p, q); + } else if (q < n) { + // DEVIATION: Allow empty authority prior to non-empty + // path, query component or fragment identifier + } else + failExpecting("authority", p); + } + int q = scan(p, n, "", "?#"); // DEVIATION: May be empty + checkChars(p, q, L_PATH, H_PATH, "path"); + path = substring(p, q); + p = q; + if (at(p, n, '?')) { + p++; + q = scan(p, n, "", "#"); + checkChars(p, q, L_URIC, H_URIC, "query"); + query = substring(p, q); + p = q; + } + return p; + } + + // authority = server | reg_name + // + // Ambiguity: An authority that is a registry name rather than a server + // might have a prefix that parses as a server. We use the fact that + // the authority component is always followed by '/' or the end of the + // input string to resolve this: If the complete authority did not + // parse as a server then we try to parse it as a registry name. + // + private int parseAuthority(int start, int n) + throws URISyntaxException + { + int p = start; + int q = p; + URISyntaxException ex = null; + + boolean serverChars; + boolean regChars; + + if (scan(p, n, "", "]") > p) { + // contains a literal IPv6 address, therefore % is allowed + serverChars = (scan(p, n, L_SERVER_PERCENT, H_SERVER_PERCENT) == n); + } else { + serverChars = (scan(p, n, L_SERVER, H_SERVER) == n); + } + regChars = (scan(p, n, L_REG_NAME, H_REG_NAME) == n); + + if (regChars && !serverChars) { + // Must be a registry-based authority + authority = substring(p, n); + return n; + } + + if (serverChars) { + // Might be (probably is) a server-based authority, so attempt + // to parse it as such. If the attempt fails, try to treat it + // as a registry-based authority. + try { + q = parseServer(p, n); + if (q < n) + failExpecting("end of authority", q); + authority = substring(p, n); + } catch (URISyntaxException x) { + // Undo results of failed parse + userInfo = null; + host = null; + port = -1; + if (requireServerAuthority) { + // If we're insisting upon a server-based authority, + // then just re-throw the exception + throw x; + } else { + // Save the exception in case it doesn't parse as a + // registry either + ex = x; + q = p; + } + } + } + + if (q < n) { + if (regChars) { + // Registry-based authority + authority = substring(p, n); + } else if (ex != null) { + // Re-throw exception; it was probably due to + // a malformed IPv6 address + throw ex; + } else { + fail("Illegal character in authority", q); + } + } + + return n; + } + + + // [<userinfo>@]<host>[:<port>] + // + private int parseServer(int start, int n) + throws URISyntaxException + { + int p = start; + int q; + + // userinfo + q = scan(p, n, "/?#", "@"); + if ((q >= p) && at(q, n, '@')) { + checkChars(p, q, L_USERINFO, H_USERINFO, "user info"); + userInfo = substring(p, q); + p = q + 1; // Skip '@' + } + + // hostname, IPv4 address, or IPv6 address + if (at(p, n, '[')) { + // DEVIATION from RFC2396: Support IPv6 addresses, per RFC2732 + p++; + q = scan(p, n, "/?#", "]"); + if ((q > p) && at(q, n, ']')) { + // look for a "%" scope id + int r = scan (p, q, "", "%"); + if (r > p) { + parseIPv6Reference(p, r); + if (r+1 == q) { + fail ("scope id expected"); + } + checkChars (r+1, q, L_ALPHANUM, H_ALPHANUM, + "scope id"); + } else { + parseIPv6Reference(p, q); + } + host = substring(p-1, q+1); + p = q + 1; + } else { + failExpecting("closing bracket for IPv6 address", q); + } + } else { + q = parseIPv4Address(p, n); + if (q <= p) + q = parseHostname(p, n); + p = q; + } + + // port + if (at(p, n, ':')) { + p++; + q = scan(p, n, "", "/"); + if (q > p) { + checkChars(p, q, L_DIGIT, H_DIGIT, "port number"); + try { + port = Integer.parseInt(substring(p, q)); + } catch (NumberFormatException x) { + fail("Malformed port number", p); + } + p = q; + } + } + if (p < n) + failExpecting("port number", p); + + return p; + } + + // Scan a string of decimal digits whose value fits in a byte + // + private int scanByte(int start, int n) + throws URISyntaxException + { + int p = start; + int q = scan(p, n, L_DIGIT, H_DIGIT); + if (q <= p) return q; + if (Integer.parseInt(substring(p, q)) > 255) return p; + return q; + } + + // Scan an IPv4 address. + // + // If the strict argument is true then we require that the given + // interval contain nothing besides an IPv4 address; if it is false + // then we only require that it start with an IPv4 address. + // + // If the interval does not contain or start with (depending upon the + // strict argument) a legal IPv4 address characters then we return -1 + // immediately; otherwise we insist that these characters parse as a + // legal IPv4 address and throw an exception on failure. + // + // We assume that any string of decimal digits and dots must be an IPv4 + // address. It won't parse as a hostname anyway, so making that + // assumption here allows more meaningful exceptions to be thrown. + // + private int scanIPv4Address(int start, int n, boolean strict) + throws URISyntaxException + { + int p = start; + int q; + int m = scan(p, n, L_DIGIT | L_DOT, H_DIGIT | H_DOT); + if ((m <= p) || (strict && (m != n))) + return -1; + for (;;) { + // Per RFC2732: At most three digits per byte + // Further constraint: Each element fits in a byte + if ((q = scanByte(p, m)) <= p) break; p = q; + if ((q = scan(p, m, '.')) <= p) break; p = q; + if ((q = scanByte(p, m)) <= p) break; p = q; + if ((q = scan(p, m, '.')) <= p) break; p = q; + if ((q = scanByte(p, m)) <= p) break; p = q; + if ((q = scan(p, m, '.')) <= p) break; p = q; + if ((q = scanByte(p, m)) <= p) break; p = q; + if (q < m) break; + return q; + } + fail("Malformed IPv4 address", q); + return -1; + } + + // Take an IPv4 address: Throw an exception if the given interval + // contains anything except an IPv4 address + // + private int takeIPv4Address(int start, int n, String expected) + throws URISyntaxException + { + int p = scanIPv4Address(start, n, true); + if (p <= start) + failExpecting(expected, start); + return p; + } + + // Attempt to parse an IPv4 address, returning -1 on failure but + // allowing the given interval to contain [:<characters>] after + // the IPv4 address. + // + private int parseIPv4Address(int start, int n) { + int p; + + try { + p = scanIPv4Address(start, n, false); + } catch (URISyntaxException x) { + return -1; + } catch (NumberFormatException nfe) { + return -1; + } + + if (p > start && p < n) { + // IPv4 address is followed by something - check that + // it's a ":" as this is the only valid character to + // follow an address. + if (charAt(p) != ':') { + p = -1; + } + } + + if (p > start) + host = substring(start, p); + + return p; + } + + // Android-changed: Allow underscore in hostname. + // Added "_" to the grammars for domainLabel and topLabel. + // hostname = domainlabel [ "." ] | 1*( domainlabel "." ) toplabel [ "." ] + // domainlabel = alphanum | alphanum *( alphanum | "-" | "_" ) alphanum + // toplabel = alpha | alpha *( alphanum | "-" | "_" ) alphanum + // + private int parseHostname(int start, int n) + throws URISyntaxException + { + int p = start; + int q; + int l = -1; // Start of last parsed label + + do { + // Android-changed: Allow underscore in hostname. + // RFC 2396 only allows alphanumeric characters and hyphens, but real, + // large Internet hosts in the wild use underscore, so we have to allow it. + // http://code.google.com/p/android/issues/detail?id=37577 + // http://b/17579865 + // http://b/18016625 + // http://b/18023709 + + // domainlabel = alphanum [ *( alphanum | "-" | "_" ) alphanum ] + q = scan(p, n, L_ALPHANUM, H_ALPHANUM); + if (q <= p) + break; + l = p; + if (q > p) { + p = q; + // Android-changed: Allow underscore in hostname. + // q = scan(p, n, L_ALPHANUM | L_DASH, H_ALPHANUM | H_DASH); + q = scan(p, n, L_ALPHANUM | L_DASH | L_UNDERSCORE, H_ALPHANUM | H_DASH | H_UNDERSCORE); + if (q > p) { + if (charAt(q - 1) == '-') + fail("Illegal character in hostname", q - 1); + p = q; + } + } + q = scan(p, n, '.'); + if (q <= p) + break; + p = q; + } while (p < n); + + if ((p < n) && !at(p, n, ':')) + fail("Illegal character in hostname", p); + + if (l < 0) + failExpecting("hostname", start); + + // for a fully qualified hostname check that the rightmost + // label starts with an alpha character. + if (l > start && !match(charAt(l), L_ALPHA, H_ALPHA)) { + fail("Illegal character in hostname", l); + } + + host = substring(start, p); + return p; + } + + + // IPv6 address parsing, from RFC2373: IPv6 Addressing Architecture + // + // Bug: The grammar in RFC2373 Appendix B does not allow addresses of + // the form ::12.34.56.78, which are clearly shown in the examples + // earlier in the document. Here is the original grammar: + // + // IPv6address = hexpart [ ":" IPv4address ] + // hexpart = hexseq | hexseq "::" [ hexseq ] | "::" [ hexseq ] + // hexseq = hex4 *( ":" hex4) + // hex4 = 1*4HEXDIG + // + // We therefore use the following revised grammar: + // + // IPv6address = hexseq [ ":" IPv4address ] + // | hexseq [ "::" [ hexpost ] ] + // | "::" [ hexpost ] + // hexpost = hexseq | hexseq ":" IPv4address | IPv4address + // hexseq = hex4 *( ":" hex4) + // hex4 = 1*4HEXDIG + // + // This covers all and only the following cases: + // + // hexseq + // hexseq : IPv4address + // hexseq :: + // hexseq :: hexseq + // hexseq :: hexseq : IPv4address + // hexseq :: IPv4address + // :: hexseq + // :: hexseq : IPv4address + // :: IPv4address + // :: + // + // Additionally we constrain the IPv6 address as follows :- + // + // i. IPv6 addresses without compressed zeros should contain + // exactly 16 bytes. + // + // ii. IPv6 addresses with compressed zeros should contain + // less than 16 bytes. + + private int ipv6byteCount = 0; + + private int parseIPv6Reference(int start, int n) + throws URISyntaxException + { + int p = start; + int q; + boolean compressedZeros = false; + + q = scanHexSeq(p, n); + + if (q > p) { + p = q; + if (at(p, n, "::")) { + compressedZeros = true; + p = scanHexPost(p + 2, n); + } else if (at(p, n, ':')) { + p = takeIPv4Address(p + 1, n, "IPv4 address"); + ipv6byteCount += 4; + } + } else if (at(p, n, "::")) { + compressedZeros = true; + p = scanHexPost(p + 2, n); + } + if (p < n) + fail("Malformed IPv6 address", start); + if (ipv6byteCount > 16) + fail("IPv6 address too long", start); + if (!compressedZeros && ipv6byteCount < 16) + fail("IPv6 address too short", start); + if (compressedZeros && ipv6byteCount == 16) + fail("Malformed IPv6 address", start); + + return p; + } + + private int scanHexPost(int start, int n) + throws URISyntaxException + { + int p = start; + int q; + + if (p == n) + return p; + + q = scanHexSeq(p, n); + if (q > p) { + p = q; + if (at(p, n, ':')) { + p++; + p = takeIPv4Address(p, n, "hex digits or IPv4 address"); + ipv6byteCount += 4; + } + } else { + p = takeIPv4Address(p, n, "hex digits or IPv4 address"); + ipv6byteCount += 4; + } + return p; + } + + // Scan a hex sequence; return -1 if one could not be scanned + // + private int scanHexSeq(int start, int n) + throws URISyntaxException + { + int p = start; + int q; + + q = scan(p, n, L_HEX, H_HEX); + if (q <= p) + return -1; + if (at(q, n, '.')) // Beginning of IPv4 address + return -1; + if (q > p + 4) + fail("IPv6 hexadecimal digit sequence too long", p); + ipv6byteCount += 2; + p = q; + while (p < n) { + if (!at(p, n, ':')) + break; + if (at(p + 1, n, ':')) + break; // "::" + p++; + q = scan(p, n, L_HEX, H_HEX); + if (q <= p) + failExpecting("digits for an IPv6 address", p); + if (at(q, n, '.')) { // Beginning of IPv4 address + p--; + break; + } + if (q > p + 4) + fail("IPv6 hexadecimal digit sequence too long", p); + ipv6byteCount += 2; + p = q; + } + + return p; + } + + } + +}
diff --git a/java/net/URISyntaxException.java b/java/net/URISyntaxException.java new file mode 100644 index 0000000..8072c37 --- /dev/null +++ b/java/net/URISyntaxException.java
@@ -0,0 +1,135 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + + +/** + * Checked exception thrown to indicate that a string could not be parsed as a + * URI reference. + * + * @author Mark Reinhold + * @see URI + * @since 1.4 + */ + +public class URISyntaxException + extends Exception +{ + private static final long serialVersionUID = 2137979680897488891L; + + private String input; + private int index; + + /** + * Constructs an instance from the given input string, reason, and error + * index. + * + * @param input The input string + * @param reason A string explaining why the input could not be parsed + * @param index The index at which the parse error occurred, + * or {@code -1} if the index is not known + * + * @throws NullPointerException + * If either the input or reason strings are {@code null} + * + * @throws IllegalArgumentException + * If the error index is less than {@code -1} + */ + public URISyntaxException(String input, String reason, int index) { + super(reason); + if ((input == null) || (reason == null)) + throw new NullPointerException(); + if (index < -1) + throw new IllegalArgumentException(); + this.input = input; + this.index = index; + } + + /** + * Constructs an instance from the given input string and reason. The + * resulting object will have an error index of {@code -1}. + * + * @param input The input string + * @param reason A string explaining why the input could not be parsed + * + * @throws NullPointerException + * If either the input or reason strings are {@code null} + */ + public URISyntaxException(String input, String reason) { + this(input, reason, -1); + } + + /** + * Returns the input string. + * + * @return The input string + */ + public String getInput() { + return input; + } + + /** + * Returns a string explaining why the input string could not be parsed. + * + * @return The reason string + */ + public String getReason() { + return super.getMessage(); + } + + /** + * Returns an index into the input string of the position at which the + * parse error occurred, or {@code -1} if this position is not known. + * + * @return The error index + */ + public int getIndex() { + return index; + } + + /** + * Returns a string describing the parse error. The resulting string + * consists of the reason string followed by a colon character + * ({@code ':'}), a space, and the input string. If the error index is + * defined then the string {@code " at index "} followed by the index, in + * decimal, is inserted after the reason string and before the colon + * character. + * + * @return A string describing the parse error + */ + public String getMessage() { + StringBuffer sb = new StringBuffer(); + sb.append(getReason()); + if (index > -1) { + sb.append(" at index "); + sb.append(index); + } + sb.append(": "); + sb.append(input); + return sb.toString(); + } + +}
diff --git a/java/net/URL.java b/java/net/URL.java new file mode 100644 index 0000000..74dec92 --- /dev/null +++ b/java/net/URL.java
@@ -0,0 +1,1627 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream.GetField; +import java.io.ObjectStreamException; +import java.io.ObjectStreamField; +import java.util.Collections; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Set; +import java.util.StringTokenizer; +import sun.security.util.SecurityConstants; + +/** + * Class {@code URL} represents a Uniform Resource + * Locator, a pointer to a "resource" on the World + * Wide Web. A resource can be something as simple as a file or a + * directory, or it can be a reference to a more complicated object, + * such as a query to a database or to a search engine. More + * information on the types of URLs and their formats can be found at: + * <a href= + * "http://web.archive.org/web/20051219043731/http://archive.ncsa.uiuc.edu/SDG/Software/Mosaic/Demo/url-primer.html"> + * <i>Types of URL</i></a> + * <p> + * In general, a URL can be broken into several parts. Consider the + * following example: + * <blockquote><pre> + * http://www.example.com/docs/resource1.html + * </pre></blockquote> + * <p> + * The URL above indicates that the protocol to use is + * {@code http} (HyperText Transfer Protocol) and that the + * information resides on a host machine named + * {@code www.example.com}. The information on that host + * machine is named {@code /docs/resource1.html}. The exact + * meaning of this name on the host machine is both protocol + * dependent and host dependent. The information normally resides in + * a file, but it could be generated on the fly. This component of + * the URL is called the <i>path</i> component. + * <p> + * A URL can optionally specify a "port", which is the + * port number to which the TCP connection is made on the remote host + * machine. If the port is not specified, the default port for + * the protocol is used instead. For example, the default port for + * {@code http} is {@code 80}. An alternative port could be + * specified as: + * <blockquote><pre> + * http://www.example.com:1080/docs/resource1.html + * </pre></blockquote> + * <p> + * The syntax of {@code URL} is defined by <a + * href="http://www.ietf.org/rfc/rfc2396.txt"><i>RFC 2396: Uniform + * Resource Identifiers (URI): Generic Syntax</i></a>, amended by <a + * href="http://www.ietf.org/rfc/rfc2732.txt"><i>RFC 2732: Format for + * Literal IPv6 Addresses in URLs</i></a>. The Literal IPv6 address format + * also supports scope_ids. The syntax and usage of scope_ids is described + * <a href="Inet6Address.html#scoped">here</a>. + * <p> + * A URL may have appended to it a "fragment", also known + * as a "ref" or a "reference". The fragment is indicated by the sharp + * sign character "#" followed by more characters. For example, + * <blockquote><pre> + * http://java.sun.com/index.html#chapter1 + * </pre></blockquote> + * <p> + * This fragment is not technically part of the URL. Rather, it + * indicates that after the specified resource is retrieved, the + * application is specifically interested in that part of the + * document that has the tag {@code chapter1} attached to it. The + * meaning of a tag is resource specific. + * <p> + * An application can also specify a "relative URL", + * which contains only enough information to reach the resource + * relative to another URL. Relative URLs are frequently used within + * HTML pages. For example, if the contents of the URL: + * <blockquote><pre> + * http://java.sun.com/index.html + * </pre></blockquote> + * contained within it the relative URL: + * <blockquote><pre> + * FAQ.html + * </pre></blockquote> + * it would be a shorthand for: + * <blockquote><pre> + * http://java.sun.com/FAQ.html + * </pre></blockquote> + * <p> + * The relative URL need not specify all the components of a URL. If + * the protocol, host name, or port number is missing, the value is + * inherited from the fully specified URL. The file component must be + * specified. The optional fragment is not inherited. + * <p> + * The URL class does not itself encode or decode any URL components + * according to the escaping mechanism defined in RFC2396. It is the + * responsibility of the caller to encode any fields, which need to be + * escaped prior to calling URL, and also to decode any escaped fields, + * that are returned from URL. Furthermore, because URL has no knowledge + * of URL escaping, it does not recognise equivalence between the encoded + * or decoded form of the same URL. For example, the two URLs:<br> + * <pre> http://foo.com/hello world/ and http://foo.com/hello%20world</pre> + * would be considered not equal to each other. + * <p> + * Note, the {@link java.net.URI} class does perform escaping of its + * component fields in certain circumstances. The recommended way + * to manage the encoding and decoding of URLs is to use {@link java.net.URI}, + * and to convert between these two classes using {@link #toURI()} and + * {@link URI#toURL()}. + * <p> + * The {@link URLEncoder} and {@link URLDecoder} classes can also be + * used, but only for HTML form encoding, which is not the same + * as the encoding scheme defined in RFC2396. + * + * @author James Gosling + * @since JDK1.0 + */ +public final class URL implements java.io.Serializable { + + // Android-changed: Custom built-in URLStreamHandlers for http, https. + // static final String BUILTIN_HANDLERS_PREFIX = "sun.net.www.protocol"; + private static final Set<String> BUILTIN_HANDLER_CLASS_NAMES = createBuiltinHandlerClassNames(); + static final long serialVersionUID = -7627629688361524110L; + + /** + * The property which specifies the package prefix list to be scanned + * for protocol handlers. The value of this property (if any) should + * be a vertical bar delimited list of package names to search through + * for a protocol handler to load. The policy of this class is that + * all protocol handlers will be in a class called <protocolname>.Handler, + * and each package in the list is examined in turn for a matching + * handler. If none are found (or the property is not specified), the + * default package prefix, sun.net.www.protocol, is used. The search + * proceeds from the first package in the list to the last and stops + * when a match is found. + */ + private static final String protocolPathProp = "java.protocol.handler.pkgs"; + + /** + * The protocol to use (ftp, http, nntp, ... etc.) . + * @serial + */ + private String protocol; + + /** + * The host name to connect to. + * @serial + */ + private String host; + + /** + * The protocol port to connect to. + * @serial + */ + private int port = -1; + + /** + * The specified file name on that host. {@code file} is + * defined as {@code path[?query]} + * @serial + */ + private String file; + + /** + * The query part of this URL. + */ + private transient String query; + + /** + * The authority part of this URL. + * @serial + */ + private String authority; + + /** + * The path part of this URL. + */ + private transient String path; + + /** + * The userinfo part of this URL. + */ + private transient String userInfo; + + /** + * # reference. + * @serial + */ + private String ref; + + /** + * The host's IP address, used in equals and hashCode. + * Computed on demand. An uninitialized or unknown hostAddress is null. + */ + transient InetAddress hostAddress; + + /** + * The URLStreamHandler for this URL. + */ + transient URLStreamHandler handler; + + /* Our hash code. + * @serial + */ + private int hashCode = -1; + + private transient UrlDeserializedState tempState; + + /** + * Creates a {@code URL} object from the specified + * {@code protocol}, {@code host}, {@code port} + * number, and {@code file}.<p> + * + * {@code host} can be expressed as a host name or a literal + * IP address. If IPv6 literal address is used, it should be + * enclosed in square brackets ({@code '['} and {@code ']'}), as + * specified by <a + * href="http://www.ietf.org/rfc/rfc2732.txt">RFC 2732</a>; + * However, the literal IPv6 address format defined in <a + * href="http://www.ietf.org/rfc/rfc2373.txt"><i>RFC 2373: IP + * Version 6 Addressing Architecture</i></a> is also accepted.<p> + * + * Specifying a {@code port} number of {@code -1} + * indicates that the URL should use the default port for the + * protocol.<p> + * + * If this is the first URL object being created with the specified + * protocol, a <i>stream protocol handler</i> object, an instance of + * class {@code URLStreamHandler}, is created for that protocol: + * <ol> + * <li>If the application has previously set up an instance of + * {@code URLStreamHandlerFactory} as the stream handler factory, + * then the {@code createURLStreamHandler} method of that instance + * is called with the protocol string as an argument to create the + * stream protocol handler. + * <li>If no {@code URLStreamHandlerFactory} has yet been set up, + * or if the factory's {@code createURLStreamHandler} method + * returns {@code null}, then the constructor finds the + * value of the system property: + * <blockquote><pre> + * java.protocol.handler.pkgs + * </pre></blockquote> + * If the value of that system property is not {@code null}, + * it is interpreted as a list of packages separated by a vertical + * slash character '{@code |}'. The constructor tries to load + * the class named: + * <blockquote><pre> + * <<i>package</i>>.<<i>protocol</i>>.Handler + * </pre></blockquote> + * where <<i>package</i>> is replaced by the name of the package + * and <<i>protocol</i>> is replaced by the name of the protocol. + * If this class does not exist, or if the class exists but it is not + * a subclass of {@code URLStreamHandler}, then the next package + * in the list is tried. + * <li>If the previous step fails to find a protocol handler, then the + * constructor tries to load from a system default package. + * <blockquote><pre> + * <<i>system default package</i>>.<<i>protocol</i>>.Handler + * </pre></blockquote> + * If this class does not exist, or if the class exists but it is not a + * subclass of {@code URLStreamHandler}, then a + * {@code MalformedURLException} is thrown. + * </ol> + * + * <p>Protocol handlers for the following protocols are guaranteed + * to exist on the search path :- + * <blockquote><pre> + * http, https, file, and jar + * </pre></blockquote> + * Protocol handlers for additional protocols may also be + * available. + * + * <p>No validation of the inputs is performed by this constructor. + * + * @param protocol the name of the protocol to use. + * @param host the name of the host. + * @param port the port number on the host. + * @param file the file on the host + * @exception MalformedURLException if an unknown protocol is specified. + * @see java.lang.System#getProperty(java.lang.String) + * @see java.net.URL#setURLStreamHandlerFactory( + * java.net.URLStreamHandlerFactory) + * @see java.net.URLStreamHandler + * @see java.net.URLStreamHandlerFactory#createURLStreamHandler( + * java.lang.String) + */ + public URL(String protocol, String host, int port, String file) + throws MalformedURLException + { + this(protocol, host, port, file, null); + } + + /** + * Creates a URL from the specified {@code protocol} + * name, {@code host} name, and {@code file} name. The + * default port for the specified protocol is used. + * <p> + * This method is equivalent to calling the four-argument + * constructor with the arguments being {@code protocol}, + * {@code host}, {@code -1}, and {@code file}. + * + * No validation of the inputs is performed by this constructor. + * + * @param protocol the name of the protocol to use. + * @param host the name of the host. + * @param file the file on the host. + * @exception MalformedURLException if an unknown protocol is specified. + * @see java.net.URL#URL(java.lang.String, java.lang.String, + * int, java.lang.String) + */ + public URL(String protocol, String host, String file) + throws MalformedURLException { + this(protocol, host, -1, file); + } + + /** + * Creates a {@code URL} object from the specified + * {@code protocol}, {@code host}, {@code port} + * number, {@code file}, and {@code handler}. Specifying + * a {@code port} number of {@code -1} indicates that + * the URL should use the default port for the protocol. Specifying + * a {@code handler} of {@code null} indicates that the URL + * should use a default stream handler for the protocol, as outlined + * for: + * java.net.URL#URL(java.lang.String, java.lang.String, int, + * java.lang.String) + * + * <p>If the handler is not null and there is a security manager, + * the security manager's {@code checkPermission} + * method is called with a + * {@code NetPermission("specifyStreamHandler")} permission. + * This may result in a SecurityException. + * + * No validation of the inputs is performed by this constructor. + * + * @param protocol the name of the protocol to use. + * @param host the name of the host. + * @param port the port number on the host. + * @param file the file on the host + * @param handler the stream handler for the URL. + * @exception MalformedURLException if an unknown protocol is specified. + * @exception SecurityException + * if a security manager exists and its + * {@code checkPermission} method doesn't allow + * specifying a stream handler explicitly. + * @see java.lang.System#getProperty(java.lang.String) + * @see java.net.URL#setURLStreamHandlerFactory( + * java.net.URLStreamHandlerFactory) + * @see java.net.URLStreamHandler + * @see java.net.URLStreamHandlerFactory#createURLStreamHandler( + * java.lang.String) + * @see SecurityManager#checkPermission + * @see java.net.NetPermission + */ + public URL(String protocol, String host, int port, String file, + URLStreamHandler handler) throws MalformedURLException { + if (handler != null) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + // check for permission to specify a handler + checkSpecifyHandler(sm); + } + } + + protocol = protocol.toLowerCase(); + this.protocol = protocol; + if (host != null) { + + /** + * if host is a literal IPv6 address, + * we will make it conform to RFC 2732 + */ + if (host.indexOf(':') >= 0 && !host.startsWith("[")) { + host = "["+host+"]"; + } + this.host = host; + + if (port < -1) { + throw new MalformedURLException("Invalid port number :" + + port); + } + this.port = port; + authority = (port == -1) ? host : host + ":" + port; + } + + // Android-changed: App compat. Prepend '/' if host is null / empty + // Parts parts = new Parts(file); + Parts parts = new Parts(file, host); + path = parts.getPath(); + query = parts.getQuery(); + + if (query != null) { + this.file = path + "?" + query; + } else { + this.file = path; + } + ref = parts.getRef(); + + // Note: we don't do validation of the URL here. Too risky to change + // right now, but worth considering for future reference. -br + if (handler == null && + (handler = getURLStreamHandler(protocol)) == null) { + throw new MalformedURLException("unknown protocol: " + protocol); + } + this.handler = handler; + } + + /** + * Creates a {@code URL} object from the {@code String} + * representation. + * <p> + * This constructor is equivalent to a call to the two-argument + * constructor with a {@code null} first argument. + * + * @param spec the {@code String} to parse as a URL. + * @exception MalformedURLException if no protocol is specified, or an + * unknown protocol is found, or {@code spec} is {@code null}. + * @see java.net.URL#URL(java.net.URL, java.lang.String) + */ + public URL(String spec) throws MalformedURLException { + this(null, spec); + } + + /** + * Creates a URL by parsing the given spec within a specified context. + * + * The new URL is created from the given context URL and the spec + * argument as described in + * RFC2396 "Uniform Resource Identifiers : Generic * Syntax" : + * <blockquote><pre> + * <scheme>://<authority><path>?<query>#<fragment> + * </pre></blockquote> + * The reference is parsed into the scheme, authority, path, query and + * fragment parts. If the path component is empty and the scheme, + * authority, and query components are undefined, then the new URL is a + * reference to the current document. Otherwise, the fragment and query + * parts present in the spec are used in the new URL. + * <p> + * If the scheme component is defined in the given spec and does not match + * the scheme of the context, then the new URL is created as an absolute + * URL based on the spec alone. Otherwise the scheme component is inherited + * from the context URL. + * <p> + * If the authority component is present in the spec then the spec is + * treated as absolute and the spec authority and path will replace the + * context authority and path. If the authority component is absent in the + * spec then the authority of the new URL will be inherited from the + * context. + * <p> + * If the spec's path component begins with a slash character + * "/" then the + * path is treated as absolute and the spec path replaces the context path. + * <p> + * Otherwise, the path is treated as a relative path and is appended to the + * context path, as described in RFC2396. Also, in this case, + * the path is canonicalized through the removal of directory + * changes made by occurrences of ".." and ".". + * <p> + * For a more detailed description of URL parsing, refer to RFC2396. + * + * @param context the context in which to parse the specification. + * @param spec the {@code String} to parse as a URL. + * @exception MalformedURLException if no protocol is specified, or an + * unknown protocol is found, or {@code spec} is {@code null}. + * @see java.net.URL#URL(java.lang.String, java.lang.String, + * int, java.lang.String) + * @see java.net.URLStreamHandler + * @see java.net.URLStreamHandler#parseURL(java.net.URL, + * java.lang.String, int, int) + */ + public URL(URL context, String spec) throws MalformedURLException { + this(context, spec, null); + } + + /** + * Creates a URL by parsing the given spec with the specified handler + * within a specified context. If the handler is null, the parsing + * occurs as with the two argument constructor. + * + * @param context the context in which to parse the specification. + * @param spec the {@code String} to parse as a URL. + * @param handler the stream handler for the URL. + * @exception MalformedURLException if no protocol is specified, or an + * unknown protocol is found, or {@code spec} is {@code null}. + * @exception SecurityException + * if a security manager exists and its + * {@code checkPermission} method doesn't allow + * specifying a stream handler. + * @see java.net.URL#URL(java.lang.String, java.lang.String, + * int, java.lang.String) + * @see java.net.URLStreamHandler + * @see java.net.URLStreamHandler#parseURL(java.net.URL, + * java.lang.String, int, int) + */ + public URL(URL context, String spec, URLStreamHandler handler) + throws MalformedURLException + { + String original = spec; + int i, limit, c; + int start = 0; + String newProtocol = null; + boolean aRef=false; + boolean isRelative = false; + + // Check for permission to specify a handler + if (handler != null) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + checkSpecifyHandler(sm); + } + } + + try { + limit = spec.length(); + while ((limit > 0) && (spec.charAt(limit - 1) <= ' ')) { + limit--; //eliminate trailing whitespace + } + while ((start < limit) && (spec.charAt(start) <= ' ')) { + start++; // eliminate leading whitespace + } + + if (spec.regionMatches(true, start, "url:", 0, 4)) { + start += 4; + } + if (start < spec.length() && spec.charAt(start) == '#') { + /* we're assuming this is a ref relative to the context URL. + * This means protocols cannot start w/ '#', but we must parse + * ref URL's like: "hello:there" w/ a ':' in them. + */ + aRef=true; + } + for (i = start ; !aRef && (i < limit) && + ((c = spec.charAt(i)) != '/') ; i++) { + if (c == ':') { + + String s = spec.substring(start, i).toLowerCase(); + if (isValidProtocol(s)) { + newProtocol = s; + start = i + 1; + } + break; + } + } + + // Only use our context if the protocols match. + protocol = newProtocol; + if ((context != null) && ((newProtocol == null) || + newProtocol.equalsIgnoreCase(context.protocol))) { + // inherit the protocol handler from the context + // if not specified to the constructor + if (handler == null) { + handler = context.handler; + } + + // If the context is a hierarchical URL scheme and the spec + // contains a matching scheme then maintain backwards + // compatibility and treat it as if the spec didn't contain + // the scheme; see 5.2.3 of RFC2396 + if (context.path != null && context.path.startsWith("/")) + newProtocol = null; + + if (newProtocol == null) { + protocol = context.protocol; + authority = context.authority; + userInfo = context.userInfo; + host = context.host; + port = context.port; + file = context.file; + path = context.path; + isRelative = true; + } + } + + if (protocol == null) { + throw new MalformedURLException("no protocol: "+original); + } + + // Get the protocol handler if not specified or the protocol + // of the context could not be used + if (handler == null && + (handler = getURLStreamHandler(protocol)) == null) { + throw new MalformedURLException("unknown protocol: "+protocol); + } + + this.handler = handler; + + i = spec.indexOf('#', start); + if (i >= 0) { + ref = spec.substring(i + 1, limit); + limit = i; + } + + /* + * Handle special case inheritance of query and fragment + * implied by RFC2396 section 5.2.2. + */ + if (isRelative && start == limit) { + query = context.query; + if (ref == null) { + ref = context.ref; + } + } + + handler.parseURL(this, spec, start, limit); + + } catch(MalformedURLException e) { + throw e; + } catch(Exception e) { + MalformedURLException exception = new MalformedURLException(e.getMessage()); + exception.initCause(e); + throw exception; + } + } + + /* + * Returns true if specified string is a valid protocol name. + */ + private boolean isValidProtocol(String protocol) { + int len = protocol.length(); + if (len < 1) + return false; + char c = protocol.charAt(0); + if (!Character.isLetter(c)) + return false; + for (int i = 1; i < len; i++) { + c = protocol.charAt(i); + if (!Character.isLetterOrDigit(c) && c != '.' && c != '+' && + c != '-') { + return false; + } + } + return true; + } + + /* + * Checks for permission to specify a stream handler. + */ + private void checkSpecifyHandler(SecurityManager sm) { + sm.checkPermission(SecurityConstants.SPECIFY_HANDLER_PERMISSION); + } + + /** + * Sets the fields of the URL. This is not a public method so that + * only URLStreamHandlers can modify URL fields. URLs are + * otherwise constant. + * + * @param protocol the name of the protocol to use + * @param host the name of the host + @param port the port number on the host + * @param file the file on the host + * @param ref the internal reference in the URL + */ + void set(String protocol, String host, int port, + String file, String ref) { + synchronized (this) { + this.protocol = protocol; + this.host = host; + authority = port == -1 ? host : host + ":" + port; + this.port = port; + this.file = file; + this.ref = ref; + /* This is very important. We must recompute this after the + * URL has been changed. */ + hashCode = -1; + hostAddress = null; + int q = file.lastIndexOf('?'); + if (q != -1) { + query = file.substring(q+1); + path = file.substring(0, q); + } else + path = file; + } + } + + /** + * Sets the specified 8 fields of the URL. This is not a public method so + * that only URLStreamHandlers can modify URL fields. URLs are otherwise + * constant. + * + * @param protocol the name of the protocol to use + * @param host the name of the host + * @param port the port number on the host + * @param authority the authority part for the url + * @param userInfo the username and password + * @param path the file on the host + * @param ref the internal reference in the URL + * @param query the query part of this URL + * @since 1.3 + */ + void set(String protocol, String host, int port, + String authority, String userInfo, String path, + String query, String ref) { + synchronized (this) { + this.protocol = protocol; + this.host = host; + this.port = port; + // Android-changed: App compat. Only include query part if it's nonempty. + // this.file = query == null ? path : path + "?" + query; + this.file = (query == null || query.isEmpty()) ? path : path + "?" + query; + this.userInfo = userInfo; + this.path = path; + this.ref = ref; + /* This is very important. We must recompute this after the + * URL has been changed. */ + hashCode = -1; + hostAddress = null; + this.query = query; + this.authority = authority; + } + } + + /** + * Gets the query part of this {@code URL}. + * + * @return the query part of this {@code URL}, + * or <CODE>null</CODE> if one does not exist + * @since 1.3 + */ + public String getQuery() { + return query; + } + + /** + * Gets the path part of this {@code URL}. + * + * @return the path part of this {@code URL}, or an + * empty string if one does not exist + * @since 1.3 + */ + public String getPath() { + return path; + } + + /** + * Gets the userInfo part of this {@code URL}. + * + * @return the userInfo part of this {@code URL}, or + * <CODE>null</CODE> if one does not exist + * @since 1.3 + */ + public String getUserInfo() { + return userInfo; + } + + /** + * Gets the authority part of this {@code URL}. + * + * @return the authority part of this {@code URL} + * @since 1.3 + */ + public String getAuthority() { + return authority; + } + + /** + * Gets the port number of this {@code URL}. + * + * @return the port number, or -1 if the port is not set + */ + public int getPort() { + return port; + } + + /** + * Gets the default port number of the protocol associated + * with this {@code URL}. If the URL scheme or the URLStreamHandler + * for the URL do not define a default port number, + * then -1 is returned. + * + * @return the port number + * @since 1.4 + */ + public int getDefaultPort() { + return handler.getDefaultPort(); + } + + /** + * Gets the protocol name of this {@code URL}. + * + * @return the protocol of this {@code URL}. + */ + public String getProtocol() { + return protocol; + } + + /** + * Gets the host name of this {@code URL}, if applicable. + * The format of the host conforms to RFC 2732, i.e. for a + * literal IPv6 address, this method will return the IPv6 address + * enclosed in square brackets ({@code '['} and {@code ']'}). + * + * @return the host name of this {@code URL}. + */ + public String getHost() { + return host; + } + + /** + * Gets the file name of this {@code URL}. + * The returned file portion will be + * the same as <CODE>getPath()</CODE>, plus the concatenation of + * the value of <CODE>getQuery()</CODE>, if any. If there is + * no query portion, this method and <CODE>getPath()</CODE> will + * return identical results. + * + * @return the file name of this {@code URL}, + * or an empty string if one does not exist + */ + public String getFile() { + return file; + } + + /** + * Gets the anchor (also known as the "reference") of this + * {@code URL}. + * + * @return the anchor (also known as the "reference") of this + * {@code URL}, or <CODE>null</CODE> if one does not exist + */ + public String getRef() { + return ref; + } + + // Android-changed: Don't let URL.equals() attempt to resolve host names. + /** + * Compares this URL for equality with another object.<p> + * + * If the given object is not a URL then this method immediately returns + * {@code false}.<p> + * + * Two URL objects are equal if they have the same protocol, reference + * equivalent hosts, have the same port number on the host, and the same + * file and fragment of the file.<p> + * + * Returns true if this URL equals {@code o}. URLs are equal if they have + * the same protocol, host, port, file, and reference. + * + * <h3>Network I/O Warning</h3> + * <p>Some implementations of URL.equals() resolve host names over the + * network. This is problematic: + * <ul> + * <li><strong>The network may be slow.</strong> Many classes, including + * core collections like {@link java.util.Map Map} and {@link java.util.Set + * Set} expect that {@code equals} and {@code hashCode} will return quickly. + * By violating this assumption, this method posed potential performance + * problems. + * <li><strong>Equal IP addresses do not imply equal content.</strong> + * Virtual hosting permits unrelated sites to share an IP address. This + * method could report two otherwise unrelated URLs to be equal because + * they're hosted on the same server.</li> + * <li><strong>The network may not be available.</strong> Two URLs could be + * equal when a network is available and unequal otherwise.</li> + * <li><strong>The network may change.</strong> The IP address for a given + * host name varies by network and over time. This is problematic for mobile + * devices. Two URLs could be equal on some networks and unequal on + * others.</li> + * </ul> + * <p>This problem is fixed in Android 4.0 (Ice Cream Sandwich). In that + * release, URLs are only equal if their host names are equal (ignoring + * case). + * + * @param obj the URL to compare against. + * @return {@code true} if the objects are the same; + * {@code false} otherwise. + */ + public boolean equals(Object obj) { + if (!(obj instanceof URL)) + return false; + URL u2 = (URL)obj; + + return handler.equals(this, u2); + } + + /** + * Creates an integer suitable for hash table indexing.<p> + * + * The hash code is based upon all the URL components relevant for URL + * comparison. As such, this operation is a blocking operation.<p> + * + * @return a hash code for this {@code URL}. + */ + public synchronized int hashCode() { + if (hashCode != -1) + return hashCode; + + hashCode = handler.hashCode(this); + return hashCode; + } + + /** + * Compares two URLs, excluding the fragment component.<p> + * + * Returns {@code true} if this {@code URL} and the + * {@code other} argument are equal without taking the + * fragment component into consideration. + * + * @param other the {@code URL} to compare against. + * @return {@code true} if they reference the same remote object; + * {@code false} otherwise. + */ + public boolean sameFile(URL other) { + return handler.sameFile(this, other); + } + + /** + * Constructs a string representation of this {@code URL}. The + * string is created by calling the {@code toExternalForm} + * method of the stream protocol handler for this object. + * + * @return a string representation of this object. + * @see java.net.URL#URL(java.lang.String, java.lang.String, int, + * java.lang.String) + * @see java.net.URLStreamHandler#toExternalForm(java.net.URL) + */ + public String toString() { + return toExternalForm(); + } + + /** + * Constructs a string representation of this {@code URL}. The + * string is created by calling the {@code toExternalForm} + * method of the stream protocol handler for this object. + * + * @return a string representation of this object. + * @see java.net.URL#URL(java.lang.String, java.lang.String, + * int, java.lang.String) + * @see java.net.URLStreamHandler#toExternalForm(java.net.URL) + */ + public String toExternalForm() { + return handler.toExternalForm(this); + } + + /** + * Returns a {@link java.net.URI} equivalent to this URL. + * This method functions in the same way as {@code new URI (this.toString())}. + * <p>Note, any URL instance that complies with RFC 2396 can be converted + * to a URI. However, some URLs that are not strictly in compliance + * can not be converted to a URI. + * + * @exception URISyntaxException if this URL is not formatted strictly according to + * to RFC2396 and cannot be converted to a URI. + * + * @return a URI instance equivalent to this URL. + * @since 1.5 + */ + public URI toURI() throws URISyntaxException { + return new URI (toString()); + } + + /** + * Returns a {@link java.net.URLConnection URLConnection} instance that + * represents a connection to the remote object referred to by the + * {@code URL}. + * + * <P>A new instance of {@linkplain java.net.URLConnection URLConnection} is + * created every time when invoking the + * {@linkplain java.net.URLStreamHandler#openConnection(URL) + * URLStreamHandler.openConnection(URL)} method of the protocol handler for + * this URL.</P> + * + * <P>It should be noted that a URLConnection instance does not establish + * the actual network connection on creation. This will happen only when + * calling {@linkplain java.net.URLConnection#connect() URLConnection.connect()}.</P> + * + * <P>If for the URL's protocol (such as HTTP or JAR), there + * exists a public, specialized URLConnection subclass belonging + * to one of the following packages or one of their subpackages: + * java.lang, java.io, java.util, java.net, the connection + * returned will be of that subclass. For example, for HTTP an + * HttpURLConnection will be returned, and for JAR a + * JarURLConnection will be returned.</P> + * + * @return a {@link java.net.URLConnection URLConnection} linking + * to the URL. + * @exception IOException if an I/O exception occurs. + * @see java.net.URL#URL(java.lang.String, java.lang.String, + * int, java.lang.String) + */ + public URLConnection openConnection() throws java.io.IOException { + return handler.openConnection(this); + } + + /** + * Same as {@link #openConnection()}, except that the connection will be + * made through the specified proxy; Protocol handlers that do not + * support proxing will ignore the proxy parameter and make a + * normal connection. + * + * Invoking this method preempts the system's default ProxySelector + * settings. + * + * @param proxy the Proxy through which this connection + * will be made. If direct connection is desired, + * Proxy.NO_PROXY should be specified. + * @return a {@code URLConnection} to the URL. + * @exception IOException if an I/O exception occurs. + * @exception SecurityException if a security manager is present + * and the caller doesn't have permission to connect + * to the proxy. + * @exception IllegalArgumentException will be thrown if proxy is null, + * or proxy has the wrong type + * @exception UnsupportedOperationException if the subclass that + * implements the protocol handler doesn't support + * this method. + * @see java.net.URL#URL(java.lang.String, java.lang.String, + * int, java.lang.String) + * @see java.net.URLConnection + * @see java.net.URLStreamHandler#openConnection(java.net.URL, + * java.net.Proxy) + * @since 1.5 + */ + public URLConnection openConnection(Proxy proxy) + throws java.io.IOException { + if (proxy == null) { + throw new IllegalArgumentException("proxy can not be null"); + } + + // Create a copy of Proxy as a security measure + Proxy p = proxy == Proxy.NO_PROXY ? Proxy.NO_PROXY : sun.net.ApplicationProxy.create(proxy); + SecurityManager sm = System.getSecurityManager(); + if (p.type() != Proxy.Type.DIRECT && sm != null) { + InetSocketAddress epoint = (InetSocketAddress) p.address(); + if (epoint.isUnresolved()) + sm.checkConnect(epoint.getHostName(), epoint.getPort()); + else + sm.checkConnect(epoint.getAddress().getHostAddress(), + epoint.getPort()); + } + return handler.openConnection(this, p); + } + + /** + * Opens a connection to this {@code URL} and returns an + * {@code InputStream} for reading from that connection. This + * method is a shorthand for: + * <blockquote><pre> + * openConnection().getInputStream() + * </pre></blockquote> + * + * @return an input stream for reading from the URL connection. + * @exception IOException if an I/O exception occurs. + * @see java.net.URL#openConnection() + * @see java.net.URLConnection#getInputStream() + */ + public final InputStream openStream() throws java.io.IOException { + return openConnection().getInputStream(); + } + + /** + * Gets the contents of this URL. This method is a shorthand for: + * <blockquote><pre> + * openConnection().getContent() + * </pre></blockquote> + * + * @return the contents of this URL. + * @exception IOException if an I/O exception occurs. + * @see java.net.URLConnection#getContent() + */ + public final Object getContent() throws java.io.IOException { + return openConnection().getContent(); + } + + /** + * Gets the contents of this URL. This method is a shorthand for: + * <blockquote><pre> + * openConnection().getContent(Class[]) + * </pre></blockquote> + * + * @param classes an array of Java types + * @return the content object of this URL that is the first match of + * the types specified in the classes array. + * null if none of the requested types are supported. + * @exception IOException if an I/O exception occurs. + * @see java.net.URLConnection#getContent(Class[]) + * @since 1.3 + */ + public final Object getContent(Class[] classes) + throws java.io.IOException { + return openConnection().getContent(classes); + } + + /** + * The URLStreamHandler factory. + */ + static URLStreamHandlerFactory factory; + + /** + * Sets an application's {@code URLStreamHandlerFactory}. + * This method can be called at most once in a given Java Virtual + * Machine. + * + *<p> The {@code URLStreamHandlerFactory} instance is used to + *construct a stream protocol handler from a protocol name. + * + * <p> If there is a security manager, this method first calls + * the security manager's {@code checkSetFactory} method + * to ensure the operation is allowed. + * This could result in a SecurityException. + * + * @param fac the desired factory. + * @exception Error if the application has already set a factory. + * @exception SecurityException if a security manager exists and its + * {@code checkSetFactory} method doesn't allow + * the operation. + * @see java.net.URL#URL(java.lang.String, java.lang.String, + * int, java.lang.String) + * @see java.net.URLStreamHandlerFactory + * @see SecurityManager#checkSetFactory + */ + public static void setURLStreamHandlerFactory(URLStreamHandlerFactory fac) { + synchronized (streamHandlerLock) { + if (factory != null) { + throw new Error("factory already defined"); + } + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkSetFactory(); + } + handlers.clear(); + factory = fac; + } + } + + /** + * A table of protocol handlers. + */ + static Hashtable<String,URLStreamHandler> handlers = new Hashtable<>(); + private static Object streamHandlerLock = new Object(); + + /** + * Returns the Stream Handler. + * @param protocol the protocol to use + */ + static URLStreamHandler getURLStreamHandler(String protocol) { + + URLStreamHandler handler = handlers.get(protocol); + if (handler == null) { + + boolean checkedWithFactory = false; + + // Use the factory (if any) + if (factory != null) { + handler = factory.createURLStreamHandler(protocol); + checkedWithFactory = true; + } + + // Try java protocol handler + if (handler == null) { + // Android-changed: Android doesn't need AccessController. + // Remove unnecessary use of reflection for sun classes + /* + packagePrefixList + = java.security.AccessController.doPrivileged( + new sun.security.action.GetPropertyAction( + protocolPathProp,"")); + if (packagePrefixList != "") { + packagePrefixList += "|"; + } + + // REMIND: decide whether to allow the "null" class prefix + // or not. + packagePrefixList += "sun.net.www.protocol"; + */ + final String packagePrefixList = System.getProperty(protocolPathProp,""); + + StringTokenizer packagePrefixIter = + new StringTokenizer(packagePrefixList, "|"); + + while (handler == null && + packagePrefixIter.hasMoreTokens()) { + + String packagePrefix = + packagePrefixIter.nextToken().trim(); + try { + String clsName = packagePrefix + "." + protocol + + ".Handler"; + Class<?> cls = null; + try { + ClassLoader cl = ClassLoader.getSystemClassLoader(); + // BEGIN Android-changed: Fall back to thread's contextClassLoader. + // http://b/25897689 + cls = Class.forName(clsName, true, cl); + } catch (ClassNotFoundException e) { + ClassLoader contextLoader = Thread.currentThread().getContextClassLoader(); + if (contextLoader != null) { + cls = Class.forName(clsName, true, contextLoader); + } + // END Android-changed: Fall back to thread's contextClassLoader. + } + if (cls != null) { + handler = + (URLStreamHandler)cls.newInstance(); + } + } catch (ReflectiveOperationException ignored) { + } + } + } + + // BEGIN Android-added: Custom built-in URLStreamHandlers for http, https. + // Fallback to built-in stream handler. + if (handler == null) { + try { + handler = createBuiltinHandler(protocol); + } catch (Exception e) { + throw new AssertionError(e); + } + } + // END Android-added: Custom built-in URLStreamHandlers for http, https. + + synchronized (streamHandlerLock) { + + URLStreamHandler handler2 = null; + + // Check again with hashtable just in case another + // thread created a handler since we last checked + handler2 = handlers.get(protocol); + + if (handler2 != null) { + return handler2; + } + + // Check with factory if another thread set a + // factory since our last check + if (!checkedWithFactory && factory != null) { + handler2 = factory.createURLStreamHandler(protocol); + } + + if (handler2 != null) { + // The handler from the factory must be given more + // importance. Discard the default handler that + // this thread created. + handler = handler2; + } + + // Insert this handler into the hashtable + if (handler != null) { + handlers.put(protocol, handler); + } + + } + } + + return handler; + + } + + // BEGIN Android-added: Custom built-in URLStreamHandlers for http, https. + /** + * Returns an instance of the built-in handler for the given protocol, or null if none exists. + */ + private static URLStreamHandler createBuiltinHandler(String protocol) + throws ClassNotFoundException, InstantiationException, IllegalAccessException { + URLStreamHandler handler = null; + if (protocol.equals("file")) { + handler = new sun.net.www.protocol.file.Handler(); + } else if (protocol.equals("ftp")) { + handler = new sun.net.www.protocol.ftp.Handler(); + } else if (protocol.equals("jar")) { + handler = new sun.net.www.protocol.jar.Handler(); + } else if (protocol.equals("http")) { + handler = (URLStreamHandler)Class. + forName("com.android.okhttp.HttpHandler").newInstance(); + } else if (protocol.equals("https")) { + handler = (URLStreamHandler)Class. + forName("com.android.okhttp.HttpsHandler").newInstance(); + } + return handler; + } + + /** Names of implementation classes returned by {@link #createBuiltinHandler(String)}. */ + private static Set<String> createBuiltinHandlerClassNames() { + Set<String> result = new HashSet<>(); + // Refer to class names rather than classes to avoid needlessly triggering <clinit>. + result.add("sun.net.www.protocol.file.Handler"); + result.add("sun.net.www.protocol.ftp.Handler"); + result.add("sun.net.www.protocol.jar.Handler"); + result.add("com.android.okhttp.HttpHandler"); + result.add("com.android.okhttp.HttpsHandler"); + return Collections.unmodifiableSet(result); + } + // END Android-added: Custom built-in URLStreamHandlers for http, https. + + /** + * @serialField protocol String + * + * @serialField host String + * + * @serialField port int + * + * @serialField authority String + * + * @serialField file String + * + * @serialField ref String + * + * @serialField hashCode int + * + */ + private static final ObjectStreamField[] serialPersistentFields = { + new ObjectStreamField("protocol", String.class), + new ObjectStreamField("host", String.class), + new ObjectStreamField("port", int.class), + new ObjectStreamField("authority", String.class), + new ObjectStreamField("file", String.class), + new ObjectStreamField("ref", String.class), + // Android-changed: App compat: hashCode should not be serialized. + // new ObjectStreamField("hashCode", int.class), }; + }; + + /** + * WriteObject is called to save the state of the URL to an + * ObjectOutputStream. The handler is not saved since it is + * specific to this system. + * + * @serialData the default write object value. When read back in, + * the reader must ensure that calling getURLStreamHandler with + * the protocol variable returns a valid URLStreamHandler and + * throw an IOException if it does not. + */ + private synchronized void writeObject(java.io.ObjectOutputStream s) + throws IOException + { + s.defaultWriteObject(); // write the fields + } + + /** + * readObject is called to restore the state of the URL from the + * stream. It reads the components of the URL and finds the local + * stream handler. + */ + private synchronized void readObject(java.io.ObjectInputStream s) + throws IOException, ClassNotFoundException { + GetField gf = s.readFields(); + String protocol = (String)gf.get("protocol", null); + if (getURLStreamHandler(protocol) == null) { + throw new IOException("unknown protocol: " + protocol); + } + String host = (String)gf.get("host", null); + int port = gf.get("port", -1); + String authority = (String)gf.get("authority", null); + String file = (String)gf.get("file", null); + String ref = (String)gf.get("ref", null); + // Android-changed: App compat: hashCode should not be serialized. + // int hashCode = gf.get("hashCode", -1); + final int hashCode = -1; + if (authority == null + && ((host != null && host.length() > 0) || port != -1)) { + if (host == null) + host = ""; + authority = (port == -1) ? host : host + ":" + port; + } + tempState = new UrlDeserializedState(protocol, host, port, authority, + file, ref, hashCode); + } + + /** + * Replaces the de-serialized object with an URL object. + * + * @return a newly created object from the deserialzed state. + * + * @throws ObjectStreamException if a new object replacing this + * object could not be created + */ + + private Object readResolve() throws ObjectStreamException { + + URLStreamHandler handler = null; + // already been checked in readObject + handler = getURLStreamHandler(tempState.getProtocol()); + + URL replacementURL = null; + if (isBuiltinStreamHandler(handler.getClass().getName())) { + replacementURL = fabricateNewURL(); + } else { + replacementURL = setDeserializedFields(handler); + } + return replacementURL; + } + + private URL setDeserializedFields(URLStreamHandler handler) { + URL replacementURL; + String userInfo = null; + String protocol = tempState.getProtocol(); + String host = tempState.getHost(); + int port = tempState.getPort(); + String authority = tempState.getAuthority(); + String file = tempState.getFile(); + String ref = tempState.getRef(); + int hashCode = tempState.getHashCode(); + + + // Construct authority part + if (authority == null + && ((host != null && host.length() > 0) || port != -1)) { + if (host == null) + host = ""; + authority = (port == -1) ? host : host + ":" + port; + + // Handle hosts with userInfo in them + int at = host.lastIndexOf('@'); + if (at != -1) { + userInfo = host.substring(0, at); + host = host.substring(at+1); + } + } else if (authority != null) { + // Construct user info part + int ind = authority.indexOf('@'); + if (ind != -1) + userInfo = authority.substring(0, ind); + } + + // Construct path and query part + String path = null; + String query = null; + if (file != null) { + // Fix: only do this if hierarchical? + int q = file.lastIndexOf('?'); + if (q != -1) { + query = file.substring(q+1); + path = file.substring(0, q); + } else + path = file; + } + + // Set the object fields. + this.protocol = protocol; + this.host = host; + this.port = port; + this.file = file; + this.authority = authority; + this.ref = ref; + this.hashCode = hashCode; + this.handler = handler; + this.query = query; + this.path = path; + this.userInfo = userInfo; + replacementURL = this; + return replacementURL; + } + + private URL fabricateNewURL() + throws InvalidObjectException { + // create URL string from deserialized object + URL replacementURL = null; + String urlString = tempState.reconstituteUrlString(); + + try { + replacementURL = new URL(urlString); + } catch (MalformedURLException mEx) { + resetState(); + InvalidObjectException invoEx = new InvalidObjectException( + "Malformed URL: " + urlString); + invoEx.initCause(mEx); + throw invoEx; + } + replacementURL.setSerializedHashCode(tempState.getHashCode()); + resetState(); + return replacementURL; + } + + private boolean isBuiltinStreamHandler(String handlerClassName) { + // Android-changed: Some built-in handlers (eg. HttpHandler) are not in sun.net.www.protocol. + // return (handlerClassName.startsWith(BUILTIN_HANDLERS_PREFIX)); + return BUILTIN_HANDLER_CLASS_NAMES.contains(handlerClassName); + } + + private void resetState() { + this.protocol = null; + this.host = null; + this.port = -1; + this.file = null; + this.authority = null; + this.ref = null; + this.hashCode = -1; + this.handler = null; + this.query = null; + this.path = null; + this.userInfo = null; + this.tempState = null; + } + + private void setSerializedHashCode(int hc) { + this.hashCode = hc; + } +} + +class Parts { + String path, query, ref; + + // Android-changed: App compat. Prepend '/' if host is null / empty. + // Parts(String file) + Parts(String file, String host) { + int ind = file.indexOf('#'); + ref = ind < 0 ? null: file.substring(ind + 1); + file = ind < 0 ? file: file.substring(0, ind); + int q = file.lastIndexOf('?'); + if (q != -1) { + query = file.substring(q+1); + path = file.substring(0, q); + } else { + path = file; + } + // BEGIN Android-changed: App compat. Prepend '/' if host is null / empty. + if (path != null && path.length() > 0 && path.charAt(0) != '/' && + host != null && !host.isEmpty()) { + path = '/' + path; + } + // END Android-changed: App compat. Prepend '/' if host is null / empty. + } + + String getPath() { + return path; + } + + String getQuery() { + return query; + } + + String getRef() { + return ref; + } +} + +final class UrlDeserializedState { + private final String protocol; + private final String host; + private final int port; + private final String authority; + private final String file; + private final String ref; + private final int hashCode; + + public UrlDeserializedState(String protocol, + String host, int port, + String authority, String file, + String ref, int hashCode) { + this.protocol = protocol; + this.host = host; + this.port = port; + this.authority = authority; + this.file = file; + this.ref = ref; + this.hashCode = hashCode; + } + + String getProtocol() { + return protocol; + } + + String getHost() { + return host; + } + + String getAuthority () { + return authority; + } + + int getPort() { + return port; + } + + String getFile () { + return file; + } + + String getRef () { + return ref; + } + + int getHashCode () { + return hashCode; + } + + String reconstituteUrlString() { + + // pre-compute length of StringBuilder + int len = protocol.length() + 1; + if (authority != null && authority.length() > 0) + len += 2 + authority.length(); + if (file != null) { + len += file.length(); + } + if (ref != null) + len += 1 + ref.length(); + StringBuilder result = new StringBuilder(len); + result.append(protocol); + result.append(":"); + if (authority != null && authority.length() > 0) { + result.append("//"); + result.append(authority); + } + if (file != null) { + result.append(file); + } + if (ref != null) { + result.append("#"); + result.append(ref); + } + return result.toString(); + } +}
diff --git a/java/net/URLClassLoader.java b/java/net/URLClassLoader.java new file mode 100644 index 0000000..6e8acb1 --- /dev/null +++ b/java/net/URLClassLoader.java
@@ -0,0 +1,819 @@ +/* + * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.Closeable; +import java.io.File; +import java.io.FilePermission; +import java.io.IOException; +import java.io.InputStream; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.CodeSigner; +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.PrivilegedAction; +import java.security.PrivilegedExceptionAction; +import java.security.SecureClassLoader; +import java.util.Enumeration; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.jar.Attributes; +import java.util.jar.Attributes.Name; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import sun.misc.Resource; +import sun.misc.URLClassPath; +import sun.net.www.ParseUtil; +import sun.security.util.SecurityConstants; + +/** + * This class loader is used to load classes and resources from a search + * path of URLs referring to both JAR files and directories. Any URL that + * ends with a '/' is assumed to refer to a directory. Otherwise, the URL + * is assumed to refer to a JAR file which will be opened as needed. + * <p> + * The AccessControlContext of the thread that created the instance of + * URLClassLoader will be used when subsequently loading classes and + * resources. + * <p> + * The classes that are loaded are by default granted permission only to + * access the URLs specified when the URLClassLoader was created. + * + * @author David Connelly + * @since 1.2 + */ +public class URLClassLoader extends SecureClassLoader implements Closeable { + /* The search path for classes and resources */ + private final URLClassPath ucp; + + /* The context to be used when loading classes and resources */ + private final AccessControlContext acc; + + /** + * Constructs a new URLClassLoader for the given URLs. The URLs will be + * searched in the order specified for classes and resources after first + * searching in the specified parent class loader. Any URL that ends with + * a '/' is assumed to refer to a directory. Otherwise, the URL is assumed + * to refer to a JAR file which will be downloaded and opened as needed. + * + * <p>If there is a security manager, this method first + * calls the security manager's {@code checkCreateClassLoader} method + * to ensure creation of a class loader is allowed. + * + * @param urls the URLs from which to load classes and resources + * @param parent the parent class loader for delegation + * @exception SecurityException if a security manager exists and its + * {@code checkCreateClassLoader} method doesn't allow + * creation of a class loader. + * @exception NullPointerException if {@code urls} is {@code null}. + * @see SecurityManager#checkCreateClassLoader + */ + public URLClassLoader(URL[] urls, ClassLoader parent) { + super(parent); + // this is to make the stack depth consistent with 1.1 + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkCreateClassLoader(); + } + this.acc = AccessController.getContext(); + ucp = new URLClassPath(urls, acc); + } + + URLClassLoader(URL[] urls, ClassLoader parent, + AccessControlContext acc) { + super(parent); + // this is to make the stack depth consistent with 1.1 + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkCreateClassLoader(); + } + this.acc = acc; + ucp = new URLClassPath(urls, acc); + } + + /** + * Constructs a new URLClassLoader for the specified URLs using the + * default delegation parent {@code ClassLoader}. The URLs will + * be searched in the order specified for classes and resources after + * first searching in the parent class loader. Any URL that ends with + * a '/' is assumed to refer to a directory. Otherwise, the URL is + * assumed to refer to a JAR file which will be downloaded and opened + * as needed. + * + * <p>If there is a security manager, this method first + * calls the security manager's {@code checkCreateClassLoader} method + * to ensure creation of a class loader is allowed. + * + * @param urls the URLs from which to load classes and resources + * + * @exception SecurityException if a security manager exists and its + * {@code checkCreateClassLoader} method doesn't allow + * creation of a class loader. + * @exception NullPointerException if {@code urls} is {@code null}. + * @see SecurityManager#checkCreateClassLoader + */ + public URLClassLoader(URL[] urls) { + super(); + // this is to make the stack depth consistent with 1.1 + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkCreateClassLoader(); + } + this.acc = AccessController.getContext(); + ucp = new URLClassPath(urls, acc); + } + + URLClassLoader(URL[] urls, AccessControlContext acc) { + super(); + // this is to make the stack depth consistent with 1.1 + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkCreateClassLoader(); + } + this.acc = acc; + ucp = new URLClassPath(urls, acc); + } + + /** + * Constructs a new URLClassLoader for the specified URLs, parent + * class loader, and URLStreamHandlerFactory. The parent argument + * will be used as the parent class loader for delegation. The + * factory argument will be used as the stream handler factory to + * obtain protocol handlers when creating new jar URLs. + * + * <p>If there is a security manager, this method first + * calls the security manager's {@code checkCreateClassLoader} method + * to ensure creation of a class loader is allowed. + * + * @param urls the URLs from which to load classes and resources + * @param parent the parent class loader for delegation + * @param factory the URLStreamHandlerFactory to use when creating URLs + * + * @exception SecurityException if a security manager exists and its + * {@code checkCreateClassLoader} method doesn't allow + * creation of a class loader. + * @exception NullPointerException if {@code urls} is {@code null}. + * @see SecurityManager#checkCreateClassLoader + */ + public URLClassLoader(URL[] urls, ClassLoader parent, + URLStreamHandlerFactory factory) { + super(parent); + // this is to make the stack depth consistent with 1.1 + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkCreateClassLoader(); + } + acc = AccessController.getContext(); + ucp = new URLClassPath(urls, factory, acc); + } + + /* A map (used as a set) to keep track of closeable local resources + * (either JarFiles or FileInputStreams). We don't care about + * Http resources since they don't need to be closed. + * + * If the resource is coming from a jar file + * we keep a (weak) reference to the JarFile object which can + * be closed if URLClassLoader.close() called. Due to jar file + * caching there will typically be only one JarFile object + * per underlying jar file. + * + * For file resources, which is probably a less common situation + * we have to keep a weak reference to each stream. + */ + + private WeakHashMap<Closeable,Void> + closeables = new WeakHashMap<>(); + + /** + * Returns an input stream for reading the specified resource. + * If this loader is closed, then any resources opened by this method + * will be closed. + * + * <p> The search order is described in the documentation for {@link + * #getResource(String)}. </p> + * + * @param name + * The resource name + * + * @return An input stream for reading the resource, or {@code null} + * if the resource could not be found + * + * @since 1.7 + */ + public InputStream getResourceAsStream(String name) { + URL url = getResource(name); + try { + if (url == null) { + return null; + } + URLConnection urlc = url.openConnection(); + InputStream is = urlc.getInputStream(); + if (urlc instanceof JarURLConnection) { + JarURLConnection juc = (JarURLConnection)urlc; + JarFile jar = juc.getJarFile(); + synchronized (closeables) { + if (!closeables.containsKey(jar)) { + closeables.put(jar, null); + } + } + } else if (urlc instanceof sun.net.www.protocol.file.FileURLConnection) { + synchronized (closeables) { + closeables.put(is, null); + } + } + return is; + } catch (IOException e) { + return null; + } + } + + /** + * Closes this URLClassLoader, so that it can no longer be used to load + * new classes or resources that are defined by this loader. + * Classes and resources defined by any of this loader's parents in the + * delegation hierarchy are still accessible. Also, any classes or resources + * that are already loaded, are still accessible. + * <p> + * In the case of jar: and file: URLs, it also closes any files + * that were opened by it. If another thread is loading a + * class when the {@code close} method is invoked, then the result of + * that load is undefined. + * <p> + * The method makes a best effort attempt to close all opened files, + * by catching {@link IOException}s internally. Unchecked exceptions + * and errors are not caught. Calling close on an already closed + * loader has no effect. + * <p> + * @exception IOException if closing any file opened by this class loader + * resulted in an IOException. Any such exceptions are caught internally. + * If only one is caught, then it is re-thrown. If more than one exception + * is caught, then the second and following exceptions are added + * as suppressed exceptions of the first one caught, which is then re-thrown. + * + * @exception SecurityException if a security manager is set, and it denies + * {@link RuntimePermission}{@code ("closeClassLoader")} + * + * @since 1.7 + */ + public void close() throws IOException { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkPermission(new RuntimePermission("closeClassLoader")); + } + List<IOException> errors = ucp.closeLoaders(); + + // now close any remaining streams. + + synchronized (closeables) { + Set<Closeable> keys = closeables.keySet(); + for (Closeable c : keys) { + try { + c.close(); + } catch (IOException ioex) { + errors.add(ioex); + } + } + closeables.clear(); + } + + if (errors.isEmpty()) { + return; + } + + IOException firstex = errors.remove(0); + + // Suppress any remaining exceptions + + for (IOException error: errors) { + firstex.addSuppressed(error); + } + throw firstex; + } + + /** + * Appends the specified URL to the list of URLs to search for + * classes and resources. + * <p> + * If the URL specified is {@code null} or is already in the + * list of URLs, or if this loader is closed, then invoking this + * method has no effect. + * + * @param url the URL to be added to the search path of URLs + */ + protected void addURL(URL url) { + ucp.addURL(url); + } + + /** + * Returns the search path of URLs for loading classes and resources. + * This includes the original list of URLs specified to the constructor, + * along with any URLs subsequently appended by the addURL() method. + * @return the search path of URLs for loading classes and resources. + */ + public URL[] getURLs() { + return ucp.getURLs(); + } + + /** + * Finds and loads the class with the specified name from the URL search + * path. Any URLs referring to JAR files are loaded and opened as needed + * until the class is found. + * + * @param name the name of the class + * @return the resulting class + * @exception ClassNotFoundException if the class could not be found, + * or if the loader is closed. + * @exception NullPointerException if {@code name} is {@code null}. + */ + protected Class<?> findClass(final String name) + throws ClassNotFoundException + { + final Class<?> result; + try { + result = AccessController.doPrivileged( + new PrivilegedExceptionAction<Class<?>>() { + public Class<?> run() throws ClassNotFoundException { + String path = name.replace('.', '/').concat(".class"); + Resource res = ucp.getResource(path, false); + if (res != null) { + try { + return defineClass(name, res); + } catch (IOException e) { + throw new ClassNotFoundException(name, e); + } + } else { + return null; + } + } + }, acc); + } catch (java.security.PrivilegedActionException pae) { + throw (ClassNotFoundException) pae.getException(); + } + if (result == null) { + throw new ClassNotFoundException(name); + } + return result; + } + + /* + * Retrieve the package using the specified package name. + * If non-null, verify the package using the specified code + * source and manifest. + */ + private Package getAndVerifyPackage(String pkgname, + Manifest man, URL url) { + Package pkg = getPackage(pkgname); + if (pkg != null) { + // Package found, so check package sealing. + if (pkg.isSealed()) { + // Verify that code source URL is the same. + if (!pkg.isSealed(url)) { + throw new SecurityException( + "sealing violation: package " + pkgname + " is sealed"); + } + } else { + // Make sure we are not attempting to seal the package + // at this code source URL. + if ((man != null) && isSealed(pkgname, man)) { + throw new SecurityException( + "sealing violation: can't seal package " + pkgname + + ": already loaded"); + } + } + } + return pkg; + } + + // Also called by VM to define Package for classes loaded from the CDS + // archive + private void definePackageInternal(String pkgname, Manifest man, URL url) + { + if (getAndVerifyPackage(pkgname, man, url) == null) { + try { + if (man != null) { + definePackage(pkgname, man, url); + } else { + definePackage(pkgname, null, null, null, null, null, null, null); + } + } catch (IllegalArgumentException iae) { + // parallel-capable class loaders: re-verify in case of a + // race condition + if (getAndVerifyPackage(pkgname, man, url) == null) { + // Should never happen + throw new AssertionError("Cannot find package " + + pkgname); + } + } + } + } + + /* + * Defines a Class using the class bytes obtained from the specified + * Resource. The resulting Class must be resolved before it can be + * used. + */ + private Class<?> defineClass(String name, Resource res) throws IOException { + long t0 = System.nanoTime(); + int i = name.lastIndexOf('.'); + URL url = res.getCodeSourceURL(); + if (i != -1) { + String pkgname = name.substring(0, i); + // Check if package already loaded. + Manifest man = res.getManifest(); + definePackageInternal(pkgname, man, url); + } + // Now read the class bytes and define the class + java.nio.ByteBuffer bb = res.getByteBuffer(); + if (bb != null) { + // Use (direct) ByteBuffer: + CodeSigner[] signers = res.getCodeSigners(); + CodeSource cs = new CodeSource(url, signers); + // Android-removed: Android doesn't use sun.misc.PerfCounter. + // sun.misc.PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0); + return defineClass(name, bb, cs); + } else { + byte[] b = res.getBytes(); + // must read certificates AFTER reading bytes. + CodeSigner[] signers = res.getCodeSigners(); + CodeSource cs = new CodeSource(url, signers); + // Android-removed: Android doesn't use sun.misc.PerfCounter. + // sun.misc.PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0); + return defineClass(name, b, 0, b.length, cs); + } + } + + /** + * Defines a new package by name in this ClassLoader. The attributes + * contained in the specified Manifest will be used to obtain package + * version and sealing information. For sealed packages, the additional + * URL specifies the code source URL from which the package was loaded. + * + * @param name the package name + * @param man the Manifest containing package version and sealing + * information + * @param url the code source url for the package, or null if none + * @exception IllegalArgumentException if the package name duplicates + * an existing package either in this class loader or one + * of its ancestors + * @return the newly defined Package object + */ + protected Package definePackage(String name, Manifest man, URL url) + throws IllegalArgumentException + { + String path = name.replace('.', '/').concat("/"); + String specTitle = null, specVersion = null, specVendor = null; + String implTitle = null, implVersion = null, implVendor = null; + String sealed = null; + URL sealBase = null; + + Attributes attr = man.getAttributes(path); + if (attr != null) { + specTitle = attr.getValue(Name.SPECIFICATION_TITLE); + specVersion = attr.getValue(Name.SPECIFICATION_VERSION); + specVendor = attr.getValue(Name.SPECIFICATION_VENDOR); + implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE); + implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION); + implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR); + sealed = attr.getValue(Name.SEALED); + } + attr = man.getMainAttributes(); + if (attr != null) { + if (specTitle == null) { + specTitle = attr.getValue(Name.SPECIFICATION_TITLE); + } + if (specVersion == null) { + specVersion = attr.getValue(Name.SPECIFICATION_VERSION); + } + if (specVendor == null) { + specVendor = attr.getValue(Name.SPECIFICATION_VENDOR); + } + if (implTitle == null) { + implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE); + } + if (implVersion == null) { + implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION); + } + if (implVendor == null) { + implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR); + } + if (sealed == null) { + sealed = attr.getValue(Name.SEALED); + } + } + if ("true".equalsIgnoreCase(sealed)) { + sealBase = url; + } + return definePackage(name, specTitle, specVersion, specVendor, + implTitle, implVersion, implVendor, sealBase); + } + + /* + * Returns true if the specified package name is sealed according to the + * given manifest. + */ + private boolean isSealed(String name, Manifest man) { + String path = name.replace('.', '/').concat("/"); + Attributes attr = man.getAttributes(path); + String sealed = null; + if (attr != null) { + sealed = attr.getValue(Name.SEALED); + } + if (sealed == null) { + if ((attr = man.getMainAttributes()) != null) { + sealed = attr.getValue(Name.SEALED); + } + } + return "true".equalsIgnoreCase(sealed); + } + + /** + * Finds the resource with the specified name on the URL search path. + * + * @param name the name of the resource + * @return a {@code URL} for the resource, or {@code null} + * if the resource could not be found, or if the loader is closed. + */ + public URL findResource(final String name) { + /* + * The same restriction to finding classes applies to resources + */ + URL url = AccessController.doPrivileged( + new PrivilegedAction<URL>() { + public URL run() { + return ucp.findResource(name, true); + } + }, acc); + + return url != null ? ucp.checkURL(url) : null; + } + + /** + * Returns an Enumeration of URLs representing all of the resources + * on the URL search path having the specified name. + * + * @param name the resource name + * @exception IOException if an I/O exception occurs + * @return an {@code Enumeration} of {@code URL}s + * If the loader is closed, the Enumeration will be empty. + */ + public Enumeration<URL> findResources(final String name) + throws IOException + { + final Enumeration<URL> e = ucp.findResources(name, true); + + return new Enumeration<URL>() { + private URL url = null; + + private boolean next() { + if (url != null) { + return true; + } + do { + URL u = AccessController.doPrivileged( + new PrivilegedAction<URL>() { + public URL run() { + if (!e.hasMoreElements()) + return null; + return e.nextElement(); + } + }, acc); + if (u == null) + break; + url = ucp.checkURL(u); + } while (url == null); + return url != null; + } + + public URL nextElement() { + if (!next()) { + throw new NoSuchElementException(); + } + URL u = url; + url = null; + return u; + } + + public boolean hasMoreElements() { + return next(); + } + }; + } + + /** + * Returns the permissions for the given codesource object. + * The implementation of this method first calls super.getPermissions + * and then adds permissions based on the URL of the codesource. + * <p> + * If the protocol of this URL is "jar", then the permission granted + * is based on the permission that is required by the URL of the Jar + * file. + * <p> + * If the protocol is "file" and there is an authority component, then + * permission to connect to and accept connections from that authority + * may be granted. If the protocol is "file" + * and the path specifies a file, then permission to read that + * file is granted. If protocol is "file" and the path is + * a directory, permission is granted to read all files + * and (recursively) all files and subdirectories contained in + * that directory. + * <p> + * If the protocol is not "file", then permission + * to connect to and accept connections from the URL's host is granted. + * @param codesource the codesource + * @exception NullPointerException if {@code codesource} is {@code null}. + * @return the permissions granted to the codesource + */ + protected PermissionCollection getPermissions(CodeSource codesource) + { + PermissionCollection perms = super.getPermissions(codesource); + + URL url = codesource.getLocation(); + + Permission p; + URLConnection urlConnection; + + try { + urlConnection = url.openConnection(); + p = urlConnection.getPermission(); + } catch (java.io.IOException ioe) { + p = null; + urlConnection = null; + } + + if (p instanceof FilePermission) { + // if the permission has a separator char on the end, + // it means the codebase is a directory, and we need + // to add an additional permission to read recursively + String path = p.getName(); + if (path.endsWith(File.separator)) { + path += "-"; + p = new FilePermission(path, SecurityConstants.FILE_READ_ACTION); + } + } else if ((p == null) && (url.getProtocol().equals("file"))) { + String path = url.getFile().replace('/', File.separatorChar); + path = ParseUtil.decode(path); + if (path.endsWith(File.separator)) + path += "-"; + p = new FilePermission(path, SecurityConstants.FILE_READ_ACTION); + } else { + /** + * Not loading from a 'file:' URL so we want to give the class + * permission to connect to and accept from the remote host + * after we've made sure the host is the correct one and is valid. + */ + URL locUrl = url; + if (urlConnection instanceof JarURLConnection) { + locUrl = ((JarURLConnection)urlConnection).getJarFileURL(); + } + String host = locUrl.getHost(); + if (host != null && (host.length() > 0)) + p = new SocketPermission(host, + SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION); + } + + // make sure the person that created this class loader + // would have this permission + + if (p != null) { + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + final Permission fp = p; + AccessController.doPrivileged(new PrivilegedAction<Void>() { + public Void run() throws SecurityException { + sm.checkPermission(fp); + return null; + } + }, acc); + } + perms.add(p); + } + return perms; + } + + /** + * Creates a new instance of URLClassLoader for the specified + * URLs and parent class loader. If a security manager is + * installed, the {@code loadClass} method of the URLClassLoader + * returned by this method will invoke the + * {@code SecurityManager.checkPackageAccess} method before + * loading the class. + * + * @param urls the URLs to search for classes and resources + * @param parent the parent class loader for delegation + * @exception NullPointerException if {@code urls} is {@code null}. + * @return the resulting class loader + */ + public static URLClassLoader newInstance(final URL[] urls, + final ClassLoader parent) { + // Save the caller's context + final AccessControlContext acc = AccessController.getContext(); + // Need a privileged block to create the class loader + URLClassLoader ucl = AccessController.doPrivileged( + new PrivilegedAction<URLClassLoader>() { + public URLClassLoader run() { + return new FactoryURLClassLoader(urls, parent, acc); + } + }); + return ucl; + } + + /** + * Creates a new instance of URLClassLoader for the specified + * URLs and default parent class loader. If a security manager is + * installed, the {@code loadClass} method of the URLClassLoader + * returned by this method will invoke the + * {@code SecurityManager.checkPackageAccess} before + * loading the class. + * + * @param urls the URLs to search for classes and resources + * @exception NullPointerException if {@code urls} is {@code null}. + * @return the resulting class loader + */ + public static URLClassLoader newInstance(final URL[] urls) { + // Save the caller's context + final AccessControlContext acc = AccessController.getContext(); + // Need a privileged block to create the class loader + URLClassLoader ucl = AccessController.doPrivileged( + new PrivilegedAction<URLClassLoader>() { + public URLClassLoader run() { + return new FactoryURLClassLoader(urls, acc); + } + }); + return ucl; + } + + static { + // Android-removed: SharedSecrets.setJavaNetAccess call. Android doesn't use it. + /*sun.misc.SharedSecrets.setJavaNetAccess ( + new sun.misc.JavaNetAccess() { + public URLClassPath getURLClassPath (URLClassLoader u) { + return u.ucp; + } + + public String getOriginalHostName(InetAddress ia) { + return ia.holder.getOriginalHostName(); + } + } + );*/ + ClassLoader.registerAsParallelCapable(); + } +} + +final class FactoryURLClassLoader extends URLClassLoader { + + static { + ClassLoader.registerAsParallelCapable(); + } + + FactoryURLClassLoader(URL[] urls, ClassLoader parent, + AccessControlContext acc) { + super(urls, parent, acc); + } + + FactoryURLClassLoader(URL[] urls, AccessControlContext acc) { + super(urls, acc); + } + + public final Class<?> loadClass(String name, boolean resolve) + throws ClassNotFoundException + { + // First check if we have permission to access the package. This + // should go away once we've added support for exported packages. + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + int i = name.lastIndexOf('.'); + if (i != -1) { + sm.checkPackageAccess(name.substring(0, i)); + } + } + return super.loadClass(name, resolve); + } +}
diff --git a/java/net/URLConnection.java b/java/net/URLConnection.java new file mode 100644 index 0000000..55ec26d --- /dev/null +++ b/java/net/URLConnection.java
@@ -0,0 +1,1820 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 1995, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Hashtable; +import java.util.Date; +import java.util.StringTokenizer; +import java.util.Collections; +import java.util.Map; +import java.util.List; +import java.security.Permission; +import java.security.AccessController; +import sun.security.util.SecurityConstants; +import sun.net.www.MessageHeader; + +/** + * The abstract class {@code URLConnection} is the superclass + * of all classes that represent a communications link between the + * application and a URL. Instances of this class can be used both to + * read from and to write to the resource referenced by the URL. In + * general, creating a connection to a URL is a multistep process: + * + * <center><table border=2 summary="Describes the process of creating a connection to a URL: openConnection() and connect() over time."> + * <tr><th>{@code openConnection()}</th> + * <th>{@code connect()}</th></tr> + * <tr><td>Manipulate parameters that affect the connection to the remote + * resource.</td> + * <td>Interact with the resource; query header fields and + * contents.</td></tr> + * </table> + * ----------------------------> + * <br>time</center> + * + * <ol> + * <li>The connection object is created by invoking the + * {@code openConnection} method on a URL. + * <li>The setup parameters and general request properties are manipulated. + * <li>The actual connection to the remote object is made, using the + * {@code connect} method. + * <li>The remote object becomes available. The header fields and the contents + * of the remote object can be accessed. + * </ol> + * <p> + * The setup parameters are modified using the following methods: + * <ul> + * <li>{@code setAllowUserInteraction} + * <li>{@code setDoInput} + * <li>{@code setDoOutput} + * <li>{@code setIfModifiedSince} + * <li>{@code setUseCaches} + * </ul> + * <p> + * and the general request properties are modified using the method: + * <ul> + * <li>{@code setRequestProperty} + * </ul> + * <p> + * Default values for the {@code AllowUserInteraction} and + * {@code UseCaches} parameters can be set using the methods + * {@code setDefaultAllowUserInteraction} and + * {@code setDefaultUseCaches}. + * <p> + * Each of the above {@code set} methods has a corresponding + * {@code get} method to retrieve the value of the parameter or + * general request property. The specific parameters and general + * request properties that are applicable are protocol specific. + * <p> + * The following methods are used to access the header fields and + * the contents after the connection is made to the remote object: + * <ul> + * <li>{@code getContent} + * <li>{@code getHeaderField} + * <li>{@code getInputStream} + * <li>{@code getOutputStream} + * </ul> + * <p> + * Certain header fields are accessed frequently. The methods: + * <ul> + * <li>{@code getContentEncoding} + * <li>{@code getContentLength} + * <li>{@code getContentType} + * <li>{@code getDate} + * <li>{@code getExpiration} + * <li>{@code getLastModifed} + * </ul> + * <p> + * provide convenient access to these fields. The + * {@code getContentType} method is used by the + * {@code getContent} method to determine the type of the remote + * object; subclasses may find it convenient to override the + * {@code getContentType} method. + * <p> + * In the common case, all of the pre-connection parameters and + * general request properties can be ignored: the pre-connection + * parameters and request properties default to sensible values. For + * most clients of this interface, there are only two interesting + * methods: {@code getInputStream} and {@code getContent}, + * which are mirrored in the {@code URL} class by convenience methods. + * <p> + * More information on the request properties and header fields of + * an {@code http} connection can be found at: + * <blockquote><pre> + * <a href="http://www.ietf.org/rfc/rfc2616.txt">http://www.ietf.org/rfc/rfc2616.txt</a> + * </pre></blockquote> + * + * Invoking the {@code close()} methods on the {@code InputStream} or {@code OutputStream} of an + * {@code URLConnection} after a request may free network resources associated with this + * instance, unless particular protocol specifications specify different behaviours + * for it. + * + * @author James Gosling + * @see java.net.URL#openConnection() + * @see java.net.URLConnection#connect() + * @see java.net.URLConnection#getContent() + * @see java.net.URLConnection#getContentEncoding() + * @see java.net.URLConnection#getContentLength() + * @see java.net.URLConnection#getContentType() + * @see java.net.URLConnection#getDate() + * @see java.net.URLConnection#getExpiration() + * @see java.net.URLConnection#getHeaderField(int) + * @see java.net.URLConnection#getHeaderField(java.lang.String) + * @see java.net.URLConnection#getInputStream() + * @see java.net.URLConnection#getLastModified() + * @see java.net.URLConnection#getOutputStream() + * @see java.net.URLConnection#setAllowUserInteraction(boolean) + * @see java.net.URLConnection#setDefaultUseCaches(boolean) + * @see java.net.URLConnection#setDoInput(boolean) + * @see java.net.URLConnection#setDoOutput(boolean) + * @see java.net.URLConnection#setIfModifiedSince(long) + * @see java.net.URLConnection#setRequestProperty(java.lang.String, java.lang.String) + * @see java.net.URLConnection#setUseCaches(boolean) + * @since JDK1.0 + */ +public abstract class URLConnection { + + /** + * The URL represents the remote object on the World Wide Web to + * which this connection is opened. + * <p> + * The value of this field can be accessed by the + * {@code getURL} method. + * <p> + * The default value of this variable is the value of the URL + * argument in the {@code URLConnection} constructor. + * + * @see java.net.URLConnection#getURL() + * @see java.net.URLConnection#url + */ + protected URL url; + + /** + * This variable is set by the {@code setDoInput} method. Its + * value is returned by the {@code getDoInput} method. + * <p> + * A URL connection can be used for input and/or output. Setting the + * {@code doInput} flag to {@code true} indicates that + * the application intends to read data from the URL connection. + * <p> + * The default value of this field is {@code true}. + * + * @see java.net.URLConnection#getDoInput() + * @see java.net.URLConnection#setDoInput(boolean) + */ + protected boolean doInput = true; + + /** + * This variable is set by the {@code setDoOutput} method. Its + * value is returned by the {@code getDoOutput} method. + * <p> + * A URL connection can be used for input and/or output. Setting the + * {@code doOutput} flag to {@code true} indicates + * that the application intends to write data to the URL connection. + * <p> + * The default value of this field is {@code false}. + * + * @see java.net.URLConnection#getDoOutput() + * @see java.net.URLConnection#setDoOutput(boolean) + */ + protected boolean doOutput = false; + + private static boolean defaultAllowUserInteraction = false; + + /** + * If {@code true}, this {@code URL} is being examined in + * a context in which it makes sense to allow user interactions such + * as popping up an authentication dialog. If {@code false}, + * then no user interaction is allowed. + * <p> + * The value of this field can be set by the + * {@code setAllowUserInteraction} method. + * Its value is returned by the + * {@code getAllowUserInteraction} method. + * Its default value is the value of the argument in the last invocation + * of the {@code setDefaultAllowUserInteraction} method. + * + * @see java.net.URLConnection#getAllowUserInteraction() + * @see java.net.URLConnection#setAllowUserInteraction(boolean) + * @see java.net.URLConnection#setDefaultAllowUserInteraction(boolean) + */ + protected boolean allowUserInteraction = defaultAllowUserInteraction; + + private static boolean defaultUseCaches = true; + + /** + * If {@code true}, the protocol is allowed to use caching + * whenever it can. If {@code false}, the protocol must always + * try to get a fresh copy of the object. + * <p> + * This field is set by the {@code setUseCaches} method. Its + * value is returned by the {@code getUseCaches} method. + * <p> + * Its default value is the value given in the last invocation of the + * {@code setDefaultUseCaches} method. + * + * @see java.net.URLConnection#setUseCaches(boolean) + * @see java.net.URLConnection#getUseCaches() + * @see java.net.URLConnection#setDefaultUseCaches(boolean) + */ + protected boolean useCaches = defaultUseCaches; + + /** + * Some protocols support skipping the fetching of the object unless + * the object has been modified more recently than a certain time. + * <p> + * A nonzero value gives a time as the number of milliseconds since + * January 1, 1970, GMT. The object is fetched only if it has been + * modified more recently than that time. + * <p> + * This variable is set by the {@code setIfModifiedSince} + * method. Its value is returned by the + * {@code getIfModifiedSince} method. + * <p> + * The default value of this field is {@code 0}, indicating + * that the fetching must always occur. + * + * @see java.net.URLConnection#getIfModifiedSince() + * @see java.net.URLConnection#setIfModifiedSince(long) + */ + protected long ifModifiedSince = 0; + + /** + * If {@code false}, this connection object has not created a + * communications link to the specified URL. If {@code true}, + * the communications link has been established. + */ + protected boolean connected = false; + + /** + * @since 1.5 + */ + private int connectTimeout; + private int readTimeout; + + /** + * @since 1.6 + */ + private MessageHeader requests; + + /** + * @since JDK1.1 + */ + private static FileNameMap fileNameMap; + + // BEGIN Android-changed: Android has its own mime table. + /* + /** + * @since 1.2.2 + * + private static boolean fileNameMapLoaded = false; + + /** + * Loads filename map (a mimetable) from a data file. It will + * first try to load the user-specific table, defined + * by "content.types.user.table" property. If that fails, + * it tries to load the default built-in table. + * + * @return the FileNameMap + * @since 1.2 + * @see #setFileNameMap(java.net.FileNameMap) + * + public static synchronized FileNameMap getFileNameMap() { + if ((fileNameMap == null) && !fileNameMapLoaded) { + fileNameMap = sun.net.www.MimeTable.loadTable(); + fileNameMapLoaded = true; + } + + return new FileNameMap() { + private FileNameMap map = fileNameMap; + public String getContentTypeFor(String fileName) { + return map.getContentTypeFor(fileName); + } + }; + } + */ + /** + * Returns a {@link FileNameMap} implementation suitable for guessing a + * content type based on a URL's "file" component. + * + * @see #guessContentTypeFromName(String) + * @see #setFileNameMap(java.net.FileNameMap) + * + */ + public static synchronized FileNameMap getFileNameMap() { + if (fileNameMap == null) { + fileNameMap = new DefaultFileNameMap(); + } + return fileNameMap; + } + // END Android-changed: Android has its own mime table. + + /** + * Sets the FileNameMap. + * <p> + * If there is a security manager, this method first calls + * the security manager's {@code checkSetFactory} method + * to ensure the operation is allowed. + * This could result in a SecurityException. + * + * @param map the FileNameMap to be set + * @exception SecurityException if a security manager exists and its + * {@code checkSetFactory} method doesn't allow the operation. + * @see SecurityManager#checkSetFactory + * @see #getFileNameMap() + * @since 1.2 + */ + public static void setFileNameMap(FileNameMap map) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) sm.checkSetFactory(); + fileNameMap = map; + } + + /** + * Opens a communications link to the resource referenced by this + * URL, if such a connection has not already been established. + * <p> + * If the {@code connect} method is called when the connection + * has already been opened (indicated by the {@code connected} + * field having the value {@code true}), the call is ignored. + * <p> + * URLConnection objects go through two phases: first they are + * created, then they are connected. After being created, and + * before being connected, various options can be specified + * (e.g., doInput and UseCaches). After connecting, it is an + * error to try to set them. Operations that depend on being + * connected, like getContentLength, will implicitly perform the + * connection, if necessary. + * + * @throws SocketTimeoutException if the timeout expires before + * the connection can be established + * @exception IOException if an I/O error occurs while opening the + * connection. + * @see java.net.URLConnection#connected + * @see #getConnectTimeout() + * @see #setConnectTimeout(int) + */ + abstract public void connect() throws IOException; + + // Android-changed: Add javadoc to specify Android's timeout behavior. + /** + * Sets a specified timeout value, in milliseconds, to be used + * when opening a communications link to the resource referenced + * by this URLConnection. If the timeout expires before the + * connection can be established, a + * java.net.SocketTimeoutException is raised. A timeout of zero is + * interpreted as an infinite timeout. + + * <p> Some non-standard implementation of this method may ignore + * the specified timeout. To see the connect timeout set, please + * call getConnectTimeout(). + * + * <p><strong>Warning</strong>: If the hostname resolves to multiple IP + * addresses, Android's default implementation of {@link HttpURLConnection} + * will try each in + * <a href="http://www.ietf.org/rfc/rfc3484.txt">RFC 3484</a> order. If + * connecting to each of these addresses fails, multiple timeouts will + * elapse before the connect attempt throws an exception. Host names + * that support both IPv6 and IPv4 always have at least 2 IP addresses. + * + * @param timeout an {@code int} that specifies the connect + * timeout value in milliseconds + * @throws IllegalArgumentException if the timeout parameter is negative + * + * @see #getConnectTimeout() + * @see #connect() + * @since 1.5 + */ + public void setConnectTimeout(int timeout) { + if (timeout < 0) { + throw new IllegalArgumentException("timeout can not be negative"); + } + connectTimeout = timeout; + } + + /** + * Returns setting for connect timeout. + * <p> + * 0 return implies that the option is disabled + * (i.e., timeout of infinity). + * + * @return an {@code int} that indicates the connect timeout + * value in milliseconds + * @see #setConnectTimeout(int) + * @see #connect() + * @since 1.5 + */ + public int getConnectTimeout() { + return connectTimeout; + } + + /** + * Sets the read timeout to a specified timeout, in + * milliseconds. A non-zero value specifies the timeout when + * reading from Input stream when a connection is established to a + * resource. If the timeout expires before there is data available + * for read, a java.net.SocketTimeoutException is raised. A + * timeout of zero is interpreted as an infinite timeout. + * + *<p> Some non-standard implementation of this method ignores the + * specified timeout. To see the read timeout set, please call + * getReadTimeout(). + * + * @param timeout an {@code int} that specifies the timeout + * value to be used in milliseconds + * @throws IllegalArgumentException if the timeout parameter is negative + * + * @see #getReadTimeout() + * @see InputStream#read() + * @since 1.5 + */ + public void setReadTimeout(int timeout) { + if (timeout < 0) { + throw new IllegalArgumentException("timeout can not be negative"); + } + readTimeout = timeout; + } + + /** + * Returns setting for read timeout. 0 return implies that the + * option is disabled (i.e., timeout of infinity). + * + * @return an {@code int} that indicates the read timeout + * value in milliseconds + * + * @see #setReadTimeout(int) + * @see InputStream#read() + * @since 1.5 + */ + public int getReadTimeout() { + return readTimeout; + } + + /** + * Constructs a URL connection to the specified URL. A connection to + * the object referenced by the URL is not created. + * + * @param url the specified URL. + */ + protected URLConnection(URL url) { + this.url = url; + } + + /** + * Returns the value of this {@code URLConnection}'s {@code URL} + * field. + * + * @return the value of this {@code URLConnection}'s {@code URL} + * field. + * @see java.net.URLConnection#url + */ + public URL getURL() { + return url; + } + + /** + * Returns the value of the {@code content-length} header field. + * <P> + * <B>Note</B>: {@link #getContentLengthLong() getContentLengthLong()} + * should be preferred over this method, since it returns a {@code long} + * instead and is therefore more portable.</P> + * + * @return the content length of the resource that this connection's URL + * references, {@code -1} if the content length is not known, + * or if the content length is greater than Integer.MAX_VALUE. + */ + public int getContentLength() { + long l = getContentLengthLong(); + if (l > Integer.MAX_VALUE) + return -1; + return (int) l; + } + + /** + * Returns the value of the {@code content-length} header field as a + * long. + * + * @return the content length of the resource that this connection's URL + * references, or {@code -1} if the content length is + * not known. + * @since 7.0 + */ + public long getContentLengthLong() { + return getHeaderFieldLong("content-length", -1); + } + + /** + * Returns the value of the {@code content-type} header field. + * + * @return the content type of the resource that the URL references, + * or {@code null} if not known. + * @see java.net.URLConnection#getHeaderField(java.lang.String) + */ + public String getContentType() { + return getHeaderField("content-type"); + } + + /** + * Returns the value of the {@code content-encoding} header field. + * + * @return the content encoding of the resource that the URL references, + * or {@code null} if not known. + * @see java.net.URLConnection#getHeaderField(java.lang.String) + */ + public String getContentEncoding() { + return getHeaderField("content-encoding"); + } + + /** + * Returns the value of the {@code expires} header field. + * + * @return the expiration date of the resource that this URL references, + * or 0 if not known. The value is the number of milliseconds since + * January 1, 1970 GMT. + * @see java.net.URLConnection#getHeaderField(java.lang.String) + */ + public long getExpiration() { + return getHeaderFieldDate("expires", 0); + } + + /** + * Returns the value of the {@code date} header field. + * + * @return the sending date of the resource that the URL references, + * or {@code 0} if not known. The value returned is the + * number of milliseconds since January 1, 1970 GMT. + * @see java.net.URLConnection#getHeaderField(java.lang.String) + */ + public long getDate() { + return getHeaderFieldDate("date", 0); + } + + /** + * Returns the value of the {@code last-modified} header field. + * The result is the number of milliseconds since January 1, 1970 GMT. + * + * @return the date the resource referenced by this + * {@code URLConnection} was last modified, or 0 if not known. + * @see java.net.URLConnection#getHeaderField(java.lang.String) + */ + public long getLastModified() { + return getHeaderFieldDate("last-modified", 0); + } + + /** + * Returns the value of the named header field. + * <p> + * If called on a connection that sets the same header multiple times + * with possibly different values, only the last value is returned. + * + * + * @param name the name of a header field. + * @return the value of the named header field, or {@code null} + * if there is no such field in the header. + */ + public String getHeaderField(String name) { + return null; + } + + /** + * Returns an unmodifiable Map of the header fields. + * The Map keys are Strings that represent the + * response-header field names. Each Map value is an + * unmodifiable List of Strings that represents + * the corresponding field values. + * + * @return a Map of header fields + * @since 1.4 + */ + public Map<String,List<String>> getHeaderFields() { + return Collections.emptyMap(); + } + + /** + * Returns the value of the named field parsed as a number. + * <p> + * This form of {@code getHeaderField} exists because some + * connection types (e.g., {@code http-ng}) have pre-parsed + * headers. Classes for that connection type can override this method + * and short-circuit the parsing. + * + * @param name the name of the header field. + * @param Default the default value. + * @return the value of the named field, parsed as an integer. The + * {@code Default} value is returned if the field is + * missing or malformed. + */ + public int getHeaderFieldInt(String name, int Default) { + String value = getHeaderField(name); + try { + return Integer.parseInt(value); + } catch (Exception e) { } + return Default; + } + + /** + * Returns the value of the named field parsed as a number. + * <p> + * This form of {@code getHeaderField} exists because some + * connection types (e.g., {@code http-ng}) have pre-parsed + * headers. Classes for that connection type can override this method + * and short-circuit the parsing. + * + * @param name the name of the header field. + * @param Default the default value. + * @return the value of the named field, parsed as a long. The + * {@code Default} value is returned if the field is + * missing or malformed. + * @since 7.0 + */ + public long getHeaderFieldLong(String name, long Default) { + String value = getHeaderField(name); + try { + return Long.parseLong(value); + } catch (Exception e) { } + return Default; + } + + /** + * Returns the value of the named field parsed as date. + * The result is the number of milliseconds since January 1, 1970 GMT + * represented by the named field. + * <p> + * This form of {@code getHeaderField} exists because some + * connection types (e.g., {@code http-ng}) have pre-parsed + * headers. Classes for that connection type can override this method + * and short-circuit the parsing. + * + * @param name the name of the header field. + * @param Default a default value. + * @return the value of the field, parsed as a date. The value of the + * {@code Default} argument is returned if the field is + * missing or malformed. + */ + @SuppressWarnings("deprecation") + public long getHeaderFieldDate(String name, long Default) { + String value = getHeaderField(name); + try { + return Date.parse(value); + } catch (Exception e) { } + return Default; + } + + /** + * Returns the key for the {@code n}<sup>th</sup> header field. + * It returns {@code null} if there are fewer than {@code n+1} fields. + * + * @param n an index, where {@code n>=0} + * @return the key for the {@code n}<sup>th</sup> header field, + * or {@code null} if there are fewer than {@code n+1} + * fields. + */ + public String getHeaderFieldKey(int n) { + return null; + } + + /** + * Returns the value for the {@code n}<sup>th</sup> header field. + * It returns {@code null} if there are fewer than + * {@code n+1}fields. + * <p> + * This method can be used in conjunction with the + * {@link #getHeaderFieldKey(int) getHeaderFieldKey} method to iterate through all + * the headers in the message. + * + * @param n an index, where {@code n>=0} + * @return the value of the {@code n}<sup>th</sup> header field + * or {@code null} if there are fewer than {@code n+1} fields + * @see java.net.URLConnection#getHeaderFieldKey(int) + */ + public String getHeaderField(int n) { + return null; + } + + /** + * Retrieves the contents of this URL connection. + * <p> + * This method first determines the content type of the object by + * calling the {@code getContentType} method. If this is + * the first time that the application has seen that specific content + * type, a content handler for that content type is created: + * <ol> + * <li>If the application has set up a content handler factory instance + * using the {@code setContentHandlerFactory} method, the + * {@code createContentHandler} method of that instance is called + * with the content type as an argument; the result is a content + * handler for that content type. + * <li>If no content handler factory has yet been set up, or if the + * factory's {@code createContentHandler} method returns + * {@code null}, then the application loads the class named: + * <blockquote><pre> + * sun.net.www.content.<<i>contentType</i>> + * </pre></blockquote> + * where <<i>contentType</i>> is formed by taking the + * content-type string, replacing all slash characters with a + * {@code period} ('.'), and all other non-alphanumeric characters + * with the underscore character '{@code _}'. The alphanumeric + * characters are specifically the 26 uppercase ASCII letters + * '{@code A}' through '{@code Z}', the 26 lowercase ASCII + * letters '{@code a}' through '{@code z}', and the 10 ASCII + * digits '{@code 0}' through '{@code 9}'. If the specified + * class does not exist, or is not a subclass of + * {@code ContentHandler}, then an + * {@code UnknownServiceException} is thrown. + * </ol> + * + * @return the object fetched. The {@code instanceof} operator + * should be used to determine the specific kind of object + * returned. + * @exception IOException if an I/O error occurs while + * getting the content. + * @exception UnknownServiceException if the protocol does not support + * the content type. + * @see java.net.ContentHandlerFactory#createContentHandler(java.lang.String) + * @see java.net.URLConnection#getContentType() + * @see java.net.URLConnection#setContentHandlerFactory(java.net.ContentHandlerFactory) + */ + public Object getContent() throws IOException { + // Must call getInputStream before GetHeaderField gets called + // so that FileNotFoundException has a chance to be thrown up + // from here without being caught. + getInputStream(); + return getContentHandler().getContent(this); + } + + /** + * Retrieves the contents of this URL connection. + * + * @param classes the {@code Class} array + * indicating the requested types + * @return the object fetched that is the first match of the type + * specified in the classes array. null if none of + * the requested types are supported. + * The {@code instanceof} operator should be used to + * determine the specific kind of object returned. + * @exception IOException if an I/O error occurs while + * getting the content. + * @exception UnknownServiceException if the protocol does not support + * the content type. + * @see java.net.URLConnection#getContent() + * @see java.net.ContentHandlerFactory#createContentHandler(java.lang.String) + * @see java.net.URLConnection#getContent(java.lang.Class[]) + * @see java.net.URLConnection#setContentHandlerFactory(java.net.ContentHandlerFactory) + * @since 1.3 + */ + public Object getContent(Class[] classes) throws IOException { + // Must call getInputStream before GetHeaderField gets called + // so that FileNotFoundException has a chance to be thrown up + // from here without being caught. + getInputStream(); + return getContentHandler().getContent(this, classes); + } + + /** + * Returns a permission object representing the permission + * necessary to make the connection represented by this + * object. This method returns null if no permission is + * required to make the connection. By default, this method + * returns {@code java.security.AllPermission}. Subclasses + * should override this method and return the permission + * that best represents the permission required to make a + * a connection to the URL. For example, a {@code URLConnection} + * representing a {@code file:} URL would return a + * {@code java.io.FilePermission} object. + * + * <p>The permission returned may dependent upon the state of the + * connection. For example, the permission before connecting may be + * different from that after connecting. For example, an HTTP + * sever, say foo.com, may redirect the connection to a different + * host, say bar.com. Before connecting the permission returned by + * the connection will represent the permission needed to connect + * to foo.com, while the permission returned after connecting will + * be to bar.com. + * + * <p>Permissions are generally used for two purposes: to protect + * caches of objects obtained through URLConnections, and to check + * the right of a recipient to learn about a particular URL. In + * the first case, the permission should be obtained + * <em>after</em> the object has been obtained. For example, in an + * HTTP connection, this will represent the permission to connect + * to the host from which the data was ultimately fetched. In the + * second case, the permission should be obtained and tested + * <em>before</em> connecting. + * + * @return the permission object representing the permission + * necessary to make the connection represented by this + * URLConnection. + * + * @exception IOException if the computation of the permission + * requires network or file I/O and an exception occurs while + * computing it. + */ + public Permission getPermission() throws IOException { + return SecurityConstants.ALL_PERMISSION; + } + + /** + * Returns an input stream that reads from this open connection. + * + * A SocketTimeoutException can be thrown when reading from the + * returned input stream if the read timeout expires before data + * is available for read. + * + * @return an input stream that reads from this open connection. + * @exception IOException if an I/O error occurs while + * creating the input stream. + * @exception UnknownServiceException if the protocol does not support + * input. + * @see #setReadTimeout(int) + * @see #getReadTimeout() + */ + public InputStream getInputStream() throws IOException { + throw new UnknownServiceException("protocol doesn't support input"); + } + + /** + * Returns an output stream that writes to this connection. + * + * @return an output stream that writes to this connection. + * @exception IOException if an I/O error occurs while + * creating the output stream. + * @exception UnknownServiceException if the protocol does not support + * output. + */ + public OutputStream getOutputStream() throws IOException { + throw new UnknownServiceException("protocol doesn't support output"); + } + + /** + * Returns a {@code String} representation of this URL connection. + * + * @return a string representation of this {@code URLConnection}. + */ + public String toString() { + return this.getClass().getName() + ":" + url; + } + + /** + * Sets the value of the {@code doInput} field for this + * {@code URLConnection} to the specified value. + * <p> + * A URL connection can be used for input and/or output. Set the DoInput + * flag to true if you intend to use the URL connection for input, + * false if not. The default is true. + * + * @param doinput the new value. + * @throws IllegalStateException if already connected + * @see java.net.URLConnection#doInput + * @see #getDoInput() + */ + public void setDoInput(boolean doinput) { + if (connected) + throw new IllegalStateException("Already connected"); + doInput = doinput; + } + + /** + * Returns the value of this {@code URLConnection}'s + * {@code doInput} flag. + * + * @return the value of this {@code URLConnection}'s + * {@code doInput} flag. + * @see #setDoInput(boolean) + */ + public boolean getDoInput() { + return doInput; + } + + /** + * Sets the value of the {@code doOutput} field for this + * {@code URLConnection} to the specified value. + * <p> + * A URL connection can be used for input and/or output. Set the DoOutput + * flag to true if you intend to use the URL connection for output, + * false if not. The default is false. + * + * @param dooutput the new value. + * @throws IllegalStateException if already connected + * @see #getDoOutput() + */ + public void setDoOutput(boolean dooutput) { + if (connected) + throw new IllegalStateException("Already connected"); + doOutput = dooutput; + } + + /** + * Returns the value of this {@code URLConnection}'s + * {@code doOutput} flag. + * + * @return the value of this {@code URLConnection}'s + * {@code doOutput} flag. + * @see #setDoOutput(boolean) + */ + public boolean getDoOutput() { + return doOutput; + } + + /** + * Set the value of the {@code allowUserInteraction} field of + * this {@code URLConnection}. + * + * @param allowuserinteraction the new value. + * @throws IllegalStateException if already connected + * @see #getAllowUserInteraction() + */ + public void setAllowUserInteraction(boolean allowuserinteraction) { + if (connected) + throw new IllegalStateException("Already connected"); + allowUserInteraction = allowuserinteraction; + } + + /** + * Returns the value of the {@code allowUserInteraction} field for + * this object. + * + * @return the value of the {@code allowUserInteraction} field for + * this object. + * @see #setAllowUserInteraction(boolean) + */ + public boolean getAllowUserInteraction() { + return allowUserInteraction; + } + + /** + * Sets the default value of the + * {@code allowUserInteraction} field for all future + * {@code URLConnection} objects to the specified value. + * + * @param defaultallowuserinteraction the new value. + * @see #getDefaultAllowUserInteraction() + */ + public static void setDefaultAllowUserInteraction(boolean defaultallowuserinteraction) { + defaultAllowUserInteraction = defaultallowuserinteraction; + } + + /** + * Returns the default value of the {@code allowUserInteraction} + * field. + * <p> + * Ths default is "sticky", being a part of the static state of all + * URLConnections. This flag applies to the next, and all following + * URLConnections that are created. + * + * @return the default value of the {@code allowUserInteraction} + * field. + * @see #setDefaultAllowUserInteraction(boolean) + */ + public static boolean getDefaultAllowUserInteraction() { + return defaultAllowUserInteraction; + } + + /** + * Sets the value of the {@code useCaches} field of this + * {@code URLConnection} to the specified value. + * <p> + * Some protocols do caching of documents. Occasionally, it is important + * to be able to "tunnel through" and ignore the caches (e.g., the + * "reload" button in a browser). If the UseCaches flag on a connection + * is true, the connection is allowed to use whatever caches it can. + * If false, caches are to be ignored. + * The default value comes from DefaultUseCaches, which defaults to + * true. + * + * @param usecaches a {@code boolean} indicating whether + * or not to allow caching + * @throws IllegalStateException if already connected + * @see #getUseCaches() + */ + public void setUseCaches(boolean usecaches) { + if (connected) + throw new IllegalStateException("Already connected"); + useCaches = usecaches; + } + + /** + * Returns the value of this {@code URLConnection}'s + * {@code useCaches} field. + * + * @return the value of this {@code URLConnection}'s + * {@code useCaches} field. + * @see #setUseCaches(boolean) + */ + public boolean getUseCaches() { + return useCaches; + } + + /** + * Sets the value of the {@code ifModifiedSince} field of + * this {@code URLConnection} to the specified value. + * + * @param ifmodifiedsince the new value. + * @throws IllegalStateException if already connected + * @see #getIfModifiedSince() + */ + public void setIfModifiedSince(long ifmodifiedsince) { + if (connected) + throw new IllegalStateException("Already connected"); + ifModifiedSince = ifmodifiedsince; + } + + /** + * Returns the value of this object's {@code ifModifiedSince} field. + * + * @return the value of this object's {@code ifModifiedSince} field. + * @see #setIfModifiedSince(long) + */ + public long getIfModifiedSince() { + return ifModifiedSince; + } + + /** + * Returns the default value of a {@code URLConnection}'s + * {@code useCaches} flag. + * <p> + * Ths default is "sticky", being a part of the static state of all + * URLConnections. This flag applies to the next, and all following + * URLConnections that are created. + * + * @return the default value of a {@code URLConnection}'s + * {@code useCaches} flag. + * @see #setDefaultUseCaches(boolean) + */ + public boolean getDefaultUseCaches() { + return defaultUseCaches; + } + + /** + * Sets the default value of the {@code useCaches} field to the + * specified value. + * + * @param defaultusecaches the new value. + * @see #getDefaultUseCaches() + */ + public void setDefaultUseCaches(boolean defaultusecaches) { + defaultUseCaches = defaultusecaches; + } + + /** + * Sets the general request property. If a property with the key already + * exists, overwrite its value with the new value. + * + * <p> NOTE: HTTP requires all request properties which can + * legally have multiple instances with the same key + * to use a comma-separated list syntax which enables multiple + * properties to be appended into a single property. + * + * @param key the keyword by which the request is known + * (e.g., "{@code Accept}"). + * @param value the value associated with it. + * @throws IllegalStateException if already connected + * @throws NullPointerException if key is <CODE>null</CODE> + * @see #getRequestProperty(java.lang.String) + */ + public void setRequestProperty(String key, String value) { + if (connected) + throw new IllegalStateException("Already connected"); + if (key == null) + throw new NullPointerException ("key is null"); + + if (requests == null) + requests = new MessageHeader(); + + requests.set(key, value); + } + + /** + * Adds a general request property specified by a + * key-value pair. This method will not overwrite + * existing values associated with the same key. + * + * @param key the keyword by which the request is known + * (e.g., "{@code Accept}"). + * @param value the value associated with it. + * @throws IllegalStateException if already connected + * @throws NullPointerException if key is null + * @see #getRequestProperties() + * @since 1.4 + */ + public void addRequestProperty(String key, String value) { + if (connected) + throw new IllegalStateException("Already connected"); + if (key == null) + throw new NullPointerException ("key is null"); + + if (requests == null) + requests = new MessageHeader(); + + requests.add(key, value); + } + + + /** + * Returns the value of the named general request property for this + * connection. + * + * @param key the keyword by which the request is known (e.g., "Accept"). + * @return the value of the named general request property for this + * connection. If key is null, then null is returned. + * @throws IllegalStateException if already connected + * @see #setRequestProperty(java.lang.String, java.lang.String) + */ + public String getRequestProperty(String key) { + if (connected) + throw new IllegalStateException("Already connected"); + + if (requests == null) + return null; + + return requests.findValue(key); + } + + /** + * Returns an unmodifiable Map of general request + * properties for this connection. The Map keys + * are Strings that represent the request-header + * field names. Each Map value is a unmodifiable List + * of Strings that represents the corresponding + * field values. + * + * @return a Map of the general request properties for this connection. + * @throws IllegalStateException if already connected + * @since 1.4 + */ + public Map<String,List<String>> getRequestProperties() { + if (connected) + throw new IllegalStateException("Already connected"); + + if (requests == null) + return Collections.emptyMap(); + + return requests.getHeaders(null); + } + + /** + * Sets the default value of a general request property. When a + * {@code URLConnection} is created, it is initialized with + * these properties. + * + * @param key the keyword by which the request is known + * (e.g., "{@code Accept}"). + * @param value the value associated with the key. + * + * @see java.net.URLConnection#setRequestProperty(java.lang.String,java.lang.String) + * + * @deprecated The instance specific setRequestProperty method + * should be used after an appropriate instance of URLConnection + * is obtained. Invoking this method will have no effect. + * + * @see #getDefaultRequestProperty(java.lang.String) + */ + @Deprecated + public static void setDefaultRequestProperty(String key, String value) { + } + + /** + * Returns the value of the default request property. Default request + * properties are set for every connection. + * + * @param key the keyword by which the request is known (e.g., "Accept"). + * @return the value of the default request property + * for the specified key. + * + * @see java.net.URLConnection#getRequestProperty(java.lang.String) + * + * @deprecated The instance specific getRequestProperty method + * should be used after an appropriate instance of URLConnection + * is obtained. + * + * @see #setDefaultRequestProperty(java.lang.String, java.lang.String) + */ + @Deprecated + public static String getDefaultRequestProperty(String key) { + return null; + } + + /** + * The ContentHandler factory. + */ + static ContentHandlerFactory factory; + + /** + * Sets the {@code ContentHandlerFactory} of an + * application. It can be called at most once by an application. + * <p> + * The {@code ContentHandlerFactory} instance is used to + * construct a content handler from a content type + * <p> + * If there is a security manager, this method first calls + * the security manager's {@code checkSetFactory} method + * to ensure the operation is allowed. + * This could result in a SecurityException. + * + * @param fac the desired factory. + * @exception Error if the factory has already been defined. + * @exception SecurityException if a security manager exists and its + * {@code checkSetFactory} method doesn't allow the operation. + * @see java.net.ContentHandlerFactory + * @see java.net.URLConnection#getContent() + * @see SecurityManager#checkSetFactory + */ + public static synchronized void setContentHandlerFactory(ContentHandlerFactory fac) { + if (factory != null) { + throw new Error("factory already defined"); + } + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkSetFactory(); + } + factory = fac; + } + + private static Hashtable<String, ContentHandler> handlers = new Hashtable<>(); + + /** + * Gets the Content Handler appropriate for this connection. + */ + synchronized ContentHandler getContentHandler() + throws IOException + { + String contentType = stripOffParameters(getContentType()); + ContentHandler handler = null; + // BEGIN Android-changed: App Compat. Android guesses content type from name and stream. + if (contentType == null) { + if ((contentType = guessContentTypeFromName(url.getFile())) == null) { + contentType = guessContentTypeFromStream(getInputStream()); + } + } + + if (contentType == null) { + return UnknownContentHandler.INSTANCE; + } + // END Android-changed: App Compat. Android guesses content type from name and stream. + try { + handler = handlers.get(contentType); + if (handler != null) + return handler; + } catch(Exception e) { + } + + if (factory != null) + handler = factory.createContentHandler(contentType); + if (handler == null) { + try { + handler = lookupContentHandlerClassFor(contentType); + } catch(Exception e) { + e.printStackTrace(); + handler = UnknownContentHandler.INSTANCE; + } + handlers.put(contentType, handler); + } + return handler; + } + + /* + * Media types are in the format: type/subtype*(; parameter). + * For looking up the content handler, we should ignore those + * parameters. + */ + private String stripOffParameters(String contentType) + { + if (contentType == null) + return null; + int index = contentType.indexOf(';'); + + if (index > 0) + return contentType.substring(0, index); + else + return contentType; + } + + private static final String contentClassPrefix = "sun.net.www.content"; + private static final String contentPathProp = "java.content.handler.pkgs"; + + /** + * Looks for a content handler in a user-defineable set of places. + * By default it looks in sun.net.www.content, but users can define a + * vertical-bar delimited set of class prefixes to search through in + * addition by defining the java.content.handler.pkgs property. + * The class name must be of the form: + * <pre> + * {package-prefix}.{major}.{minor} + * e.g. + * YoyoDyne.experimental.text.plain + * </pre> + */ + private ContentHandler lookupContentHandlerClassFor(String contentType) + throws InstantiationException, IllegalAccessException, ClassNotFoundException { + String contentHandlerClassName = typeToPackageName(contentType); + + String contentHandlerPkgPrefixes =getContentHandlerPkgPrefixes(); + + StringTokenizer packagePrefixIter = + new StringTokenizer(contentHandlerPkgPrefixes, "|"); + + while (packagePrefixIter.hasMoreTokens()) { + String packagePrefix = packagePrefixIter.nextToken().trim(); + + try { + String clsName = packagePrefix + "." + contentHandlerClassName; + Class<?> cls = null; + try { + cls = Class.forName(clsName); + } catch (ClassNotFoundException e) { + ClassLoader cl = ClassLoader.getSystemClassLoader(); + if (cl != null) { + cls = cl.loadClass(clsName); + } + } + if (cls != null) { + ContentHandler handler = + (ContentHandler)cls.newInstance(); + return handler; + } + } catch(Exception e) { + } + } + + return UnknownContentHandler.INSTANCE; + } + + /** + * Utility function to map a MIME content type into an equivalent + * pair of class name components. For example: "text/html" would + * be returned as "text.html" + */ + private String typeToPackageName(String contentType) { + // make sure we canonicalize the class name: all lower case + contentType = contentType.toLowerCase(); + int len = contentType.length(); + char nm[] = new char[len]; + contentType.getChars(0, len, nm, 0); + for (int i = 0; i < len; i++) { + char c = nm[i]; + if (c == '/') { + nm[i] = '.'; + } else if (!('A' <= c && c <= 'Z' || + 'a' <= c && c <= 'z' || + '0' <= c && c <= '9')) { + nm[i] = '_'; + } + } + return new String(nm); + } + + + /** + * Returns a vertical bar separated list of package prefixes for potential + * content handlers. Tries to get the java.content.handler.pkgs property + * to use as a set of package prefixes to search. Whether or not + * that property has been defined, the sun.net.www.content is always + * the last one on the returned package list. + */ + private String getContentHandlerPkgPrefixes() { + String packagePrefixList = AccessController.doPrivileged( + new sun.security.action.GetPropertyAction(contentPathProp, "")); + + if (packagePrefixList != "") { + packagePrefixList += "|"; + } + + return packagePrefixList + contentClassPrefix; + } + + /** + * Tries to determine the content type of an object, based + * on the specified "file" component of a URL. + * This is a convenience method that can be used by + * subclasses that override the {@code getContentType} method. + * + * @param fname a filename. + * @return a guess as to what the content type of the object is, + * based upon its file name. + * @see java.net.URLConnection#getContentType() + */ + public static String guessContentTypeFromName(String fname) { + return getFileNameMap().getContentTypeFor(fname); + } + + /** + * Tries to determine the type of an input stream based on the + * characters at the beginning of the input stream. This method can + * be used by subclasses that override the + * {@code getContentType} method. + * <p> + * Ideally, this routine would not be needed. But many + * {@code http} servers return the incorrect content type; in + * addition, there are many nonstandard extensions. Direct inspection + * of the bytes to determine the content type is often more accurate + * than believing the content type claimed by the {@code http} server. + * + * @param is an input stream that supports marks. + * @return a guess at the content type, or {@code null} if none + * can be determined. + * @exception IOException if an I/O error occurs while reading the + * input stream. + * @see java.io.InputStream#mark(int) + * @see java.io.InputStream#markSupported() + * @see java.net.URLConnection#getContentType() + */ + static public String guessContentTypeFromStream(InputStream is) + throws IOException { + // If we can't read ahead safely, just give up on guessing + if (!is.markSupported()) + return null; + + is.mark(16); + int c1 = is.read(); + int c2 = is.read(); + int c3 = is.read(); + int c4 = is.read(); + int c5 = is.read(); + int c6 = is.read(); + int c7 = is.read(); + int c8 = is.read(); + int c9 = is.read(); + int c10 = is.read(); + int c11 = is.read(); + int c12 = is.read(); + int c13 = is.read(); + int c14 = is.read(); + int c15 = is.read(); + int c16 = is.read(); + is.reset(); + + if (c1 == 0xCA && c2 == 0xFE && c3 == 0xBA && c4 == 0xBE) { + return "application/java-vm"; + } + + if (c1 == 0xAC && c2 == 0xED) { + // next two bytes are version number, currently 0x00 0x05 + return "application/x-java-serialized-object"; + } + + if (c1 == '<') { + if (c2 == '!' + || ((c2 == 'h' && (c3 == 't' && c4 == 'm' && c5 == 'l' || + c3 == 'e' && c4 == 'a' && c5 == 'd') || + (c2 == 'b' && c3 == 'o' && c4 == 'd' && c5 == 'y'))) || + ((c2 == 'H' && (c3 == 'T' && c4 == 'M' && c5 == 'L' || + c3 == 'E' && c4 == 'A' && c5 == 'D') || + (c2 == 'B' && c3 == 'O' && c4 == 'D' && c5 == 'Y')))) { + return "text/html"; + } + + if (c2 == '?' && c3 == 'x' && c4 == 'm' && c5 == 'l' && c6 == ' ') { + return "application/xml"; + } + } + + // big and little (identical) endian UTF-8 encodings, with BOM + if (c1 == 0xef && c2 == 0xbb && c3 == 0xbf) { + if (c4 == '<' && c5 == '?' && c6 == 'x') { + return "application/xml"; + } + } + + // big and little endian UTF-16 encodings, with byte order mark + if (c1 == 0xfe && c2 == 0xff) { + if (c3 == 0 && c4 == '<' && c5 == 0 && c6 == '?' && + c7 == 0 && c8 == 'x') { + return "application/xml"; + } + } + + if (c1 == 0xff && c2 == 0xfe) { + if (c3 == '<' && c4 == 0 && c5 == '?' && c6 == 0 && + c7 == 'x' && c8 == 0) { + return "application/xml"; + } + } + + // big and little endian UTF-32 encodings, with BOM + if (c1 == 0x00 && c2 == 0x00 && c3 == 0xfe && c4 == 0xff) { + if (c5 == 0 && c6 == 0 && c7 == 0 && c8 == '<' && + c9 == 0 && c10 == 0 && c11 == 0 && c12 == '?' && + c13 == 0 && c14 == 0 && c15 == 0 && c16 == 'x') { + return "application/xml"; + } + } + + if (c1 == 0xff && c2 == 0xfe && c3 == 0x00 && c4 == 0x00) { + if (c5 == '<' && c6 == 0 && c7 == 0 && c8 == 0 && + c9 == '?' && c10 == 0 && c11 == 0 && c12 == 0 && + c13 == 'x' && c14 == 0 && c15 == 0 && c16 == 0) { + return "application/xml"; + } + } + + if (c1 == 'G' && c2 == 'I' && c3 == 'F' && c4 == '8') { + return "image/gif"; + } + + if (c1 == '#' && c2 == 'd' && c3 == 'e' && c4 == 'f') { + return "image/x-bitmap"; + } + + if (c1 == '!' && c2 == ' ' && c3 == 'X' && c4 == 'P' && + c5 == 'M' && c6 == '2') { + return "image/x-pixmap"; + } + + if (c1 == 137 && c2 == 80 && c3 == 78 && + c4 == 71 && c5 == 13 && c6 == 10 && + c7 == 26 && c8 == 10) { + return "image/png"; + } + + if (c1 == 0xFF && c2 == 0xD8 && c3 == 0xFF) { + if (c4 == 0xE0 || c4 == 0xEE) { + return "image/jpeg"; + } + + /** + * File format used by digital cameras to store images. + * Exif Format can be read by any application supporting + * JPEG. Exif Spec can be found at: + * http://www.pima.net/standards/it10/PIMA15740/Exif_2-1.PDF + */ + if ((c4 == 0xE1) && + (c7 == 'E' && c8 == 'x' && c9 == 'i' && c10 =='f' && + c11 == 0)) { + return "image/jpeg"; + } + } + + if (c1 == 0xD0 && c2 == 0xCF && c3 == 0x11 && c4 == 0xE0 && + c5 == 0xA1 && c6 == 0xB1 && c7 == 0x1A && c8 == 0xE1) { + + /* Above is signature of Microsoft Structured Storage. + * Below this, could have tests for various SS entities. + * For now, just test for FlashPix. + */ + if (checkfpx(is)) { + return "image/vnd.fpx"; + } + } + + if (c1 == 0x2E && c2 == 0x73 && c3 == 0x6E && c4 == 0x64) { + return "audio/basic"; // .au format, big endian + } + + if (c1 == 0x64 && c2 == 0x6E && c3 == 0x73 && c4 == 0x2E) { + return "audio/basic"; // .au format, little endian + } + + if (c1 == 'R' && c2 == 'I' && c3 == 'F' && c4 == 'F') { + /* I don't know if this is official but evidence + * suggests that .wav files start with "RIFF" - brown + */ + return "audio/x-wav"; + } + return null; + } + + /** + * Check for FlashPix image data in InputStream is. Return true if + * the stream has FlashPix data, false otherwise. Before calling this + * method, the stream should have already been checked to be sure it + * contains Microsoft Structured Storage data. + */ + static private boolean checkfpx(InputStream is) throws IOException { + + /* Test for FlashPix image data in Microsoft Structured Storage format. + * In general, should do this with calls to an SS implementation. + * Lacking that, need to dig via offsets to get to the FlashPix + * ClassID. Details: + * + * Offset to Fpx ClsID from beginning of stream should be: + * + * FpxClsidOffset = rootEntryOffset + clsidOffset + * + * where: clsidOffset = 0x50. + * rootEntryOffset = headerSize + sectorSize*sectDirStart + * + 128*rootEntryDirectory + * + * where: headerSize = 0x200 (always) + * sectorSize = 2 raised to power of uSectorShift, + * which is found in the header at + * offset 0x1E. + * sectDirStart = found in the header at offset 0x30. + * rootEntryDirectory = in general, should search for + * directory labelled as root. + * We will assume value of 0 (i.e., + * rootEntry is in first directory) + */ + + // Mark the stream so we can reset it. 0x100 is enough for the first + // few reads, but the mark will have to be reset and set again once + // the offset to the root directory entry is computed. That offset + // can be very large and isn't know until the stream has been read from + is.mark(0x100); + + // Get the byte ordering located at 0x1E. 0xFE is Intel, + // 0xFF is other + long toSkip = (long)0x1C; + long posn; + + if ((posn = skipForward(is, toSkip)) < toSkip) { + is.reset(); + return false; + } + + int c[] = new int[16]; + if (readBytes(c, 2, is) < 0) { + is.reset(); + return false; + } + + int byteOrder = c[0]; + + posn+=2; + int uSectorShift; + if (readBytes(c, 2, is) < 0) { + is.reset(); + return false; + } + + if(byteOrder == 0xFE) { + uSectorShift = c[0]; + uSectorShift += c[1] << 8; + } + else { + uSectorShift = c[0] << 8; + uSectorShift += c[1]; + } + + posn += 2; + toSkip = (long)0x30 - posn; + long skipped = 0; + if ((skipped = skipForward(is, toSkip)) < toSkip) { + is.reset(); + return false; + } + posn += skipped; + + if (readBytes(c, 4, is) < 0) { + is.reset(); + return false; + } + + int sectDirStart; + if(byteOrder == 0xFE) { + sectDirStart = c[0]; + sectDirStart += c[1] << 8; + sectDirStart += c[2] << 16; + sectDirStart += c[3] << 24; + } else { + sectDirStart = c[0] << 24; + sectDirStart += c[1] << 16; + sectDirStart += c[2] << 8; + sectDirStart += c[3]; + } + posn += 4; + is.reset(); // Reset back to the beginning + + toSkip = 0x200L + (long)(1<<uSectorShift)*sectDirStart + 0x50L; + + // Sanity check! + if (toSkip < 0) { + return false; + } + + /* + * How far can we skip? Is there any performance problem here? + * This skip can be fairly long, at least 0x4c650 in at least + * one case. Have to assume that the skip will fit in an int. + * Leave room to read whole root dir + */ + is.mark((int)toSkip+0x30); + + if ((skipForward(is, toSkip)) < toSkip) { + is.reset(); + return false; + } + + /* should be at beginning of ClassID, which is as follows + * (in Intel byte order): + * 00 67 61 56 54 C1 CE 11 85 53 00 AA 00 A1 F9 5B + * + * This is stored from Windows as long,short,short,char[8] + * so for byte order changes, the order only changes for + * the first 8 bytes in the ClassID. + * + * Test against this, ignoring second byte (Intel) since + * this could change depending on part of Fpx file we have. + */ + + if (readBytes(c, 16, is) < 0) { + is.reset(); + return false; + } + + // intel byte order + if (byteOrder == 0xFE && + c[0] == 0x00 && c[2] == 0x61 && c[3] == 0x56 && + c[4] == 0x54 && c[5] == 0xC1 && c[6] == 0xCE && + c[7] == 0x11 && c[8] == 0x85 && c[9] == 0x53 && + c[10]== 0x00 && c[11]== 0xAA && c[12]== 0x00 && + c[13]== 0xA1 && c[14]== 0xF9 && c[15]== 0x5B) { + is.reset(); + return true; + } + + // non-intel byte order + else if (c[3] == 0x00 && c[1] == 0x61 && c[0] == 0x56 && + c[5] == 0x54 && c[4] == 0xC1 && c[7] == 0xCE && + c[6] == 0x11 && c[8] == 0x85 && c[9] == 0x53 && + c[10]== 0x00 && c[11]== 0xAA && c[12]== 0x00 && + c[13]== 0xA1 && c[14]== 0xF9 && c[15]== 0x5B) { + is.reset(); + return true; + } + is.reset(); + return false; + } + + /** + * Tries to read the specified number of bytes from the stream + * Returns -1, If EOF is reached before len bytes are read, returns 0 + * otherwise + */ + static private int readBytes(int c[], int len, InputStream is) + throws IOException { + + byte buf[] = new byte[len]; + if (is.read(buf, 0, len) < len) { + return -1; + } + + // fill the passed in int array + for (int i = 0; i < len; i++) { + c[i] = buf[i] & 0xff; + } + return 0; + } + + + /** + * Skips through the specified number of bytes from the stream + * until either EOF is reached, or the specified + * number of bytes have been skipped + */ + static private long skipForward(InputStream is, long toSkip) + throws IOException { + + long eachSkip = 0; + long skipped = 0; + + while (skipped != toSkip) { + eachSkip = is.skip(toSkip - skipped); + + // check if EOF is reached + if (eachSkip <= 0) { + if (is.read() == -1) { + return skipped ; + } else { + skipped++; + } + } + skipped += eachSkip; + } + return skipped; + } + +} + + +class UnknownContentHandler extends ContentHandler { + static final ContentHandler INSTANCE = new UnknownContentHandler(); + + public Object getContent(URLConnection uc) throws IOException { + return uc.getInputStream(); + } +}
diff --git a/java/net/URLDecoder.java b/java/net/URLDecoder.java new file mode 100644 index 0000000..8b14eb8 --- /dev/null +++ b/java/net/URLDecoder.java
@@ -0,0 +1,222 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.*; + +/** + * Utility class for HTML form decoding. This class contains static methods + * for decoding a String from the <CODE>application/x-www-form-urlencoded</CODE> + * MIME format. + * <p> + * The conversion process is the reverse of that used by the URLEncoder class. It is assumed + * that all characters in the encoded string are one of the following: + * "{@code a}" through "{@code z}", + * "{@code A}" through "{@code Z}", + * "{@code 0}" through "{@code 9}", and + * "{@code -}", "{@code _}", + * "{@code .}", and "{@code *}". The + * character "{@code %}" is allowed but is interpreted + * as the start of a special escaped sequence. + * <p> + * The following rules are applied in the conversion: + * + * <ul> + * <li>The alphanumeric characters "{@code a}" through + * "{@code z}", "{@code A}" through + * "{@code Z}" and "{@code 0}" + * through "{@code 9}" remain the same. + * <li>The special characters "{@code .}", + * "{@code -}", "{@code *}", and + * "{@code _}" remain the same. + * <li>The plus sign "{@code +}" is converted into a + * space character " " . + * <li>A sequence of the form "<i>{@code %xy}</i>" will be + * treated as representing a byte where <i>xy</i> is the two-digit + * hexadecimal representation of the 8 bits. Then, all substrings + * that contain one or more of these byte sequences consecutively + * will be replaced by the character(s) whose encoding would result + * in those consecutive bytes. + * The encoding scheme used to decode these characters may be specified, + * or if unspecified, the default encoding of the platform will be used. + * </ul> + * <p> + * There are two possible ways in which this decoder could deal with + * illegal strings. It could either leave illegal characters alone or + * it could throw an {@link java.lang.IllegalArgumentException}. + * Which approach the decoder takes is left to the + * implementation. + * + * @author Mark Chamness + * @author Michael McCloskey + * @since 1.2 + */ + +public class URLDecoder { + + // The platform default encoding + static String dfltEncName = URLEncoder.dfltEncName; + + /** + * Decodes a {@code x-www-form-urlencoded} string. + * The platform's default encoding is used to determine what characters + * are represented by any consecutive sequences of the form + * "<i>{@code %xy}</i>". + * @param s the {@code String} to decode + * @deprecated The resulting string may vary depending on the platform's + * default encoding. Instead, use the decode(String,String) method + * to specify the encoding. + * @return the newly decoded {@code String} + */ + @Deprecated + public static String decode(String s) { + + String str = null; + + try { + str = decode(s, dfltEncName); + } catch (UnsupportedEncodingException e) { + // The system should always have the platform default + } + + return str; + } + + /** + * Decodes a {@code application/x-www-form-urlencoded} string using a specific + * encoding scheme. + * The supplied encoding is used to determine + * what characters are represented by any consecutive sequences of the + * form "<i>{@code %xy}</i>". + * <p> + * <em><strong>Note:</strong> The <a href= + * "http://www.w3.org/TR/html40/appendix/notes.html#non-ascii-chars"> + * World Wide Web Consortium Recommendation</a> states that + * UTF-8 should be used. Not doing so may introduce + * incompatibilities.</em> + * + * @param s the {@code String} to decode + * @param enc The name of a supported + * <a href="../lang/package-summary.html#charenc">character + * encoding</a>. + * @return the newly decoded {@code String} + * @exception UnsupportedEncodingException + * If character encoding needs to be consulted, but + * named character encoding is not supported + * @see URLEncoder#encode(java.lang.String, java.lang.String) + * @since 1.4 + */ + public static String decode(String s, String enc) + throws UnsupportedEncodingException{ + + boolean needToChange = false; + int numChars = s.length(); + StringBuffer sb = new StringBuffer(numChars > 500 ? numChars / 2 : numChars); + int i = 0; + + if (enc.length() == 0) { + throw new UnsupportedEncodingException ("URLDecoder: empty string enc parameter"); + } + + char c; + byte[] bytes = null; + while (i < numChars) { + c = s.charAt(i); + switch (c) { + case '+': + sb.append(' '); + i++; + needToChange = true; + break; + case '%': + /* + * Starting with this instance of %, process all + * consecutive substrings of the form %xy. Each + * substring %xy will yield a byte. Convert all + * consecutive bytes obtained this way to whatever + * character(s) they represent in the provided + * encoding. + */ + + try { + + // (numChars-i)/3 is an upper bound for the number + // of remaining bytes + if (bytes == null) + bytes = new byte[(numChars-i)/3]; + int pos = 0; + + while ( ((i+2) < numChars) && + (c=='%')) { + // BEGIN Android-changed: App compat. Forbid non-hex chars after '%'. + if (!isValidHexChar(s.charAt(i+1)) || !isValidHexChar(s.charAt(i+2))) { + throw new IllegalArgumentException("URLDecoder: Illegal hex characters in escape (%) pattern : " + + s.substring(i, i + 3)); + } + // END Android-changed: App compat. Forbid non-hex chars after '%'. + int v = Integer.parseInt(s.substring(i+1,i+3),16); + if (v < 0) + // Android-changed: Improve error message by printing the string value. + throw new IllegalArgumentException("URLDecoder: Illegal hex characters in escape (%) pattern - negative value : " + + s.substring(i, i + 3)); + bytes[pos++] = (byte) v; + i+= 3; + if (i < numChars) + c = s.charAt(i); + } + + // A trailing, incomplete byte encoding such as + // "%x" will cause an exception to be thrown + + if ((i < numChars) && (c=='%')) + throw new IllegalArgumentException( + "URLDecoder: Incomplete trailing escape (%) pattern"); + + sb.append(new String(bytes, 0, pos, enc)); + } catch (NumberFormatException e) { + throw new IllegalArgumentException( + "URLDecoder: Illegal hex characters in escape (%) pattern - " + + e.getMessage()); + } + needToChange = true; + break; + default: + sb.append(c); + i++; + break; + } + } + + return (needToChange? sb.toString() : s); + } + + // BEGIN Android-added: App compat. Forbid non-hex chars after '%'. + private static boolean isValidHexChar(char c) { + return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F'); + } + // END Android-added: App compat. Forbid non-hex chars after '%'. +}
diff --git a/java/net/URLEncoder.java b/java/net/URLEncoder.java new file mode 100644 index 0000000..86377b7 --- /dev/null +++ b/java/net/URLEncoder.java
@@ -0,0 +1,292 @@ +/* + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.ByteArrayOutputStream; +import java.io.BufferedWriter; +import java.io.OutputStreamWriter; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.io.CharArrayWriter; +import java.nio.charset.Charset; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.UnsupportedCharsetException ; +import java.util.BitSet; +import java.security.AccessController; +import java.security.PrivilegedAction; +import sun.security.action.GetBooleanAction; +import sun.security.action.GetPropertyAction; + +/** + * Utility class for HTML form encoding. This class contains static methods + * for converting a String to the <CODE>application/x-www-form-urlencoded</CODE> MIME + * format. For more information about HTML form encoding, consult the HTML + * <A HREF="http://www.w3.org/TR/html4/">specification</A>. + * + * <p> + * When encoding a String, the following rules apply: + * + * <ul> + * <li>The alphanumeric characters "{@code a}" through + * "{@code z}", "{@code A}" through + * "{@code Z}" and "{@code 0}" + * through "{@code 9}" remain the same. + * <li>The special characters "{@code .}", + * "{@code -}", "{@code *}", and + * "{@code _}" remain the same. + * <li>The space character " " is + * converted into a plus sign "{@code +}". + * <li>All other characters are unsafe and are first converted into + * one or more bytes using some encoding scheme. Then each byte is + * represented by the 3-character string + * "<i>{@code %xy}</i>", where <i>xy</i> is the + * two-digit hexadecimal representation of the byte. + * The recommended encoding scheme to use is UTF-8. However, + * for compatibility reasons, if an encoding is not specified, + * then the default encoding of the platform is used. + * </ul> + * + * <p> + * For example using UTF-8 as the encoding scheme the string "The + * string ü@foo-bar" would get converted to + * "The+string+%C3%BC%40foo-bar" because in UTF-8 the character + * ü is encoded as two bytes C3 (hex) and BC (hex), and the + * character @ is encoded as one byte 40 (hex). + * + * @author Herb Jellinek + * @since JDK1.0 + */ +public class URLEncoder { + static BitSet dontNeedEncoding; + static final int caseDiff = ('a' - 'A'); + static String dfltEncName = null; + + static { + + /* The list of characters that are not encoded has been + * determined as follows: + * + * RFC 2396 states: + * ----- + * Data characters that are allowed in a URI but do not have a + * reserved purpose are called unreserved. These include upper + * and lower case letters, decimal digits, and a limited set of + * punctuation marks and symbols. + * + * unreserved = alphanum | mark + * + * mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" + * + * Unreserved characters can be escaped without changing the + * semantics of the URI, but this should not be done unless the + * URI is being used in a context that does not allow the + * unescaped character to appear. + * ----- + * + * It appears that both Netscape and Internet Explorer escape + * all special characters from this list with the exception + * of "-", "_", ".", "*". While it is not clear why they are + * escaping the other characters, perhaps it is safest to + * assume that there might be contexts in which the others + * are unsafe if not escaped. Therefore, we will use the same + * list. It is also noteworthy that this is consistent with + * O'Reilly's "HTML: The Definitive Guide" (page 164). + * + * As a last note, Intenet Explorer does not encode the "@" + * character which is clearly not unreserved according to the + * RFC. We are being consistent with the RFC in this matter, + * as is Netscape. + * + */ + + dontNeedEncoding = new BitSet(256); + int i; + for (i = 'a'; i <= 'z'; i++) { + dontNeedEncoding.set(i); + } + for (i = 'A'; i <= 'Z'; i++) { + dontNeedEncoding.set(i); + } + for (i = '0'; i <= '9'; i++) { + dontNeedEncoding.set(i); + } + dontNeedEncoding.set(' '); /* encoding a space to a + is done + * in the encode() method */ + dontNeedEncoding.set('-'); + dontNeedEncoding.set('_'); + dontNeedEncoding.set('.'); + dontNeedEncoding.set('*'); + + dfltEncName = AccessController.doPrivileged( + new GetPropertyAction("file.encoding") + ); + } + + /** + * You can't call the constructor. + */ + private URLEncoder() { } + + /** + * Translates a string into {@code x-www-form-urlencoded} + * format. This method uses the platform's default encoding + * as the encoding scheme to obtain the bytes for unsafe characters. + * + * @param s {@code String} to be translated. + * @deprecated The resulting string may vary depending on the platform's + * default encoding. Instead, use the encode(String,String) + * method to specify the encoding. + * @return the translated {@code String}. + */ + @Deprecated + public static String encode(String s) { + + String str = null; + + try { + str = encode(s, dfltEncName); + } catch (UnsupportedEncodingException e) { + // The system should always have the platform default + } + + return str; + } + + /** + * Translates a string into {@code application/x-www-form-urlencoded} + * format using a specific encoding scheme. This method uses the + * supplied encoding scheme to obtain the bytes for unsafe + * characters. + * <p> + * <em><strong>Note:</strong> The <a href= + * "http://www.w3.org/TR/html40/appendix/notes.html#non-ascii-chars"> + * World Wide Web Consortium Recommendation</a> states that + * UTF-8 should be used. Not doing so may introduce + * incompatibilities.</em> + * + * @param s {@code String} to be translated. + * @param enc The name of a supported + * <a href="../lang/package-summary.html#charenc">character + * encoding</a>. + * @return the translated {@code String}. + * @exception UnsupportedEncodingException + * If the named encoding is not supported + * @see URLDecoder#decode(java.lang.String, java.lang.String) + * @since 1.4 + */ + public static String encode(String s, String enc) + throws UnsupportedEncodingException { + + boolean needToChange = false; + StringBuffer out = new StringBuffer(s.length()); + Charset charset; + CharArrayWriter charArrayWriter = new CharArrayWriter(); + + if (enc == null) + throw new NullPointerException("charsetName"); + + try { + charset = Charset.forName(enc); + } catch (IllegalCharsetNameException e) { + throw new UnsupportedEncodingException(enc); + } catch (UnsupportedCharsetException e) { + throw new UnsupportedEncodingException(enc); + } + + for (int i = 0; i < s.length();) { + int c = (int) s.charAt(i); + //System.out.println("Examining character: " + c); + if (dontNeedEncoding.get(c)) { + if (c == ' ') { + c = '+'; + needToChange = true; + } + //System.out.println("Storing: " + c); + out.append((char)c); + i++; + } else { + // convert to external encoding before hex conversion + do { + charArrayWriter.write(c); + /* + * If this character represents the start of a Unicode + * surrogate pair, then pass in two characters. It's not + * clear what should be done if a bytes reserved in the + * surrogate pairs range occurs outside of a legal + * surrogate pair. For now, just treat it as if it were + * any other character. + */ + if (c >= 0xD800 && c <= 0xDBFF) { + /* + System.out.println(Integer.toHexString(c) + + " is high surrogate"); + */ + if ( (i+1) < s.length()) { + int d = (int) s.charAt(i+1); + /* + System.out.println("\tExamining " + + Integer.toHexString(d)); + */ + if (d >= 0xDC00 && d <= 0xDFFF) { + /* + System.out.println("\t" + + Integer.toHexString(d) + + " is low surrogate"); + */ + charArrayWriter.write(d); + i++; + } + } + } + i++; + } while (i < s.length() && !dontNeedEncoding.get((c = (int) s.charAt(i)))); + + charArrayWriter.flush(); + String str = new String(charArrayWriter.toCharArray()); + byte[] ba = str.getBytes(charset); + for (int j = 0; j < ba.length; j++) { + out.append('%'); + char ch = Character.forDigit((ba[j] >> 4) & 0xF, 16); + // converting to use uppercase letter as part of + // the hex value if ch is a letter. + if (Character.isLetter(ch)) { + ch -= caseDiff; + } + out.append(ch); + ch = Character.forDigit(ba[j] & 0xF, 16); + if (Character.isLetter(ch)) { + ch -= caseDiff; + } + out.append(ch); + } + charArrayWriter.reset(); + needToChange = true; + } + } + + return (needToChange? out.toString() : s); + } +}
diff --git a/java/net/URLStreamHandler.java b/java/net/URLStreamHandler.java new file mode 100644 index 0000000..dffc6d5 --- /dev/null +++ b/java/net/URLStreamHandler.java
@@ -0,0 +1,626 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.IOException; +import java.util.Objects; + +import sun.net.util.IPAddressUtil; + +/** + * The abstract class {@code URLStreamHandler} is the common + * superclass for all stream protocol handlers. A stream protocol + * handler knows how to make a connection for a particular protocol + * type, such as {@code http} or {@code https}. + * <p> + * In most cases, an instance of a {@code URLStreamHandler} + * subclass is not created directly by an application. Rather, the + * first time a protocol name is encountered when constructing a + * {@code URL}, the appropriate stream protocol handler is + * automatically loaded. + * + * @author James Gosling + * @see java.net.URL#URL(java.lang.String, java.lang.String, int, java.lang.String) + * @since JDK1.0 + */ +public abstract class URLStreamHandler { + /** + * Opens a connection to the object referenced by the + * {@code URL} argument. + * This method should be overridden by a subclass. + * + * <p>If for the handler's protocol (such as HTTP or JAR), there + * exists a public, specialized URLConnection subclass belonging + * to one of the following packages or one of their subpackages: + * java.lang, java.io, java.util, java.net, the connection + * returned will be of that subclass. For example, for HTTP an + * HttpURLConnection will be returned, and for JAR a + * JarURLConnection will be returned. + * + * @param u the URL that this connects to. + * @return a {@code URLConnection} object for the {@code URL}. + * @exception IOException if an I/O error occurs while opening the + * connection. + */ + abstract protected URLConnection openConnection(URL u) throws IOException; + + /** + * Same as openConnection(URL), except that the connection will be + * made through the specified proxy; Protocol handlers that do not + * support proxying will ignore the proxy parameter and make a + * normal connection. + * + * Calling this method preempts the system's default ProxySelector + * settings. + * + * @param u the URL that this connects to. + * @param p the proxy through which the connection will be made. + * If direct connection is desired, Proxy.NO_PROXY + * should be specified. + * @return a {@code URLConnection} object for the {@code URL}. + * @exception IOException if an I/O error occurs while opening the + * connection. + * @exception IllegalArgumentException if either u or p is null, + * or p has the wrong type. + * @exception UnsupportedOperationException if the subclass that + * implements the protocol doesn't support this method. + * @since 1.5 + */ + protected URLConnection openConnection(URL u, Proxy p) throws IOException { + throw new UnsupportedOperationException("Method not implemented."); + } + + /** + * Parses the string representation of a {@code URL} into a + * {@code URL} object. + * <p> + * If there is any inherited context, then it has already been + * copied into the {@code URL} argument. + * <p> + * The {@code parseURL} method of {@code URLStreamHandler} + * parses the string representation as if it were an + * {@code http} specification. Most URL protocol families have a + * similar parsing. A stream protocol handler for a protocol that has + * a different syntax must override this routine. + * + * @param u the {@code URL} to receive the result of parsing + * the spec. + * @param spec the {@code String} representing the URL that + * must be parsed. + * @param start the character index at which to begin parsing. This is + * just past the '{@code :}' (if there is one) that + * specifies the determination of the protocol name. + * @param limit the character position to stop parsing at. This is the + * end of the string or the position of the + * "{@code #}" character, if present. All information + * after the sharp sign indicates an anchor. + */ + protected void parseURL(URL u, String spec, int start, int limit) { + // These fields may receive context content if this was relative URL + String protocol = u.getProtocol(); + String authority = u.getAuthority(); + String userInfo = u.getUserInfo(); + String host = u.getHost(); + int port = u.getPort(); + String path = u.getPath(); + String query = u.getQuery(); + + // This field has already been parsed + String ref = u.getRef(); + + boolean isRelPath = false; + boolean queryOnly = false; + // BEGIN Android-changed: App compat + boolean querySet = false; + // END Android-changed: App compat + +// FIX: should not assume query if opaque + // Strip off the query part + if (start < limit) { + int queryStart = spec.indexOf('?'); + queryOnly = queryStart == start; + if ((queryStart != -1) && (queryStart < limit)) { + query = spec.substring(queryStart+1, limit); + if (limit > queryStart) + limit = queryStart; + spec = spec.substring(0, queryStart); + // BEGIN Android-changed: App compat + querySet = true; + // END Android-changed: App compat + } + } + + int i = 0; + // Parse the authority part if any + // BEGIN Android-changed: App compat + // boolean isUNCName = (start <= limit - 4) && + // (spec.charAt(start) == '/') && + // (spec.charAt(start + 1) == '/') && + // (spec.charAt(start + 2) == '/') && + // (spec.charAt(start + 3) == '/'); + boolean isUNCName = false; + // END Android-changed: App compat + if (!isUNCName && (start <= limit - 2) && (spec.charAt(start) == '/') && + (spec.charAt(start + 1) == '/')) { + start += 2; + // BEGIN Android-changed: Check for all hostname termination chars. http://b/110955991 + /* + i = spec.indexOf('/', start); + if (i < 0 || i > limit) { + i = spec.indexOf('?', start); + if (i < 0 || i > limit) + i = limit; + } + */ + LOOP: for (i = start; i < limit; i++) { + switch (spec.charAt(i)) { + case '/': // Start of path + case '\\': // Start of path - see https://url.spec.whatwg.org/#host-state + case '?': // Start of query + case '#': // Start of fragment + break LOOP; + } + } + // END Android-changed: Check for all hostname termination chars. http://b/110955991 + + host = authority = spec.substring(start, i); + + int ind = authority.indexOf('@'); + if (ind != -1) { + if (ind != authority.lastIndexOf('@')) { + // more than one '@' in authority. This is not server based + userInfo = null; + host = null; + } else { + userInfo = authority.substring(0, ind); + host = authority.substring(ind+1); + } + } else { + userInfo = null; + } + if (host != null) { + // If the host is surrounded by [ and ] then its an IPv6 + // literal address as specified in RFC2732 + if (host.length()>0 && (host.charAt(0) == '[')) { + if ((ind = host.indexOf(']')) > 2) { + + String nhost = host ; + host = nhost.substring(0,ind+1); + if (!IPAddressUtil. + isIPv6LiteralAddress(host.substring(1, ind))) { + throw new IllegalArgumentException( + "Invalid host: "+ host); + } + + port = -1 ; + if (nhost.length() > ind+1) { + if (nhost.charAt(ind+1) == ':') { + ++ind ; + // port can be null according to RFC2396 + if (nhost.length() > (ind + 1)) { + port = Integer.parseInt(nhost.substring(ind+1)); + } + } else { + throw new IllegalArgumentException( + "Invalid authority field: " + authority); + } + } + } else { + throw new IllegalArgumentException( + "Invalid authority field: " + authority); + } + } else { + ind = host.indexOf(':'); + port = -1; + if (ind >= 0) { + // port can be null according to RFC2396 + if (host.length() > (ind + 1)) { + // BEGIN Android-changed: App compat + // port = Integer.parseInt(host.substring(ind + 1)); + char firstPortChar = host.charAt(ind+1); + if (firstPortChar >= '0' && firstPortChar <= '9') { + port = Integer.parseInt(host.substring(ind + 1)); + } else { + throw new IllegalArgumentException("invalid port: " + + host.substring(ind + 1)); + } + // END Android-changed: App compat + } + host = host.substring(0, ind); + } + } + } else { + host = ""; + } + if (port < -1) + throw new IllegalArgumentException("Invalid port number :" + + port); + start = i; + + // If the authority is defined then the path is defined by the + // spec only; See RFC 2396 Section 5.2.4. + // BEGIN Android-changed: App compat + // if (authority != null && authority.length() > 0) + // path = ""; + path = null; + if (!querySet) { + query = null; + } + // END Android-changed: App compat + } + + if (host == null) { + host = ""; + } + + // Parse the file path if any + if (start < limit) { + // Android-changed: Check for all hostname termination chars. http://b/110955991 + // if (spec.charAt(start) == '/') { + if (spec.charAt(start) == '/' || spec.charAt(start) == '\\') { + path = spec.substring(start, limit); + } else if (path != null && path.length() > 0) { + isRelPath = true; + int ind = path.lastIndexOf('/'); + String seperator = ""; + if (ind == -1 && authority != null) + seperator = "/"; + path = path.substring(0, ind + 1) + seperator + + spec.substring(start, limit); + + } else { + String seperator = (authority != null) ? "/" : ""; + path = seperator + spec.substring(start, limit); + } + } + // BEGIN Android-changed: App compat + //else if (queryOnly && path != null) { + // int ind = path.lastIndexOf('/'); + // if (ind < 0) + // ind = 0; + // path = path.substring(0, ind) + "/"; + //} + // END Android-changed: App compat + if (path == null) + path = ""; + + // BEGIN Android-changed + //if (isRelPath) { + if (true) { + // END Android-changed + // Remove embedded /./ + while ((i = path.indexOf("/./")) >= 0) { + path = path.substring(0, i) + path.substring(i + 2); + } + // Remove embedded /../ if possible + i = 0; + while ((i = path.indexOf("/../", i)) >= 0) { + // BEGIN Android-changed: App compat + /* + * Trailing /../ + */ + if (i == 0) { + path = path.substring(i + 3); + i = 0; + // END Android-changed: App compat + /* + * A "/../" will cancel the previous segment and itself, + * unless that segment is a "/../" itself + * i.e. "/a/b/../c" becomes "/a/c" + * but "/../../a" should stay unchanged + */ + // Android-changed: App compat + // if (i > 0 && (limit = path.lastIndexOf('/', i - 1)) >= 0 && + } else if (i > 0 && (limit = path.lastIndexOf('/', i - 1)) >= 0 && + (path.indexOf("/../", limit) != 0)) { + path = path.substring(0, limit) + path.substring(i + 3); + i = 0; + } else { + i = i + 3; + } + } + // Remove trailing .. if possible + while (path.endsWith("/..")) { + i = path.indexOf("/.."); + if ((limit = path.lastIndexOf('/', i - 1)) >= 0) { + path = path.substring(0, limit+1); + } else { + break; + } + } + // Remove starting . + if (path.startsWith("./") && path.length() > 2) + path = path.substring(2); + + // Remove trailing . + if (path.endsWith("/.")) + path = path.substring(0, path.length() -1); + + // Android-changed: App compat: Remove trailing ? + if (path.endsWith("?")) + path = path.substring(0, path.length() -1); + } + + setURL(u, protocol, host, port, authority, userInfo, path, query, ref); + } + + /** + * Returns the default port for a URL parsed by this handler. This method + * is meant to be overidden by handlers with default port numbers. + * @return the default port for a {@code URL} parsed by this handler. + * @since 1.3 + */ + protected int getDefaultPort() { + return -1; + } + + /** + * Provides the default equals calculation. May be overidden by handlers + * for other protocols that have different requirements for equals(). + * This method requires that none of its arguments is null. This is + * guaranteed by the fact that it is only called by java.net.URL class. + * @param u1 a URL object + * @param u2 a URL object + * @return {@code true} if the two urls are + * considered equal, ie. they refer to the same + * fragment in the same file. + * @since 1.3 + */ + protected boolean equals(URL u1, URL u2) { + // Android-changed: Avoid network I/O + return Objects.equals(u1.getRef(), u2.getRef()) && + Objects.equals(u1.getQuery(), u2.getQuery()) && + // sameFile compares the protocol, file, port & host components of + // the URLs. + sameFile(u1, u2); + } + + /** + * Provides the default hash calculation. May be overidden by handlers for + * other protocols that have different requirements for hashCode + * calculation. + * @param u a URL object + * @return an {@code int} suitable for hash table indexing + * @since 1.3 + */ + protected int hashCode(URL u) { + // Android-changed: Avoid network I/O + // Hash on the same set of fields that we compare in equals(). + return Objects.hash( + u.getRef(), + u.getQuery(), + u.getProtocol(), + u.getFile(), + u.getHost(), + u.getPort()); + } + + /** + * Compare two urls to see whether they refer to the same file, + * i.e., having the same protocol, host, port, and path. + * This method requires that none of its arguments is null. This is + * guaranteed by the fact that it is only called indirectly + * by java.net.URL class. + * @param u1 a URL object + * @param u2 a URL object + * @return true if u1 and u2 refer to the same file + * @since 1.3 + */ + protected boolean sameFile(URL u1, URL u2) { + // Compare the protocols. + if (!((u1.getProtocol() == u2.getProtocol()) || + (u1.getProtocol() != null && + u1.getProtocol().equalsIgnoreCase(u2.getProtocol())))) + return false; + + // Compare the files. + if (!(u1.getFile() == u2.getFile() || + (u1.getFile() != null && u1.getFile().equals(u2.getFile())))) + return false; + + // Compare the ports. + int port1, port2; + port1 = (u1.getPort() != -1) ? u1.getPort() : u1.handler.getDefaultPort(); + port2 = (u2.getPort() != -1) ? u2.getPort() : u2.handler.getDefaultPort(); + if (port1 != port2) + return false; + + // Compare the hosts. + if (!hostsEqual(u1, u2)) + return false; + + return true; + } + + /** + * Get the IP address of our host. An empty host field or a DNS failure + * will result in a null return. + * + * @param u a URL object + * @return an {@code InetAddress} representing the host + * IP address. + * @since 1.3 + */ + protected synchronized InetAddress getHostAddress(URL u) { + if (u.hostAddress != null) + return u.hostAddress; + + String host = u.getHost(); + if (host == null || host.equals("")) { + return null; + } else { + try { + u.hostAddress = InetAddress.getByName(host); + } catch (UnknownHostException ex) { + return null; + } catch (SecurityException se) { + return null; + } + } + return u.hostAddress; + } + + /** + * Compares the host components of two URLs. + * @param u1 the URL of the first host to compare + * @param u2 the URL of the second host to compare + * @return {@code true} if and only if they + * are equal, {@code false} otherwise. + * @since 1.3 + */ + protected boolean hostsEqual(URL u1, URL u2) { + // Android-changed: Don't compare the InetAddresses of the hosts. + if (u1.getHost() != null && u2.getHost() != null) + return u1.getHost().equalsIgnoreCase(u2.getHost()); + else + return u1.getHost() == null && u2.getHost() == null; + } + + /** + * Converts a {@code URL} of a specific protocol to a + * {@code String}. + * + * @param u the URL. + * @return a string representation of the {@code URL} argument. + */ + protected String toExternalForm(URL u) { + + // pre-compute length of StringBuffer + int len = u.getProtocol().length() + 1; + if (u.getAuthority() != null && u.getAuthority().length() > 0) + len += 2 + u.getAuthority().length(); + if (u.getPath() != null) { + len += u.getPath().length(); + } + if (u.getQuery() != null) { + len += 1 + u.getQuery().length(); + } + if (u.getRef() != null) + len += 1 + u.getRef().length(); + + // BEGIN Android-changed: Add a toExternalForm variant that optionally escapes illegal chars + // TODO: The variant has been removed. We can potentially revert the change + StringBuilder result = new StringBuilder(len); + result.append(u.getProtocol()); + result.append(":"); + if (u.getAuthority() != null) {// ANDROID: && u.getAuthority().length() > 0) { + result.append("//"); + result.append(u.getAuthority()); + } + String fileAndQuery = u.getFile(); + if (fileAndQuery != null) { + result.append(fileAndQuery); + } + // END Android-changed: Add a toExternalForm variant that optionally escapes illegal chars + if (u.getRef() != null) { + result.append("#"); + result.append(u.getRef()); + } + return result.toString(); + } + + // Android-changed: Removed @see tag (target is package-private): + // @see java.net.URL#set(java.lang.String, java.lang.String, int, java.lang.String, java.lang.String) + /** + * Sets the fields of the {@code URL} argument to the indicated values. + * Only classes derived from URLStreamHandler are able + * to use this method to set the values of the URL fields. + * + * @param u the URL to modify. + * @param protocol the protocol name. + * @param host the remote host value for the URL. + * @param port the port on the remote machine. + * @param authority the authority part for the URL. + * @param userInfo the userInfo part of the URL. + * @param path the path component of the URL. + * @param query the query part for the URL. + * @param ref the reference. + * @exception SecurityException if the protocol handler of the URL is + * different from this one + * @since 1.3 + */ + protected void setURL(URL u, String protocol, String host, int port, + String authority, String userInfo, String path, + String query, String ref) { + if (this != u.handler) { + throw new SecurityException("handler for url different from " + + "this handler"); + } + // ensure that no one can reset the protocol on a given URL. + u.set(u.getProtocol(), host, port, authority, userInfo, path, query, ref); + } + + /** + * Sets the fields of the {@code URL} argument to the indicated values. + * Only classes derived from URLStreamHandler are able + * to use this method to set the values of the URL fields. + * + * @param u the URL to modify. + * @param protocol the protocol name. This value is ignored since 1.2. + * @param host the remote host value for the URL. + * @param port the port on the remote machine. + * @param file the file. + * @param ref the reference. + * @exception SecurityException if the protocol handler of the URL is + * different from this one + * @deprecated Use setURL(URL, String, String, int, String, String, String, + * String); + */ + @Deprecated + protected void setURL(URL u, String protocol, String host, int port, + String file, String ref) { + /* + * Only old URL handlers call this, so assume that the host + * field might contain "user:passwd@host". Fix as necessary. + */ + String authority = null; + String userInfo = null; + if (host != null && host.length() != 0) { + authority = (port == -1) ? host : host + ":" + port; + int at = host.lastIndexOf('@'); + if (at != -1) { + userInfo = host.substring(0, at); + host = host.substring(at+1); + } + } + + /* + * Assume file might contain query part. Fix as necessary. + */ + String path = null; + String query = null; + if (file != null) { + int q = file.lastIndexOf('?'); + if (q != -1) { + query = file.substring(q+1); + path = file.substring(0, q); + } else + path = file; + } + setURL(u, protocol, host, port, authority, userInfo, path, query, ref); + } +}
diff --git a/java/net/URLStreamHandlerFactory.java b/java/net/URLStreamHandlerFactory.java new file mode 100644 index 0000000..e46e028 --- /dev/null +++ b/java/net/URLStreamHandlerFactory.java
@@ -0,0 +1,51 @@ +/* + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +/** + * This interface defines a factory for {@code URL} stream + * protocol handlers. + * <p> + * It is used by the {@code URL} class to create a + * {@code URLStreamHandler} for a specific protocol. + * + * @author Arthur van Hoff + * @see java.net.URL + * @see java.net.URLStreamHandler + * @since JDK1.0 + */ +public interface URLStreamHandlerFactory { + /** + * Creates a new {@code URLStreamHandler} instance with the specified + * protocol. + * + * @param protocol the protocol ("{@code ftp}", + * "{@code http}", "{@code nntp}", etc.). + * @return a {@code URLStreamHandler} for the specific protocol. + * @see java.net.URLStreamHandler + */ + URLStreamHandler createURLStreamHandler(String protocol); +}
diff --git a/java/net/UnknownHostException.java b/java/net/UnknownHostException.java new file mode 100644 index 0000000..21a9d14 --- /dev/null +++ b/java/net/UnknownHostException.java
@@ -0,0 +1,56 @@ +/* + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.IOException; + +/** + * Thrown to indicate that the IP address of a host could not be determined. + * + * @author Jonathan Payne + * @since JDK1.0 + */ +public +class UnknownHostException extends IOException { + private static final long serialVersionUID = -4639126076052875403L; + + /** + * Constructs a new {@code UnknownHostException} with the + * specified detail message. + * + * @param host the detail message. + */ + public UnknownHostException(String host) { + super(host); + } + + /** + * Constructs a new {@code UnknownHostException} with no detail + * message. + */ + public UnknownHostException() { + } +}
diff --git a/java/net/UnknownServiceException.java b/java/net/UnknownServiceException.java new file mode 100644 index 0000000..4eea4a7 --- /dev/null +++ b/java/net/UnknownServiceException.java
@@ -0,0 +1,58 @@ +/* + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.IOException; + +/** + * Thrown to indicate that an unknown service exception has + * occurred. Either the MIME type returned by a URL connection does + * not make sense, or the application is attempting to write to a + * read-only URL connection. + * + * @author unascribed + * @since JDK1.0 + */ +public class UnknownServiceException extends IOException { + private static final long serialVersionUID = -4169033248853639508L; + + /** + * Constructs a new {@code UnknownServiceException} with no + * detail message. + */ + public UnknownServiceException() { + } + + /** + * Constructs a new {@code UnknownServiceException} with the + * specified detail message. + * + * @param msg the detail message. + */ + public UnknownServiceException(String msg) { + super(msg); + } +}
diff --git a/java/net/package-info.java b/java/net/package-info.java new file mode 100644 index 0000000..fda2e4f --- /dev/null +++ b/java/net/package-info.java
@@ -0,0 +1,161 @@ +/* + * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * Provides the classes for implementing networking applications. + * + * <p> The java.net package can be roughly divided in two sections:</p> + * <ul> + * <li><p><i>A Low Level API</i>, which deals with the + * following abstractions:</p> + * <ul> + * <li><p><i>Addresses</i>, which are networking identifiers, + * like IP addresses.</p></li> + * <li><p><i>Sockets</i>, which are basic bidirectional data communication + * mechanisms.</p></li> + * <li><p><i>Interfaces</i>, which describe network interfaces. </p></li> + * </ul></li> + * <li> <p><i>A High Level API</i>, which deals with the following + * abstractions:</p> + * <ul> + * <li><p><i>URIs</i>, which represent + * Universal Resource Identifiers.</p></li> + * <li><p><i>URLs</i>, which represent + * Universal Resource Locators.</p></li> + * <li><p><i>Connections</i>, which represents connections to the resource + * pointed to by <i>URLs</i>.</p></li> + * </ul></li> + * </ul> + * <h2>Addresses</h2> + * <p>Addresses are used throughout the java.net APIs as either host + * identifiers, or socket endpoint identifiers.</p> + * <p>The {@link java.net.InetAddress} class is the abstraction representing an + * IP (Internet Protocol) address. It has two subclasses: + * <ul> + * <li>{@link java.net.Inet4Address} for IPv4 addresses.</li> + * <li>{@link java.net.Inet6Address} for IPv6 addresses.</li> + * </ul> + * <p>But, in most cases, there is no need to deal directly with the subclasses, + * as the InetAddress abstraction should cover most of the needed + * functionality.</p> + * <h3><b>About IPv6</b></h3> + * <p>Not all systems have support for the IPv6 protocol, and while the Java + * networking stack will attempt to detect it and use it transparently when + * available, it is also possible to disable its use with a system property. + * In the case where IPv6 is not available, or explicitly disabled, + * Inet6Address are not valid arguments for most networking operations any + * more. While methods like {@link java.net.InetAddress#getByName} are + * guaranteed not to return an Inet6Address when looking up host names, it + * is possible, by passing literals, to create such an object. In which + * case, most methods, when called with an Inet6Address will throw an + * Exception.</p> + * <h2>Sockets</h2> + * <p>Sockets are means to establish a communication link between machines over + * the network. The java.net package provides 4 kinds of Sockets:</p> + * <ul> + * <li>{@link java.net.Socket} is a TCP client API, and will typically + * be used to {@linkplain java.net.Socket#connect(SocketAddress) + * connect} to a remote host.</li> + * <li>{@link java.net.ServerSocket} is a TCP server API, and will + * typically {@linkplain java.net.ServerSocket#accept accept} + * connections from client sockets.</li> + * <li>{@link java.net.DatagramSocket} is a UDP endpoint API and is used + * to {@linkplain java.net.DatagramSocket#send send} and + * {@linkplain java.net.DatagramSocket#receive receive} + * {@linkplain java.net.DatagramPacket datagram packets}.</li> + * <li>{@link java.net.MulticastSocket} is a subclass of + * {@code DatagramSocket} used when dealing with multicast + * groups.</li> + * </ul> + * <p>Sending and receiving with TCP sockets is done through InputStreams and + * OutputStreams which can be obtained via the + * {@link java.net.Socket#getInputStream} and + * {@link java.net.Socket#getOutputStream} methods.</p> + * <h2>Interfaces</h2> + * <p>The {@link java.net.NetworkInterface} class provides APIs to browse and + * query all the networking interfaces (e.g. ethernet connection or PPP + * endpoint) of the local machine. It is through that class that you can + * check if any of the local interfaces is configured to support IPv6.</p> + * <p>Note, all conforming implementations must support at least one + * {@code NetworkInterface} object, which must either be connected to a + * network, or be a "loopback" interface that can only communicate with + * entities on the same machine.</p> + * + * <h2>High level API</h2> + * <p>A number of classes in the java.net package do provide for a much higher + * level of abstraction and allow for easy access to resources on the + * network. The classes are: + * <ul> + * <li>{@link java.net.URI} is the class representing a + * Universal Resource Identifier, as specified in RFC 2396. + * As the name indicates, this is just an Identifier and doesn't + * provide directly the means to access the resource.</li> + * <li>{@link java.net.URL} is the class representing a + * Universal Resource Locator, which is both an older concept for + * URIs and a means to access the resources.</li> + * <li>{@link java.net.URLConnection} is created from a URL and is the + * communication link used to access the resource pointed by the + * URL. This abstract class will delegate most of the work to the + * underlying protocol handlers like http or https.</li> + * <li>{@link java.net.HttpURLConnection} is a subclass of URLConnection + * and provides some additional functionalities specific to the + * HTTP protocol.</li> + * </ul> + * <p>The recommended usage is to use {@link java.net.URI} to identify + * resources, then convert it into a {@link java.net.URL} when it is time to + * access the resource. From that URL, you can either get the + * {@link java.net.URLConnection} for fine control, or get directly the + * InputStream. + * <p>Here is an example:</p> + * <pre> + * URI uri = new URI("http://java.sun.com/"); + * URL url = uri.toURL(); + * InputStream in = url.openStream(); + * </pre> + * <h2>Protocol Handlers</h2> + * As mentioned, URL and URLConnection rely on protocol handlers which must be + * present, otherwise an Exception is thrown. This is the major difference with + * URIs which only identify resources, and therefore don't need to have access + * to the protocol handler. So, while it is possible to create an URI with any + * kind of protocol scheme (e.g. {@code myproto://myhost.mydomain/resource/}), + * a similar URL will try to instantiate the handler for the specified protocol; + * if it doesn't exist an exception will be thrown. + * <p>By default the protocol handlers are loaded dynamically from the default + * location. It is, however, possible to add to the search path by setting + * the {@code java.protocol.handler.pkgs} system property. For instance if + * it is set to {@code myapp.protocols}, then the URL code will try, in the + * case of http, first to load {@code myapp.protocols.http.Handler}, then, + * if this fails, {@code http.Handler} from the default location. + * <p>Note that the Handler class <b>has to</b> be a subclass of the abstract + * class {@link java.net.URLStreamHandler}.</p> + * <h2>Additional Specification</h2> + * <ul> + * <li><a href="doc-files/net-properties.html"> + * Networking System Properties</a></li> + * </ul> + * + * @since JDK1.0 + */ +package java.net;