1/*
2 * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.nio.ch;
27
28import java.io.*;
29import java.net.*;
30import java.nio.*;
31import java.nio.channels.*;
32
33
34// Make a datagram-socket channel look like a datagram socket.
35//
36// The methods in this class are defined in exactly the same order as in
37// java.net.DatagramSocket so as to simplify tracking future changes to that
38// class.
39//
40
41public class DatagramSocketAdaptor
42    extends DatagramSocket
43{
44
45    // The channel being adapted
46    private final DatagramChannelImpl dc;
47
48    // Timeout "option" value for receives
49    private volatile int timeout;
50
51    // ## super will create a useless impl
52    private DatagramSocketAdaptor(DatagramChannelImpl dc) throws IOException {
53        // Invoke the DatagramSocketAdaptor(SocketAddress) constructor,
54        // passing a dummy DatagramSocketImpl object to aovid any native
55        // resource allocation in super class and invoking our bind method
56        // before the dc field is initialized.
57        super(dummyDatagramSocket);
58        this.dc = dc;
59    }
60
61    public static DatagramSocket create(DatagramChannelImpl dc) {
62        try {
63            return new DatagramSocketAdaptor(dc);
64        } catch (IOException x) {
65            throw new Error(x);
66        }
67    }
68
69    private void connectInternal(SocketAddress remote)
70        throws SocketException
71    {
72        InetSocketAddress isa = Net.asInetSocketAddress(remote);
73        int port = isa.getPort();
74        if (port < 0 || port > 0xFFFF)
75            throw new IllegalArgumentException("connect: " + port);
76        if (remote == null)
77            throw new IllegalArgumentException("connect: null address");
78        if (isClosed())
79            return;
80        try {
81            dc.connect(remote);
82        } catch (Exception x) {
83            Net.translateToSocketException(x);
84        }
85    }
86
87    public void bind(SocketAddress local) throws SocketException {
88        try {
89            if (local == null)
90                local = new InetSocketAddress(0);
91            dc.bind(local);
92        } catch (Exception x) {
93            Net.translateToSocketException(x);
94        }
95    }
96
97    public void connect(InetAddress address, int port) {
98        try {
99            connectInternal(new InetSocketAddress(address, port));
100        } catch (SocketException x) {
101            // Yes, j.n.DatagramSocket really does this
102        }
103    }
104
105    public void connect(SocketAddress remote) throws SocketException {
106        if (remote == null)
107            throw new IllegalArgumentException("Address can't be null");
108        connectInternal(remote);
109    }
110
111    public void disconnect() {
112        try {
113            dc.disconnect();
114        } catch (IOException x) {
115            throw new Error(x);
116        }
117    }
118
119    public boolean isBound() {
120        return dc.localAddress() != null;
121    }
122
123    public boolean isConnected() {
124        return dc.remoteAddress() != null;
125    }
126
127    public InetAddress getInetAddress() {
128        return (isConnected()
129                ? Net.asInetSocketAddress(dc.remoteAddress()).getAddress()
130                : null);
131    }
132
133    public int getPort() {
134        return (isConnected()
135                ? Net.asInetSocketAddress(dc.remoteAddress()).getPort()
136                : -1);
137    }
138
139    public void send(DatagramPacket p) throws IOException {
140        synchronized (dc.blockingLock()) {
141            if (!dc.isBlocking())
142                throw new IllegalBlockingModeException();
143            try {
144                synchronized (p) {
145                    ByteBuffer bb = ByteBuffer.wrap(p.getData(),
146                                                    p.getOffset(),
147                                                    p.getLength());
148                    if (dc.isConnected()) {
149                        if (p.getAddress() == null) {
150                            // Legacy DatagramSocket will send in this case
151                            // and set address and port of the packet
152                            InetSocketAddress isa = (InetSocketAddress)
153                                                    dc.remoteAddress();
154                            p.setPort(isa.getPort());
155                            p.setAddress(isa.getAddress());
156                            dc.write(bb);
157                        } else {
158                            // Target address may not match connected address
159                            dc.send(bb, p.getSocketAddress());
160                        }
161                    } else {
162                        // Not connected so address must be valid or throw
163                        dc.send(bb, p.getSocketAddress());
164                    }
165                }
166            } catch (IOException x) {
167                Net.translateException(x);
168            }
169        }
170    }
171
172    // Must hold dc.blockingLock()
173    //
174    private SocketAddress receive(ByteBuffer bb) throws IOException {
175        if (timeout == 0) {
176            return dc.receive(bb);
177        }
178
179        dc.configureBlocking(false);
180        try {
181            int n;
182            SocketAddress sender;
183            if ((sender = dc.receive(bb)) != null)
184                return sender;
185            long to = timeout;
186            for (;;) {
187                if (!dc.isOpen())
188                     throw new ClosedChannelException();
189                long st = System.currentTimeMillis();
190                int result = dc.poll(Net.POLLIN, to);
191                if (result > 0 &&
192                        ((result & Net.POLLIN) != 0)) {
193                    if ((sender = dc.receive(bb)) != null)
194                        return sender;
195                }
196                to -= System.currentTimeMillis() - st;
197                if (to <= 0)
198                    throw new SocketTimeoutException();
199
200            }
201        } finally {
202            if (dc.isOpen())
203                dc.configureBlocking(true);
204        }
205    }
206
207    public void receive(DatagramPacket p) throws IOException {
208        synchronized (dc.blockingLock()) {
209            if (!dc.isBlocking())
210                throw new IllegalBlockingModeException();
211            try {
212                synchronized (p) {
213                    ByteBuffer bb = ByteBuffer.wrap(p.getData(),
214                                                    p.getOffset(),
215                                                    p.getLength());
216                    SocketAddress sender = receive(bb);
217                    p.setSocketAddress(sender);
218                    p.setLength(bb.position() - p.getOffset());
219                }
220            } catch (IOException x) {
221                Net.translateException(x);
222            }
223        }
224    }
225
226    public InetAddress getLocalAddress() {
227        if (isClosed())
228            return null;
229        SocketAddress local = dc.localAddress();
230        if (local == null)
231            local = new InetSocketAddress(0);
232        InetAddress result = ((InetSocketAddress)local).getAddress();
233        SecurityManager sm = System.getSecurityManager();
234        if (sm != null) {
235            try {
236                sm.checkConnect(result.getHostAddress(), -1);
237            } catch (SecurityException x) {
238                return new InetSocketAddress(0).getAddress();
239            }
240        }
241        return result;
242    }
243
244    public int getLocalPort() {
245        if (isClosed())
246            return -1;
247        try {
248            SocketAddress local = dc.getLocalAddress();
249            if (local != null) {
250                return ((InetSocketAddress)local).getPort();
251            }
252        } catch (Exception x) {
253        }
254        return 0;
255    }
256
257    public void setSoTimeout(int timeout) throws SocketException {
258        this.timeout = timeout;
259    }
260
261    public int getSoTimeout() throws SocketException {
262        return timeout;
263    }
264
265    private void setBooleanOption(SocketOption<Boolean> name, boolean value)
266        throws SocketException
267    {
268        try {
269            dc.setOption(name, value);
270        } catch (IOException x) {
271            Net.translateToSocketException(x);
272        }
273    }
274
275    private void setIntOption(SocketOption<Integer> name, int value)
276        throws SocketException
277    {
278        try {
279            dc.setOption(name, value);
280        } catch (IOException x) {
281            Net.translateToSocketException(x);
282        }
283    }
284
285    private boolean getBooleanOption(SocketOption<Boolean> name) throws SocketException {
286        try {
287            return dc.getOption(name).booleanValue();
288        } catch (IOException x) {
289            Net.translateToSocketException(x);
290            return false;       // keep compiler happy
291        }
292    }
293
294    private int getIntOption(SocketOption<Integer> name) throws SocketException {
295        try {
296            return dc.getOption(name).intValue();
297        } catch (IOException x) {
298            Net.translateToSocketException(x);
299            return -1;          // keep compiler happy
300        }
301    }
302
303    public void setSendBufferSize(int size) throws SocketException {
304        if (size <= 0)
305            throw new IllegalArgumentException("Invalid send size");
306        setIntOption(StandardSocketOptions.SO_SNDBUF, size);
307    }
308
309    public int getSendBufferSize() throws SocketException {
310        return getIntOption(StandardSocketOptions.SO_SNDBUF);
311    }
312
313    public void setReceiveBufferSize(int size) throws SocketException {
314        if (size <= 0)
315            throw new IllegalArgumentException("Invalid receive size");
316        setIntOption(StandardSocketOptions.SO_RCVBUF, size);
317    }
318
319    public int getReceiveBufferSize() throws SocketException {
320        return getIntOption(StandardSocketOptions.SO_RCVBUF);
321    }
322
323    public void setReuseAddress(boolean on) throws SocketException {
324        setBooleanOption(StandardSocketOptions.SO_REUSEADDR, on);
325    }
326
327    public boolean getReuseAddress() throws SocketException {
328        return getBooleanOption(StandardSocketOptions.SO_REUSEADDR);
329
330    }
331
332    public void setBroadcast(boolean on) throws SocketException {
333        setBooleanOption(StandardSocketOptions.SO_BROADCAST, on);
334    }
335
336    public boolean getBroadcast() throws SocketException {
337        return getBooleanOption(StandardSocketOptions.SO_BROADCAST);
338    }
339
340    public void setTrafficClass(int tc) throws SocketException {
341        setIntOption(StandardSocketOptions.IP_TOS, tc);
342    }
343
344    public int getTrafficClass() throws SocketException {
345        return getIntOption(StandardSocketOptions.IP_TOS);
346    }
347
348    public void close() {
349        try {
350            dc.close();
351        } catch (IOException x) {
352            throw new Error(x);
353        }
354    }
355
356    public boolean isClosed() {
357        return !dc.isOpen();
358    }
359
360    public DatagramChannel getChannel() {
361        return dc;
362    }
363
364   /*
365    * A dummy implementation of DatagramSocketImpl that can be passed to the
366    * DatagramSocket constructor so that no native resources are allocated in
367    * super class.
368    */
369   private static final DatagramSocketImpl dummyDatagramSocket
370       = new DatagramSocketImpl()
371   {
372       protected void create() throws SocketException {}
373
374       protected void bind(int lport, InetAddress laddr) throws SocketException {}
375
376       protected void send(DatagramPacket p) throws IOException {}
377
378       protected int peek(InetAddress i) throws IOException { return 0; }
379
380       protected int peekData(DatagramPacket p) throws IOException { return 0; }
381
382       protected void receive(DatagramPacket p) throws IOException {}
383
384       @Deprecated
385       protected void setTTL(byte ttl) throws IOException {}
386
387       @Deprecated
388       protected byte getTTL() throws IOException { return 0; }
389
390       protected void setTimeToLive(int ttl) throws IOException {}
391
392       protected int getTimeToLive() throws IOException { return 0;}
393
394       protected void join(InetAddress inetaddr) throws IOException {}
395
396       protected void leave(InetAddress inetaddr) throws IOException {}
397
398       protected void joinGroup(SocketAddress mcastaddr,
399                                 NetworkInterface netIf) throws IOException {}
400
401       protected void leaveGroup(SocketAddress mcastaddr,
402                                 NetworkInterface netIf) throws IOException {}
403
404       protected void close() {}
405
406       public Object getOption(int optID) throws SocketException { return null;}
407
408       public void setOption(int optID, Object value) throws SocketException {}
409   };
410}
411