001    /* MulticastSocket.java -- Class for using multicast sockets
002       Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2007
003       Free Software Foundation, Inc.
004    
005    This file is part of GNU Classpath.
006    
007    GNU Classpath is free software; you can redistribute it and/or modify
008    it under the terms of the GNU General Public License as published by
009    the Free Software Foundation; either version 2, or (at your option)
010    any later version.
011    
012    GNU Classpath is distributed in the hope that it will be useful, but
013    WITHOUT ANY WARRANTY; without even the implied warranty of
014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015    General Public License for more details.
016    
017    You should have received a copy of the GNU General Public License
018    along with GNU Classpath; see the file COPYING.  If not, write to the
019    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
020    02110-1301 USA.
021    
022    Linking this library statically or dynamically with other modules is
023    making a combined work based on this library.  Thus, the terms and
024    conditions of the GNU General Public License cover the whole
025    combination.
026    
027    As a special exception, the copyright holders of this library give you
028    permission to link this library with independent modules to produce an
029    executable, regardless of the license terms of these independent
030    modules, and to copy and distribute the resulting executable under
031    terms of your choice, provided that you also meet, for each linked
032    independent module, the terms and conditions of the license of that
033    module.  An independent module is a module which is not derived from
034    or based on this library.  If you modify this library, you may extend
035    this exception to your version of the library, but you are not
036    obligated to do so.  If you do not wish to do so, delete this
037    exception statement from your version. */
038    
039    package java.net;
040    
041    import java.io.IOException;
042    import java.util.Enumeration;
043    
044    
045    /**
046     * Written using on-line Java Platform 1.2 API Specification, as well
047     * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
048     * Status:  Believed complete and correct.
049     */
050    /**
051     * This class models a multicast UDP socket.  A multicast address is a
052     * class D internet address (one whose most significant bits are 1110).
053     * A multicast group consists of a multicast address and a well known
054     * port number.  All members of the group listening on that address and
055     * port will receive all the broadcasts to the group.
056     * <p>
057     * Please note that applets are not allowed to use multicast sockets
058     *
059     * Written using on-line Java Platform 1.2 API Specification, as well
060     * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
061     * Status:  Believed complete and correct.
062     *
063     * @author Warren Levy (warrenl@cygnus.com)
064     * @author Aaron M. Renn (arenn@urbanophile.com) (Documentation comments)
065     * @since 1.1
066     * @date May 18, 1999.
067     */
068    public class MulticastSocket extends DatagramSocket
069    {
070      /**
071       * Create a MulticastSocket that this not bound to any address
072       *
073       * @exception IOException If an error occurs
074       * @exception SecurityException If a security manager exists and its
075       * checkListen method doesn't allow the operation
076       */
077      public MulticastSocket() throws IOException
078      {
079        this(new InetSocketAddress(0));
080      }
081    
082      /**
083       * Create a multicast socket bound to the specified port
084       *
085       * @param port The port to bind to
086       *
087       * @exception IOException If an error occurs
088       * @exception SecurityException If a security manager exists and its
089       * checkListen method doesn't allow the operation
090       */
091      public MulticastSocket(int port) throws IOException
092      {
093        this(new InetSocketAddress(port));
094      }
095    
096      /**
097       * Create a multicast socket bound to the specified SocketAddress.
098       *
099       * @param address The SocketAddress the multicast socket will be bound to
100       *
101       * @exception IOException If an error occurs
102       * @exception SecurityException If a security manager exists and its
103       * checkListen method doesn't allow the operation
104       *
105       * @since 1.4
106       */
107      public MulticastSocket(SocketAddress address) throws IOException
108      {
109        super((SocketAddress) null);
110        setReuseAddress(true);
111        if (address != null)
112          bind(address);
113      }
114    
115      /**
116       * Returns the interface being used for multicast packets
117       *
118       * @return The multicast interface
119       *
120       * @exception SocketException If an error occurs
121       */
122      public InetAddress getInterface() throws SocketException
123      {
124        if (isClosed())
125          throw new SocketException("socket is closed");
126    
127        return (InetAddress) getImpl().getOption(SocketOptions.IP_MULTICAST_IF);
128      }
129    
130      /**
131       * Returns the current value of the "Time to Live" option.  This is the
132       * number of hops a packet can make before it "expires".   This method id
133       * deprecated.  Use <code>getTimeToLive</code> instead.
134       *
135       * @return The TTL value
136       *
137       * @exception IOException If an error occurs
138       *
139       * @deprecated 1.2 Replaced by getTimeToLive()
140       *
141       * @see MulticastSocket#getTimeToLive()
142       */
143      public byte getTTL() throws IOException
144      {
145        if (isClosed())
146          throw new SocketException("socket is closed");
147    
148        // Use getTTL here rather than getTimeToLive in case we're using an impl
149        // other than the default PlainDatagramSocketImpl and it doesn't have
150        // getTimeToLive yet.
151        return getImpl().getTTL();
152      }
153    
154      /**
155       * Returns the current value of the "Time to Live" option.  This is the
156       * number of hops a packet can make before it "expires".
157       *
158       * @return The TTL value
159       *
160       * @exception IOException If an error occurs
161       *
162       * @since 1.2
163       */
164      public int getTimeToLive() throws IOException
165      {
166        if (isClosed())
167          throw new SocketException("socket is closed");
168    
169        return getImpl().getTimeToLive();
170      }
171    
172      /**
173       * Sets the interface to use for sending multicast packets.
174       *
175       * @param addr The new interface to use.
176       *
177       * @exception SocketException If an error occurs.
178       *
179       * @since 1.4
180       */
181      public void setInterface(InetAddress addr) throws SocketException
182      {
183        if (isClosed())
184          throw new SocketException("socket is closed");
185    
186        getImpl().setOption(SocketOptions.IP_MULTICAST_IF, addr);
187      }
188    
189      /**
190       * Sets the local network interface used to send multicast messages
191       *
192       * @param netIf The local network interface used to send multicast messages
193       *
194       * @exception SocketException If an error occurs
195       *
196       * @see MulticastSocket#getNetworkInterface()
197       *
198       * @since 1.4
199       */
200      public void setNetworkInterface(NetworkInterface netIf)
201        throws SocketException
202      {
203        if (isClosed())
204          throw new SocketException("socket is closed");
205        
206        InetAddress address;
207        if (netIf != null)
208          out:
209          {
210            Enumeration e = netIf.getInetAddresses();
211            if (getLocalAddress() instanceof Inet4Address)
212              {
213                // Search for a IPv4 address.
214                while (e.hasMoreElements())
215                  {
216                    address = (InetAddress) e.nextElement();
217                    if (address instanceof Inet4Address)
218                      break out;
219                  }
220                throw new SocketException("interface " + netIf.getName() + " has no IPv6 address");
221              }
222            else if (getLocalAddress() instanceof Inet6Address)
223              {
224                // Search for a IPv6 address.
225                while (e.hasMoreElements())
226                  {
227                    address = (InetAddress) e.nextElement();
228                    if (address instanceof Inet6Address)
229                      break out;
230                  }
231                throw new SocketException("interface " + netIf.getName() + " has no IPv6 address");
232              }
233            else
234              throw new SocketException("interface " + netIf.getName() + " has no suitable IP address");
235          }
236        else
237          address = InetAddress.ANY_IF;
238        
239        
240        getImpl().setOption(SocketOptions.IP_MULTICAST_IF, address);
241      }
242    
243      /**
244       * Gets the local network interface which is used to send multicast messages
245       *
246       * @return The local network interface to send multicast messages
247       *
248       * @exception SocketException If an error occurs
249       *
250       * @see MulticastSocket#setNetworkInterface(NetworkInterface netIf)
251       *
252       * @since 1.4
253       */
254      public NetworkInterface getNetworkInterface() throws SocketException
255      {
256        if (isClosed())
257          throw new SocketException("socket is closed");
258    
259        InetAddress address =
260          (InetAddress) getImpl().getOption(SocketOptions.IP_MULTICAST_IF);
261        
262        // FIXME: libgcj doesn't have createAnyInterface.
263    //     if (address.isAnyLocalAddress())
264    //       return NetworkInterface.createAnyInterface();
265        
266        NetworkInterface netIf = NetworkInterface.getByInetAddress(address);
267    
268        return netIf;
269      }
270    
271      /**
272       * Disable/Enable local loopback of multicast packets.  The option is used by
273       * the platform's networking code as a hint for setting whether multicast
274       * data will be looped back to the local socket.
275       *
276       * Because this option is a hint, applications that want to verify what
277       * loopback mode is set to should call #getLoopbackMode
278       *
279       * @param disable True to disable loopback mode
280       *
281       * @exception SocketException If an error occurs
282       *
283       * @since 1.4
284       */
285      public void setLoopbackMode(boolean disable) throws SocketException
286      {
287        if (isClosed())
288          throw new SocketException("socket is closed");
289    
290        getImpl().setOption(SocketOptions.IP_MULTICAST_LOOP,
291                            Boolean.valueOf(disable));
292      }
293    
294      /**
295       * Checks if local loopback mode is enabled
296       *
297       * @return true if loopback mode is enabled, false otherwise
298       * 
299       * @exception SocketException If an error occurs
300       *
301       * @since 1.4
302       */
303      public boolean getLoopbackMode() throws SocketException
304      {
305        if (isClosed())
306          throw new SocketException("socket is closed");
307    
308        Object buf = getImpl().getOption(SocketOptions.IP_MULTICAST_LOOP);
309    
310        if (buf instanceof Boolean)
311          return ((Boolean) buf).booleanValue();
312    
313        throw new SocketException("unexpected type");
314      }
315    
316      /**
317       * Sets the "Time to Live" value for a socket.  The value must be between
318       * 1 and 255.
319       *
320       * @param ttl The new TTL value
321       *
322       * @exception IOException If an error occurs
323       *
324       * @deprecated 1.2 Replaced by <code>setTimeToLive</code>
325       *
326       * @see MulticastSocket#setTimeToLive(int ttl)
327       */
328      public void setTTL(byte ttl) throws IOException
329      {
330        if (isClosed())
331          throw new SocketException("socket is closed");
332    
333        // Use setTTL here rather than setTimeToLive in case we're using an impl
334        // other than the default PlainDatagramSocketImpl and it doesn't have
335        // setTimeToLive yet.
336        getImpl().setTTL(ttl);
337      }
338    
339      /**
340       * Sets the "Time to Live" value for a socket.  The value must be between
341       * 0 and 255, inclusive.
342       *
343       * @param ttl The new TTL value
344       *
345       * @exception IOException If an error occurs
346       *
347       * @since 1.2
348       */
349      public void setTimeToLive(int ttl) throws IOException
350      {
351        if (isClosed())
352          throw new SocketException("socket is closed");
353    
354        if (ttl < 0 || ttl > 255)
355          throw new IllegalArgumentException("Invalid ttl: " + ttl);
356    
357        getImpl().setTimeToLive(ttl);
358      }
359    
360      /**
361       * Joins the specified multicast group.
362       *
363       * @param mcastaddr The address of the group to join
364       *
365       * @exception IOException If an error occurs
366       * @exception SecurityException If a security manager exists and its
367       * checkMulticast method doesn't allow the operation
368       */
369      public void joinGroup(InetAddress mcastaddr) throws IOException
370      {
371        if (isClosed())
372          throw new SocketException("socket is closed");
373    
374        if (! mcastaddr.isMulticastAddress())
375          throw new IOException("Not a Multicast address");
376    
377        SecurityManager s = System.getSecurityManager();
378        if (s != null)
379          s.checkMulticast(mcastaddr);
380    
381        getImpl().join(mcastaddr);
382      }
383    
384      /**
385       * Leaves the specified multicast group
386       *
387       * @param mcastaddr The address of the group to leave
388       *
389       * @exception IOException If an error occurs
390       * @exception SecurityException If a security manager exists and its
391       * checkMulticast method doesn't allow the operation
392       */
393      public void leaveGroup(InetAddress mcastaddr) throws IOException
394      {
395        if (isClosed())
396          throw new SocketException("socket is closed");
397    
398        if (! mcastaddr.isMulticastAddress())
399          throw new IOException("Not a Multicast address");
400    
401        SecurityManager s = System.getSecurityManager();
402        if (s != null)
403          s.checkMulticast(mcastaddr);
404    
405        getImpl().leave(mcastaddr);
406      }
407    
408      /**
409       * Joins the specified mulitcast group on a specified interface.
410       *
411       * @param mcastaddr The multicast address to join
412       * @param netIf The local network interface to receive the multicast
413       * messages on or null to defer the interface set by #setInterface or
414       * #setNetworkInterface
415       *
416       * @exception IOException If an error occurs
417       * @exception IllegalArgumentException If address type is not supported
418       * @exception SecurityException If a security manager exists and its
419       * checkMulticast method doesn't allow the operation
420       *
421       * @see MulticastSocket#setInterface(InetAddress addr)
422       * @see MulticastSocket#setNetworkInterface(NetworkInterface netIf)
423       *
424       * @since 1.4
425       */
426      public void joinGroup(SocketAddress mcastaddr, NetworkInterface netIf)
427        throws IOException
428      {
429        if (isClosed())
430          throw new SocketException("socket is closed");
431    
432        if (! (mcastaddr instanceof InetSocketAddress))
433          throw new IllegalArgumentException("SocketAddress type not supported");
434    
435        InetSocketAddress tmp = (InetSocketAddress) mcastaddr;
436    
437        if (! tmp.getAddress().isMulticastAddress())
438          throw new IOException("Not a Multicast address");
439    
440        SecurityManager s = System.getSecurityManager();
441        if (s != null)
442          s.checkMulticast(tmp.getAddress());
443    
444        getImpl().joinGroup(mcastaddr, netIf);
445      }
446    
447      /**
448       * Leaves the specified mulitcast group on a specified interface.
449       *
450       * @param mcastaddr The multicast address to leave
451       * @param netIf The local networki interface or null to defer to the
452       * interface set by setInterface or setNetworkInterface
453       *
454       * @exception IOException If an error occurs
455       * @exception IllegalArgumentException If address type is not supported
456       * @exception SecurityException If a security manager exists and its
457       * checkMulticast method doesn't allow the operation
458       *
459       * @see MulticastSocket#setInterface(InetAddress addr)
460       * @see MulticastSocket#setNetworkInterface(NetworkInterface netIf)
461       *
462       * @since 1.4
463       */
464      public void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf)
465        throws IOException
466      {
467        if (isClosed())
468          throw new SocketException("socket is closed");
469    
470        InetSocketAddress tmp = (InetSocketAddress) mcastaddr;
471    
472        if (! tmp.getAddress().isMulticastAddress())
473          throw new IOException("Not a Multicast address");
474    
475        SecurityManager s = System.getSecurityManager();
476        if (s != null)
477          s.checkMulticast(tmp.getAddress());
478    
479        getImpl().leaveGroup(mcastaddr, netIf);
480      }
481    
482      /**
483       * Sends a packet of data to a multicast address with a TTL that is
484       * different from the default TTL on this socket.  The default TTL for
485       * the socket is not changed.
486       *
487       * @param packet The packet of data to send
488       * @param ttl The TTL for this packet
489       *
490       * @exception IOException If an error occurs
491       * @exception SecurityException If a security manager exists and its
492       * checkConnect or checkMulticast method doesn't allow the operation
493       *
494       * @deprecated
495       */
496      public synchronized void send(DatagramPacket packet, byte ttl)
497        throws IOException
498      {
499        if (isClosed())
500          throw new SocketException("socket is closed");
501    
502        SecurityManager s = System.getSecurityManager();
503        if (s != null)
504          {
505            InetAddress addr = packet.getAddress();
506            if (addr.isMulticastAddress())
507              s.checkPermission(new SocketPermission(addr.getHostName()
508                                                     + packet.getPort(),
509                                                     "accept,connect"));
510            else
511              s.checkConnect(addr.getHostAddress(), packet.getPort());
512          }
513    
514        int oldttl = getImpl().getTimeToLive();
515        getImpl().setTimeToLive(((int) ttl) & 0xFF);
516        getImpl().send(packet);
517        getImpl().setTimeToLive(oldttl);
518      }
519    }