001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.harmony.luni.net;
019:
020: import java.io.FileDescriptor;
021: import java.io.IOException;
022: import java.io.InterruptedIOException;
023: import java.net.DatagramPacket;
024: import java.net.DatagramSocketImpl;
025: import java.net.InetAddress;
026: import java.net.InetSocketAddress;
027: import java.net.NetworkInterface;
028: import java.net.SocketAddress;
029: import java.net.SocketException;
030: import java.net.SocketOptions;
031: import java.net.SocketTimeoutException;
032: import java.net.UnknownHostException;
033: import java.security.AccessController;
034:
035: import org.apache.harmony.luni.platform.INetworkSystem;
036: import org.apache.harmony.luni.platform.Platform;
037: import org.apache.harmony.luni.util.Msg;
038: import org.apache.harmony.luni.util.PriviAction;
039:
040: /**
041: * The default, concrete instance of datagram sockets. This class does not
042: * support security checks. Alternative types of DatagramSocketImpl's may be
043: * used by setting the <code>impl.prefix</code> system property.
044: */
045: class PlainDatagramSocketImpl extends DatagramSocketImpl {
046:
047: static final int MULTICAST_IF = 1;
048:
049: static final int MULTICAST_TTL = 2;
050:
051: static final int TCP_NODELAY = 4;
052:
053: static final int FLAG_SHUTDOWN = 8;
054:
055: private final static int SO_BROADCAST = 32;
056:
057: final static int IP_MULTICAST_ADD = 19;
058:
059: final static int IP_MULTICAST_DROP = 20;
060:
061: final static int IP_MULTICAST_TTL = 17;
062:
063: /**
064: * for datagram and multicast sockets we have to set REUSEADDR and REUSEPORT
065: * when REUSEADDR is set for other types of sockets we need to just set
066: * REUSEADDR therefore we have this other option which sets both if
067: * supported by the platform. this cannot be in SOCKET_OPTIONS because since
068: * it is a public interface it ends up being public even if it is not
069: * declared public
070: */
071: static final int REUSEADDR_AND_REUSEPORT = 10001;
072:
073: private boolean bindToDevice;
074:
075: private byte[] ipaddress = { 0, 0, 0, 0 };
076:
077: private int ttl = 1;
078:
079: private INetworkSystem netImpl = Platform.getNetworkSystem();
080:
081: private volatile boolean isNativeConnected;
082:
083: public int receiveTimeout;
084:
085: public boolean streaming = true;
086:
087: public boolean shutdownInput;
088:
089: /**
090: * used to keep address to which the socket was connected to at the native
091: * level
092: */
093: private InetAddress connectedAddress;
094:
095: private int connectedPort = -1;
096:
097: /**
098: * used to store the trafficClass value which is simply returned as the
099: * value that was set. We also need it to pass it to methods that specify an
100: * address packets are going to be sent to
101: */
102: private int trafficClass;
103:
104: public PlainDatagramSocketImpl(FileDescriptor fd, int localPort) {
105: super ();
106: this .fd = fd;
107: this .localPort = localPort;
108: }
109:
110: public PlainDatagramSocketImpl() {
111: super ();
112: fd = new FileDescriptor();
113: }
114:
115: @Override
116: public void bind(int port, InetAddress addr) throws SocketException {
117: String prop = AccessController
118: .doPrivileged(new PriviAction<String>("bindToDevice")); //$NON-NLS-1$
119: boolean useBindToDevice = prop != null
120: && prop.toLowerCase().equals("true"); //$NON-NLS-1$
121: bindToDevice = netImpl.bind2(fd, port, useBindToDevice, addr);
122: if (0 != port) {
123: localPort = port;
124: } else {
125: localPort = netImpl.getSocketLocalPort(fd, NetUtil
126: .preferIPv6Addresses());
127: }
128:
129: try {
130: // Ignore failures
131: setOption(SO_BROADCAST, Boolean.TRUE);
132: } catch (IOException e) {
133: }
134: }
135:
136: @Override
137: public void close() {
138: synchronized (fd) {
139: if (fd.valid()) {
140: try {
141: netImpl.socketClose(fd);
142: } catch (IOException e) {
143: }
144: fd = new FileDescriptor();
145: }
146: }
147: }
148:
149: @Override
150: public void create() throws SocketException {
151: netImpl.createDatagramSocket(fd, NetUtil.preferIPv4Stack());
152: }
153:
154: @Override
155: protected void finalize() {
156: close();
157: }
158:
159: @Override
160: public Object getOption(int optID) throws SocketException {
161: if (optID == SocketOptions.SO_TIMEOUT) {
162: return Integer.valueOf(receiveTimeout);
163: } else if (optID == SocketOptions.IP_TOS) {
164: return Integer.valueOf(trafficClass);
165: } else {
166: // Call the native first so there will be
167: // an exception if the socket if closed.
168: Object result = netImpl.getSocketOption(fd, optID);
169: if (optID == SocketOptions.IP_MULTICAST_IF
170: && (netImpl.getSocketFlags() & MULTICAST_IF) != 0) {
171: try {
172: return InetAddress.getByAddress(ipaddress);
173: } catch (UnknownHostException e) {
174: return null;
175: }
176: }
177: return result;
178: }
179: }
180:
181: @Override
182: public int getTimeToLive() throws IOException {
183: // Call the native first so there will be an exception if the socket if
184: // closed.
185: int result = (((Byte) getOption(IP_MULTICAST_TTL)).byteValue()) & 0xFF;
186: if ((netImpl.getSocketFlags() & MULTICAST_TTL) != 0) {
187: return ttl;
188: }
189: return result;
190: }
191:
192: @Override
193: public byte getTTL() throws IOException {
194: // Call the native first so there will be an exception if the socket if
195: // closed.
196: byte result = ((Byte) getOption(IP_MULTICAST_TTL)).byteValue();
197: if ((netImpl.getSocketFlags() & MULTICAST_TTL) != 0) {
198: return (byte) ttl;
199: }
200: return result;
201: }
202:
203: @Override
204: public void join(InetAddress addr) throws IOException {
205: setOption(IP_MULTICAST_ADD, new GenericIPMreq(addr));
206: }
207:
208: @Override
209: public void joinGroup(SocketAddress addr,
210: NetworkInterface netInterface) throws IOException {
211: if (addr instanceof InetSocketAddress) {
212: InetAddress groupAddr = ((InetSocketAddress) addr)
213: .getAddress();
214: setOption(IP_MULTICAST_ADD, new GenericIPMreq(groupAddr,
215: netInterface));
216: }
217: }
218:
219: @Override
220: public void leave(InetAddress addr) throws IOException {
221: setOption(IP_MULTICAST_DROP, new GenericIPMreq(addr));
222: }
223:
224: @Override
225: public void leaveGroup(SocketAddress addr,
226: NetworkInterface netInterface) throws IOException {
227: if (addr instanceof InetSocketAddress) {
228: InetAddress groupAddr = ((InetSocketAddress) addr)
229: .getAddress();
230: setOption(IP_MULTICAST_DROP, new GenericIPMreq(groupAddr,
231: netInterface));
232: }
233: }
234:
235: @Override
236: protected int peek(InetAddress sender) throws IOException {
237: if (isNativeConnected) {
238: /*
239: * in this case we know the port and address from which the data
240: * must have be been received as the socket is connected. However,
241: * we still need to do the receive in order to know that there was
242: * data received. We use a short buffer as we don't actually need
243: * the packet, only the knowledge that it is there
244: */
245: byte[] storageArray = new byte[10];
246: DatagramPacket pack = new DatagramPacket(storageArray,
247: storageArray.length);
248: netImpl.recvConnectedDatagram(fd, pack, pack.getData(),
249: pack.getOffset(), pack.getLength(), receiveTimeout,
250: true); // peek
251: // to set the sender ,we now use a native function
252: // sender.ipaddress = connectedAddress.getAddress();
253: netImpl.setInetAddress(sender, connectedAddress
254: .getAddress());
255: return connectedPort;
256: }
257: return netImpl.peekDatagram(fd, sender, receiveTimeout);
258: }
259:
260: @Override
261: public void receive(DatagramPacket pack) throws java.io.IOException {
262: try {
263: if (isNativeConnected) {
264: // do not peek
265: netImpl.recvConnectedDatagram(fd, pack, pack.getData(),
266: pack.getOffset(), pack.getLength(),
267: receiveTimeout, false);
268: updatePacketRecvAddress(pack);
269: } else {
270: // receiveDatagramImpl2
271: netImpl.receiveDatagram(fd, pack, pack.getData(), pack
272: .getOffset(), pack.getLength(), receiveTimeout,
273: false);
274: }
275: } catch (InterruptedIOException e) {
276: throw new SocketTimeoutException(e.getMessage());
277: }
278: }
279:
280: @Override
281: public void send(DatagramPacket packet) throws IOException {
282:
283: if (isNativeConnected) {
284: netImpl.sendConnectedDatagram(fd, packet.getData(), packet
285: .getOffset(), packet.getLength(), bindToDevice);
286: } else {
287: // sendDatagramImpl2
288: netImpl.sendDatagram(fd, packet.getData(), packet
289: .getOffset(), packet.getLength(), packet.getPort(),
290: bindToDevice, trafficClass, packet.getAddress());
291: }
292: }
293:
294: /**
295: * Set the nominated socket option. As the timeouts are not set as options
296: * in the IP stack, the value is stored in an instance field.
297: *
298: * @throws SocketException thrown if the option value is unsupported or
299: * invalid
300: */
301: @Override
302: public void setOption(int optID, Object val) throws SocketException {
303: /*
304: * for datagram sockets on some platforms we have to set both the
305: * REUSEADDR AND REUSEPORT so for REUSEADDR set this option option which
306: * tells the VM to set the two values as appropriate for the platform
307: */
308: if (optID == SocketOptions.SO_REUSEADDR) {
309: optID = REUSEADDR_AND_REUSEPORT;
310: }
311:
312: if (optID == SocketOptions.SO_TIMEOUT) {
313: receiveTimeout = ((Integer) val).intValue();
314: } else {
315: int flags = netImpl.getSocketFlags();
316: try {
317: netImpl.setSocketOption(fd, optID | (flags << 16), val);
318: } catch (SocketException e) {
319: // we don't throw an exception for IP_TOS even if the platform
320: // won't let us set the requested value
321: if (optID != SocketOptions.IP_TOS) {
322: throw e;
323: }
324: }
325: if (optID == SocketOptions.IP_MULTICAST_IF
326: && (flags & MULTICAST_IF) != 0) {
327: InetAddress inet = (InetAddress) val;
328: if (NetUtil.bytesToInt(inet.getAddress(), 0) == 0
329: || inet.isLoopbackAddress()) {
330: ipaddress = ((InetAddress) val).getAddress();
331: } else {
332: InetAddress local = null;
333: try {
334: local = InetAddress.getLocalHost();
335: } catch (UnknownHostException e) {
336: throw new SocketException("getLocalHost(): "
337: + e.toString());
338: }
339: if (inet.equals(local)) {
340: ipaddress = ((InetAddress) val).getAddress();
341: } else {
342: throw new SocketException(val
343: + " != getLocalHost(): " + local);
344: }
345: }
346: }
347: /*
348: * save this value as it is actually used differently for IPv4 and
349: * IPv6 so we cannot get the value using the getOption. The option
350: * is actually only set for IPv4 and a masked version of the value
351: * will be set as only a subset of the values are allowed on the
352: * socket. Therefore we need to retain it to return the value that
353: * was set. We also need the value to be passed into a number of
354: * natives so that it can be used properly with IPv6
355: */
356: if (optID == SocketOptions.IP_TOS) {
357: trafficClass = ((Integer) val).intValue();
358: }
359: }
360: }
361:
362: @Override
363: public void setTimeToLive(int ttl) throws java.io.IOException {
364: setOption(IP_MULTICAST_TTL, Byte.valueOf((byte) (ttl & 0xFF)));
365: if ((netImpl.getSocketFlags() & MULTICAST_TTL) != 0) {
366: this .ttl = ttl;
367: }
368: }
369:
370: @Override
371: public void setTTL(byte ttl) throws java.io.IOException {
372: setOption(IP_MULTICAST_TTL, Byte.valueOf(ttl));
373: if ((netImpl.getSocketFlags() & MULTICAST_TTL) != 0) {
374: this .ttl = ttl;
375: }
376: }
377:
378: @Override
379: public void connect(InetAddress inetAddr, int port)
380: throws SocketException {
381:
382: // connectDatagram impl2
383: netImpl.connectDatagram(fd, port, trafficClass, inetAddr);
384:
385: // if we get here then we are connected at the native level
386: try {
387: connectedAddress = InetAddress.getByAddress(inetAddr
388: .getAddress());
389: } catch (UnknownHostException e) {
390: // this is never expected to happen as we should not have gotten
391: // here if the address is not resolvable
392: throw new SocketException(Msg.getString(
393: "K0317", inetAddr.getHostName())); //$NON-NLS-1$
394: }
395: connectedPort = port;
396: isNativeConnected = true;
397: }
398:
399: @Override
400: public void disconnect() {
401: try {
402: netImpl.disconnectDatagram(fd);
403: } catch (Exception e) {
404: // there is currently no way to return an error so just eat any
405: // exception
406: }
407: connectedPort = -1;
408: connectedAddress = null;
409: isNativeConnected = false;
410: }
411:
412: @Override
413: public int peekData(DatagramPacket pack) throws IOException {
414: try {
415: if (isNativeConnected) {
416: netImpl.recvConnectedDatagram(fd, pack, pack.getData(),
417: pack.getOffset(), pack.getLength(),
418: receiveTimeout, true); // peek
419: updatePacketRecvAddress(pack);
420: } else {
421: // receiveDatagram 2
422: netImpl.receiveDatagram(fd, pack, pack.getData(), pack
423: .getOffset(), pack.getLength(), receiveTimeout,
424: true); // peek
425: }
426: } catch (InterruptedIOException e) {
427: throw new SocketTimeoutException(e.toString());
428: }
429: return pack.getPort();
430: }
431:
432: /**
433: * Set the received address and port in the packet. We do this when the
434: * Datagram socket is connected at the native level and the
435: * recvConnnectedDatagramImpl does not update the packet with address from
436: * which the packet was received
437: *
438: * @param packet
439: * the packet to be updated
440: */
441: private void updatePacketRecvAddress(DatagramPacket packet) {
442: packet.setAddress(connectedAddress);
443: packet.setPort(connectedPort);
444: }
445: }
|