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 java.net;
019:
020: import java.io.IOException;
021: import java.util.Enumeration;
022:
023: import org.apache.harmony.luni.net.SocketImplProvider;
024: import org.apache.harmony.luni.util.Msg;
025:
026: /**
027: * This class models a multicast socket for sending & receiving datagram packets
028: * to a multicast group.
029: *
030: * @see DatagramSocket
031: */
032: public class MulticastSocket extends DatagramSocket {
033:
034: final static int SO_REUSEPORT = 512;
035:
036: private InetAddress interfaceSet;
037:
038: /**
039: * Constructs a multicast socket, bound to any available port on the
040: * localhost.
041: *
042: * @throws IOException
043: * if a problem occurs creating or binding the socket
044: */
045: public MulticastSocket() throws IOException {
046: super ();
047: setReuseAddress(true);
048: }
049:
050: /**
051: * Answers a multicast socket, bound to the nominated port on the localhost.
052: *
053: * @param aPort
054: * the port to bind on the localhost
055: *
056: * @throws IOException
057: * if a problem occurs creating or binding the socket
058: */
059: public MulticastSocket(int aPort) throws IOException {
060: super (aPort);
061: setReuseAddress(true);
062: }
063:
064: /**
065: * Answer the network address used by the socket. This is useful on
066: * multi-homed machines.
067: *
068: * @return java.net.InetAddress the network address
069: * @exception java.net.SocketException
070: * The exception thrown while getting the address
071: */
072: public InetAddress getInterface() throws SocketException {
073: checkClosedAndBind(false);
074: if (interfaceSet == null) {
075: InetAddress ipvXaddress = (InetAddress) impl
076: .getOption(SocketOptions.IP_MULTICAST_IF);
077: if (ipvXaddress.isAnyLocalAddress()) {
078: // the address was not set at the IPV4 level so check the IPV6
079: // level
080: NetworkInterface theInterface = getNetworkInterface();
081: if (theInterface != null) {
082: Enumeration<InetAddress> addresses = theInterface
083: .getInetAddresses();
084: if (addresses != null) {
085: while (addresses.hasMoreElements()) {
086: InetAddress nextAddress = addresses
087: .nextElement();
088: if (nextAddress instanceof Inet6Address) {
089: return nextAddress;
090: }
091: }
092: }
093: }
094: }
095: return ipvXaddress;
096: }
097: return interfaceSet;
098: }
099:
100: /**
101: * Answer the network interface used by the socket. This is useful on
102: * multi-homed machines.
103: *
104: * @return java.net.NetworkInterface the network address
105: * @exception java.net.SocketException
106: * The exception thrown while getting the address
107: *
108: * @since 1.4
109: */
110: public NetworkInterface getNetworkInterface()
111: throws SocketException {
112: checkClosedAndBind(false);
113:
114: // check if it is set at the IPV6 level. If so then use that. Otherwise
115: // do it at the IPV4 level
116: Integer theIndex = Integer.valueOf(0);
117: try {
118: theIndex = (Integer) impl
119: .getOption(SocketOptions.IP_MULTICAST_IF2);
120: } catch (SocketException e) {
121: // we may get an exception if IPV6 is not enabled.
122: }
123:
124: if (theIndex.intValue() != 0) {
125: Enumeration<NetworkInterface> theInterfaces = NetworkInterface
126: .getNetworkInterfaces();
127: while (theInterfaces.hasMoreElements()) {
128: NetworkInterface nextInterface = theInterfaces
129: .nextElement();
130: if (nextInterface.getIndex() == theIndex.intValue()) {
131: return nextInterface;
132: }
133: }
134: }
135:
136: // ok it was not set at the IPV6 level so try at the IPV4 level
137: InetAddress theAddress = (InetAddress) impl
138: .getOption(SocketOptions.IP_MULTICAST_IF);
139: if (theAddress != null) {
140: if (!theAddress.isAnyLocalAddress()) {
141: return NetworkInterface.getByInetAddress(theAddress);
142: }
143:
144: // not set as we got the any address so return a dummy network
145: // interface with only the any address. We do this to be
146: // compatible
147: InetAddress theAddresses[] = new InetAddress[1];
148: if ((Socket.preferIPv4Stack() == false)
149: && (InetAddress.preferIPv6Addresses() == true)) {
150: theAddresses[0] = Inet6Address.ANY;
151: } else {
152: theAddresses[0] = InetAddress.ANY;
153: }
154: return new NetworkInterface(null, null, theAddresses,
155: NetworkInterface.UNSET_INTERFACE_INDEX);
156: }
157:
158: // ok not set at all so return null
159: return null;
160: }
161:
162: /**
163: * Answer the time-to-live (TTL) for multicast packets sent on this socket.
164: *
165: * @return java.net.InetAddress
166: * @exception IOException
167: * The exception description.
168: */
169: public int getTimeToLive() throws IOException {
170: checkClosedAndBind(false);
171: return impl.getTimeToLive();
172: }
173:
174: /**
175: * Answer the time-to-live (TTL) for multicast packets sent on this socket.
176: *
177: * @return java.net.InetAddress
178: * @exception IOException
179: * The exception description.
180: * @deprecated Replaced by getTimeToLive
181: * @see #getTimeToLive()
182: */
183: @SuppressWarnings("deprecation")
184: @Deprecated
185: public byte getTTL() throws IOException {
186: checkClosedAndBind(false);
187: return impl.getTTL();
188: }
189:
190: /**
191: * Add this socket to the multicast group. A socket must joint a group
192: * before data may be received. A socket may be a member of multiple groups
193: * but may join any group once.
194: *
195: * @param groupAddr
196: * the multicast group to be joined
197: * @exception IOException
198: * may be thrown while joining a group
199: */
200: public void joinGroup(InetAddress groupAddr) throws IOException {
201: checkClosedAndBind(false);
202: if (!groupAddr.isMulticastAddress()) {
203: throw new IOException(Msg.getString("K0039")); //$NON-NLS-1$
204: }
205: SecurityManager security = System.getSecurityManager();
206: if (security != null) {
207: security.checkMulticast(groupAddr);
208: }
209: impl.join(groupAddr);
210: }
211:
212: /**
213: * Add this socket to the multicast group. A socket must join a group before
214: * data may be received. A socket may be a member of multiple groups but may
215: * join any group once.
216: *
217: * @param groupAddress
218: * the multicast group to be joined
219: * @param netInterface
220: * the network interface on which the addresses should be dropped
221: * @exception IOException
222: * will be thrown if address is not a multicast address
223: * @exception java.lang.SecurityException
224: * will be thrown if caller is not authorized to join group
225: * @exception java.lang.IllegalArgumentException
226: * will be through if groupAddr is null
227: *
228: * @since 1.4
229: */
230: public void joinGroup(SocketAddress groupAddress,
231: NetworkInterface netInterface) throws IOException {
232: checkClosedAndBind(false);
233: if (null == groupAddress) {
234: throw new IllegalArgumentException(Msg.getString("K0318")); //$NON-NLS-1$
235: }
236:
237: if ((netInterface != null)
238: && (netInterface.getFirstAddress() == null)) {
239: // this is ok if we could set it at the
240: throw new SocketException(Msg.getString("K0335")); //$NON-NLS-1$
241: }
242:
243: if (!(groupAddress instanceof InetSocketAddress)) {
244: throw new IllegalArgumentException(Msg.getString(
245: "K0316", groupAddress.getClass())); //$NON-NLS-1$
246: }
247:
248: InetAddress groupAddr = ((InetSocketAddress) groupAddress)
249: .getAddress();
250:
251: if (groupAddr == null) {
252: throw new SocketException(Msg.getString("K0331")); //$NON-NLS-1$
253: }
254:
255: if (!groupAddr.isMulticastAddress()) {
256: throw new IOException(Msg.getString("K0039")); //$NON-NLS-1$
257: }
258:
259: SecurityManager security = System.getSecurityManager();
260: if (security != null) {
261: security.checkMulticast(groupAddr);
262: }
263: impl.joinGroup(groupAddress, netInterface);
264: }
265:
266: /**
267: * Remove the socket from the multicast group.
268: *
269: * @param groupAddr
270: * the multicast group to be left
271: * @exception IOException
272: * will be thrown if address is not a multicast address
273: * @exception java.lang.SecurityException
274: * will be thrown if caller is not authorized to join group
275: * @exception java.lang.IllegalArgumentException
276: * will be through if groupAddr is null
277: */
278: public void leaveGroup(InetAddress groupAddr) throws IOException {
279: checkClosedAndBind(false);
280: if (!groupAddr.isMulticastAddress()) {
281: throw new IOException(Msg.getString("K003a")); //$NON-NLS-1$
282: }
283: SecurityManager security = System.getSecurityManager();
284: if (security != null) {
285: security.checkMulticast(groupAddr);
286: }
287: impl.leave(groupAddr);
288: }
289:
290: /**
291: * Remove the socket from the multicast group.
292: *
293: * @param groupAddress
294: * the multicast group to be left
295: * @param netInterface
296: * the network interface on which the addresses should be dropped
297: * @exception IOException
298: * will be thrown if address is not a multicast address
299: * @exception java.lang.SecurityException
300: * will be thrown if caller is not authorized to join group
301: * @exception java.lang.IllegalArgumentException
302: * will be through if groupAddr is null
303: *
304: * @since 1.4
305: */
306: public void leaveGroup(SocketAddress groupAddress,
307: NetworkInterface netInterface) throws IOException {
308: checkClosedAndBind(false);
309: if (null == groupAddress) {
310: throw new IllegalArgumentException(Msg.getString("K0318")); //$NON-NLS-1$
311: }
312:
313: if ((netInterface != null)
314: && (netInterface.getFirstAddress() == null)) {
315: // this is ok if we could set it at the
316: throw new SocketException(Msg.getString("K0335")); //$NON-NLS-1$
317: }
318:
319: if (!(groupAddress instanceof InetSocketAddress)) {
320: throw new IllegalArgumentException(Msg.getString(
321: "K0316", groupAddress.getClass())); //$NON-NLS-1$
322: }
323:
324: InetAddress groupAddr = ((InetSocketAddress) groupAddress)
325: .getAddress();
326:
327: if (groupAddr == null) {
328: throw new SocketException(Msg.getString("K0331")); //$NON-NLS-1$
329: }
330:
331: if (!groupAddr.isMulticastAddress()) {
332: throw new IOException(Msg.getString("K003a")); //$NON-NLS-1$
333: }
334: SecurityManager security = System.getSecurityManager();
335: if (security != null) {
336: security.checkMulticast(groupAddr);
337: }
338: impl.leaveGroup(groupAddress, netInterface);
339: }
340:
341: /**
342: * Send the packet on this socket. The packet must satisfy the security
343: * policy before it may be sent.
344: *
345: * @param pack
346: * the DatagramPacket to send
347: * @param ttl
348: * the TTL setting for this transmission, overriding the socket
349: * default
350: *
351: * @exception IOException
352: * If a send error occurs.
353: *
354: * @deprecated use MulticastSocket#setTimeToLive
355: */
356: @SuppressWarnings("deprecation")
357: @Deprecated
358: public void send(DatagramPacket pack, byte ttl) throws IOException {
359: checkClosedAndBind(false);
360: InetAddress packAddr = pack.getAddress();
361: SecurityManager security = System.getSecurityManager();
362: if (security != null) {
363: if (packAddr.isMulticastAddress()) {
364: security.checkMulticast(packAddr, ttl);
365: } else {
366: security.checkConnect(packAddr.getHostName(), pack
367: .getPort());
368: }
369: }
370: int currTTL = getTimeToLive();
371: if (packAddr.isMulticastAddress() && (byte) currTTL != ttl) {
372: try {
373: setTimeToLive(ttl & 0xff);
374: impl.send(pack);
375: } finally {
376: setTimeToLive(currTTL);
377: }
378: } else {
379: impl.send(pack);
380: }
381: }
382:
383: /**
384: * Set the network address used by the socket. This is useful on multi-homed
385: * machines.
386: *
387: * @param addr
388: * java.net.InetAddress the interface network address
389: * @exception java.net.SocketException
390: * the exception may be thrown while setting the address
391: */
392: public void setInterface(InetAddress addr) throws SocketException {
393: checkClosedAndBind(false);
394: if (addr == null) {
395: throw new NullPointerException();
396: }
397: if (addr.isAnyLocalAddress()) {
398: impl.setOption(SocketOptions.IP_MULTICAST_IF,
399: InetAddress.ANY);
400: } else if (addr instanceof Inet4Address) {
401: impl.setOption(SocketOptions.IP_MULTICAST_IF, addr);
402: // keep the address used to do the set as we must return the same
403: // value and for IPv6 we may not be able to get it back uniquely
404: interfaceSet = addr;
405: }
406:
407: /*
408: * now we should also make sure this works for IPV6 get the network
409: * interface for the address and set the interface using its index
410: * however if IPV6 is not enabled then we may get an exception. if IPV6
411: * is not enabled
412: */
413: NetworkInterface theInterface = NetworkInterface
414: .getByInetAddress(addr);
415: if ((theInterface != null) && (theInterface.getIndex() != 0)) {
416: try {
417: impl.setOption(SocketOptions.IP_MULTICAST_IF2, Integer
418: .valueOf(theInterface.getIndex()));
419: } catch (SocketException e) {
420: // Ignored
421: }
422: } else if (addr.isAnyLocalAddress()) {
423: try {
424: impl.setOption(SocketOptions.IP_MULTICAST_IF2, Integer
425: .valueOf(0));
426: } catch (SocketException e) {
427: // Ignored
428: }
429: } else if (addr instanceof Inet6Address) {
430: throw new SocketException(Msg.getString("K0338")); //$NON-NLS-1$
431: }
432: }
433:
434: /**
435: * Set the network interface used by the socket. This is useful on
436: * multi-homed machines.
437: *
438: * @param netInterface
439: * NetworkInterface the interface to be used
440: * @exception java.net.SocketException
441: * the exception may be thrown while setting the address
442: *
443: * @since 1.4
444: */
445: public void setNetworkInterface(NetworkInterface netInterface)
446: throws SocketException {
447:
448: checkClosedAndBind(false);
449:
450: if (netInterface == null) {
451: // throw a socket exception indicating that we do not support this
452: throw new SocketException(Msg.getString("K0334")); //$NON-NLS-1$
453: }
454:
455: InetAddress firstAddress = netInterface.getFirstAddress();
456: if (firstAddress == null) {
457: // this is ok if we could set it at the
458: throw new SocketException(Msg.getString("K0335")); //$NON-NLS-1$
459: }
460:
461: if (netInterface.getIndex() == NetworkInterface.UNSET_INTERFACE_INDEX) {
462: // set the address using IP_MULTICAST_IF to make sure this
463: // works for both IPV4 and IPV6
464: impl.setOption(SocketOptions.IP_MULTICAST_IF,
465: InetAddress.ANY);
466:
467: try {
468: // we have the index so now we pass set the interface
469: // using IP_MULTICAST_IF2. This is what is used to set
470: // the interface on systems which support IPV6
471: impl.setOption(SocketOptions.IP_MULTICAST_IF2, Integer
472: .valueOf(NetworkInterface.NO_INTERFACE_INDEX));
473: } catch (SocketException e) {
474: // for now just do this, -- could be narrowed?
475: }
476: }
477:
478: /*
479: * Now try to set using IPV4 way. However, if interface passed in has no
480: * IP addresses associated with it then we cannot do it. first we have
481: * to make sure there is an IPV4 address that we can use to call set
482: * interface otherwise we will not set it
483: */
484: Enumeration<InetAddress> theAddresses = netInterface
485: .getInetAddresses();
486: boolean found = false;
487: firstAddress = null;
488: while ((theAddresses.hasMoreElements()) && (found != true)) {
489: InetAddress theAddress = theAddresses.nextElement();
490: if (theAddress instanceof Inet4Address) {
491: firstAddress = theAddress;
492: found = true;
493: }
494: }
495: if (netInterface.getIndex() == NetworkInterface.NO_INTERFACE_INDEX) {
496: // the system does not support IPV6 and does not provide
497: // indexes for the network interfaces. Just pass in the
498: // first address for the network interface
499: if (firstAddress != null) {
500: impl.setOption(SocketOptions.IP_MULTICAST_IF,
501: firstAddress);
502: } else {
503: /*
504: * we should never get here as there should not be any network
505: * interfaces which have no IPV4 address and which does not have
506: * the network interface index not set correctly
507: */
508: throw new SocketException(Msg.getString("K0335")); //$NON-NLS-1$
509: }
510: } else {
511: // set the address using IP_MULTICAST_IF to make sure this
512: // works for both IPV4 and IPV6
513: if (firstAddress != null) {
514: impl.setOption(SocketOptions.IP_MULTICAST_IF,
515: firstAddress);
516: }
517:
518: try {
519: // we have the index so now we pass set the interface
520: // using IP_MULTICAST_IF2. This is what is used to set
521: // the interface on systems which support IPV6
522: impl.setOption(SocketOptions.IP_MULTICAST_IF2, Integer
523: .valueOf(netInterface.getIndex()));
524: } catch (SocketException e) {
525: // for now just do this -- could be narrowed?
526: }
527: }
528:
529: interfaceSet = null;
530: }
531:
532: /**
533: * Set the time-to-live (TTL) for multicast packets sent on this socket.
534: *
535: * @param ttl
536: * the time-to-live, 0<=ttl<= 255
537: * @exception IOException
538: * The exception thrown while setting the TTL
539: */
540: public void setTimeToLive(int ttl) throws IOException {
541: checkClosedAndBind(false);
542: if (ttl < 0 || ttl > 255) {
543: throw new IllegalArgumentException(Msg.getString("K003c")); //$NON-NLS-1$
544: }
545: impl.setTimeToLive(ttl);
546: }
547:
548: /**
549: * Set the time-to-live (TTL) for multicast packets sent on this socket.
550: *
551: * @param ttl
552: * the time-to-live, 0<ttl<= 255
553: * @exception IOException
554: * The exception thrown while setting the TTL
555: * @deprecated Replaced by setTimeToLive
556: * @see #setTimeToLive(int)
557: */
558: @SuppressWarnings("deprecation")
559: @Deprecated
560: public void setTTL(byte ttl) throws IOException {
561: checkClosedAndBind(false);
562: impl.setTTL(ttl);
563: }
564:
565: @Override
566: synchronized void createSocket(int aPort, InetAddress addr)
567: throws SocketException {
568: impl = factory != null ? factory.createDatagramSocketImpl()
569: : SocketImplProvider.getMulticastSocketImpl();
570: impl.create();
571: try {
572: // the required default options are now set in the VM where they
573: // should be
574: impl.bind(aPort, addr);
575: isBound = true;
576: } catch (SocketException e) {
577: close();
578: throw e;
579: }
580: }
581:
582: /**
583: * Constructs a MulticastSocket bound to the host/port specified by the
584: * SocketAddress, or an unbound DatagramSocket if the SocketAddress is null.
585: *
586: * @param localAddr
587: * the local machine address and port to bind to
588: *
589: * @throws IllegalArgumentException
590: * if the SocketAddress is not supported
591: * @throws IOException
592: * if a problem occurs creating or binding the socket
593: *
594: * @since 1.4
595: */
596: public MulticastSocket(SocketAddress localAddr) throws IOException {
597: super (localAddr);
598: setReuseAddress(true);
599: }
600:
601: /**
602: * Get the state of the IP_MULTICAST_LOOP socket option.
603: *
604: * @return <code>true</code> if the IP_MULTICAST_LOOP is enabled,
605: * <code>false</code> otherwise.
606: *
607: * @throws SocketException
608: * if the socket is closed or the option is invalid.
609: *
610: * @since 1.4
611: */
612: public boolean getLoopbackMode() throws SocketException {
613: checkClosedAndBind(false);
614: return !((Boolean) impl
615: .getOption(SocketOptions.IP_MULTICAST_LOOP))
616: .booleanValue();
617: }
618:
619: /**
620: * Set the IP_MULTICAST_LOOP socket option.
621: *
622: * @param loop
623: * the socket IP_MULTICAST_LOOP option setting
624: *
625: * @throws SocketException
626: * if the socket is closed or the option is invalid.
627: *
628: * @since 1.4
629: */
630: public void setLoopbackMode(boolean loop) throws SocketException {
631: checkClosedAndBind(false);
632: impl.setOption(SocketOptions.IP_MULTICAST_LOOP,
633: loop ? Boolean.FALSE : Boolean.TRUE);
634: }
635: }
|