1 /*
2  * Copyright 2016 The Netty Project
3  *
4  * The Netty Project licenses this file to you under the Apache License,
5  * version 2.0 (the "License"); you may not use this file except in compliance
6  * with the License. You may obtain a copy of the License at:
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations
14  * under the License.
15  */

16 package io.netty.util.internal;
17
18 import java.io.IOException;
19 import java.net.InetAddress;
20 import java.net.InetSocketAddress;
21 import java.net.NetworkInterface;
22 import java.net.ServerSocket;
23 import java.net.Socket;
24 import java.net.SocketAddress;
25 import java.net.SocketException;
26 import java.net.SocketPermission;
27 import java.net.UnknownHostException;
28 import java.nio.channels.DatagramChannel;
29 import java.nio.channels.ServerSocketChannel;
30 import java.nio.channels.SocketChannel;
31 import java.security.AccessController;
32 import java.security.PrivilegedAction;
33 import java.security.PrivilegedActionException;
34 import java.security.PrivilegedExceptionAction;
35 import java.util.Collections;
36 import java.util.Enumeration;
37
38 /**
39  * Provides socket operations with privileges enabled. This is necessary for applications that use the
40  * {@link SecurityManager} to restrict {@link SocketPermission} to their application. By asserting that these
41  * operations are privileged, the operations can proceed even if some code in the calling chain lacks the appropriate
42  * {@link SocketPermission}.
43  */

44 public final class SocketUtils {
45
46     private static final Enumeration<Object> EMPTY = Collections.enumeration(Collections.emptyList());
47
48     private SocketUtils() {
49     }
50
51     @SuppressWarnings("unchecked")
52     private static <T> Enumeration<T> empty() {
53         return (Enumeration<T>) EMPTY;
54     }
55
56     public static void connect(final Socket socket, final SocketAddress remoteAddress, final int timeout)
57             throws IOException {
58         try {
59             AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
60                 @Override
61                 public Void run() throws IOException {
62                     socket.connect(remoteAddress, timeout);
63                     return null;
64                 }
65             });
66         } catch (PrivilegedActionException e) {
67             throw (IOException) e.getCause();
68         }
69     }
70
71     public static void bind(final Socket socket, final SocketAddress bindpoint) throws IOException {
72         try {
73             AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
74                 @Override
75                 public Void run() throws IOException {
76                     socket.bind(bindpoint);
77                     return null;
78                 }
79             });
80         } catch (PrivilegedActionException e) {
81             throw (IOException) e.getCause();
82         }
83     }
84
85     public static boolean connect(final SocketChannel socketChannel, final SocketAddress remoteAddress)
86             throws IOException {
87         try {
88             return AccessController.doPrivileged(new PrivilegedExceptionAction<Boolean>() {
89                 @Override
90                 public Boolean run() throws IOException {
91                     return socketChannel.connect(remoteAddress);
92                 }
93             });
94         } catch (PrivilegedActionException e) {
95             throw (IOException) e.getCause();
96         }
97     }
98
99     @SuppressJava6Requirement(reason = "Usage guarded by java version check")
100     public static void bind(final SocketChannel socketChannel, final SocketAddress address) throws IOException {
101         try {
102             AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
103                 @Override
104                 public Void run() throws IOException {
105                     socketChannel.bind(address);
106                     return null;
107                 }
108             });
109         } catch (PrivilegedActionException e) {
110             throw (IOException) e.getCause();
111         }
112     }
113
114     public static SocketChannel accept(final ServerSocketChannel serverSocketChannel) throws IOException {
115         try {
116             return AccessController.doPrivileged(new PrivilegedExceptionAction<SocketChannel>() {
117                 @Override
118                 public SocketChannel run() throws IOException {
119                     return serverSocketChannel.accept();
120                 }
121             });
122         } catch (PrivilegedActionException e) {
123             throw (IOException) e.getCause();
124         }
125     }
126
127     @SuppressJava6Requirement(reason = "Usage guarded by java version check")
128     public static void bind(final DatagramChannel networkChannel, final SocketAddress address) throws IOException {
129         try {
130             AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
131                 @Override
132                 public Void run() throws IOException {
133                     networkChannel.bind(address);
134                     return null;
135                 }
136             });
137         } catch (PrivilegedActionException e) {
138             throw (IOException) e.getCause();
139         }
140     }
141
142     public static SocketAddress localSocketAddress(final ServerSocket socket) {
143         return AccessController.doPrivileged(new PrivilegedAction<SocketAddress>() {
144             @Override
145             public SocketAddress run() {
146                 return socket.getLocalSocketAddress();
147             }
148         });
149     }
150
151     public static InetAddress addressByName(final String hostname) throws UnknownHostException {
152         try {
153             return AccessController.doPrivileged(new PrivilegedExceptionAction<InetAddress>() {
154                 @Override
155                 public InetAddress run() throws UnknownHostException {
156                     return InetAddress.getByName(hostname);
157                 }
158             });
159         } catch (PrivilegedActionException e) {
160             throw (UnknownHostException) e.getCause();
161         }
162     }
163
164     public static InetAddress[] allAddressesByName(final String hostname) throws UnknownHostException {
165         try {
166             return AccessController.doPrivileged(new PrivilegedExceptionAction<InetAddress[]>() {
167                 @Override
168                 public InetAddress[] run() throws UnknownHostException {
169                     return InetAddress.getAllByName(hostname);
170                 }
171             });
172         } catch (PrivilegedActionException e) {
173             throw (UnknownHostException) e.getCause();
174         }
175     }
176
177     public static InetSocketAddress socketAddress(final String hostname, final int port) {
178         return AccessController.doPrivileged(new PrivilegedAction<InetSocketAddress>() {
179             @Override
180             public InetSocketAddress run() {
181                 return new InetSocketAddress(hostname, port);
182             }
183         });
184     }
185
186     public static Enumeration<InetAddress> addressesFromNetworkInterface(final NetworkInterface intf) {
187         Enumeration<InetAddress> addresses =
188                 AccessController.doPrivileged(new PrivilegedAction<Enumeration<InetAddress>>() {
189             @Override
190             public Enumeration<InetAddress> run() {
191                 return intf.getInetAddresses();
192             }
193         });
194         // Android seems to sometimes return null even if this is not a valid return value by the api docs.
195         // Just return an empty Enumeration in this case.
196         // See https://github.com/netty/netty/issues/10045
197         if (addresses == null) {
198             return empty();
199         }
200         return addresses;
201     }
202
203     @SuppressJava6Requirement(reason = "Usage guarded by java version check")
204     public static InetAddress loopbackAddress() {
205         return AccessController.doPrivileged(new PrivilegedAction<InetAddress>() {
206             @Override
207             public InetAddress run() {
208                 if (PlatformDependent.javaVersion() >= 7) {
209                     return InetAddress.getLoopbackAddress();
210                 }
211                 try {
212                     return InetAddress.getByName(null);
213                 } catch (UnknownHostException e) {
214                     throw new IllegalStateException(e);
215                 }
216             }
217         });
218     }
219
220     public static byte[] hardwareAddressFromNetworkInterface(final NetworkInterface intf) throws SocketException {
221         try {
222             return AccessController.doPrivileged(new PrivilegedExceptionAction<byte[]>() {
223                 @Override
224                 public byte[] run() throws SocketException {
225                     return intf.getHardwareAddress();
226                 }
227             });
228         } catch (PrivilegedActionException e) {
229             throw (SocketException) e.getCause();
230         }
231     }
232 }
233