001/* MulticastSocket.java -- Class for using multicast sockets
002   Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2007
003   Free Software Foundation, Inc.
004
005This file is part of GNU Classpath.
006
007GNU Classpath is free software; you can redistribute it and/or modify
008it under the terms of the GNU General Public License as published by
009the Free Software Foundation; either version 2, or (at your option)
010any later version.
011
012GNU Classpath is distributed in the hope that it will be useful, but
013WITHOUT ANY WARRANTY; without even the implied warranty of
014MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015General Public License for more details.
016
017You should have received a copy of the GNU General Public License
018along with GNU Classpath; see the file COPYING.  If not, write to the
019Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02002110-1301 USA.
021
022Linking this library statically or dynamically with other modules is
023making a combined work based on this library.  Thus, the terms and
024conditions of the GNU General Public License cover the whole
025combination.
026
027As a special exception, the copyright holders of this library give you
028permission to link this library with independent modules to produce an
029executable, regardless of the license terms of these independent
030modules, and to copy and distribute the resulting executable under
031terms of your choice, provided that you also meet, for each linked
032independent module, the terms and conditions of the license of that
033module.  An independent module is a module which is not derived from
034or based on this library.  If you modify this library, you may extend
035this exception to your version of the library, but you are not
036obligated to do so.  If you do not wish to do so, delete this
037exception statement from your version. */
038
039package java.net;
040
041import java.io.IOException;
042import 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 */
068public 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    if (address.isAnyLocalAddress())
263      return NetworkInterface.createAnyInterface();
264
265    NetworkInterface netIf = NetworkInterface.getByInetAddress(address);
266
267    return netIf;
268  }
269
270  /**
271   * Disable/Enable local loopback of multicast packets.  The option is used by
272   * the platform's networking code as a hint for setting whether multicast
273   * data will be looped back to the local socket.
274   *
275   * Because this option is a hint, applications that want to verify what
276   * loopback mode is set to should call #getLoopbackMode
277   *
278   * @param disable True to disable loopback mode
279   *
280   * @exception SocketException If an error occurs
281   *
282   * @since 1.4
283   */
284  public void setLoopbackMode(boolean disable) throws SocketException
285  {
286    if (isClosed())
287      throw new SocketException("socket is closed");
288
289    getImpl().setOption(SocketOptions.IP_MULTICAST_LOOP,
290                        Boolean.valueOf(disable));
291  }
292
293  /**
294   * Checks if local loopback mode is enabled
295   *
296   * @return true if loopback mode is enabled, false otherwise
297   *
298   * @exception SocketException If an error occurs
299   *
300   * @since 1.4
301   */
302  public boolean getLoopbackMode() throws SocketException
303  {
304    if (isClosed())
305      throw new SocketException("socket is closed");
306
307    Object buf = getImpl().getOption(SocketOptions.IP_MULTICAST_LOOP);
308
309    if (buf instanceof Boolean)
310      return ((Boolean) buf).booleanValue();
311
312    throw new SocketException("unexpected type");
313  }
314
315  /**
316   * Sets the "Time to Live" value for a socket.  The value must be between
317   * 1 and 255.
318   *
319   * @param ttl The new TTL value
320   *
321   * @exception IOException If an error occurs
322   *
323   * @deprecated 1.2 Replaced by <code>setTimeToLive</code>
324   *
325   * @see MulticastSocket#setTimeToLive(int ttl)
326   */
327  public void setTTL(byte ttl) throws IOException
328  {
329    if (isClosed())
330      throw new SocketException("socket is closed");
331
332    // Use setTTL here rather than setTimeToLive in case we're using an impl
333    // other than the default PlainDatagramSocketImpl and it doesn't have
334    // setTimeToLive yet.
335    getImpl().setTTL(ttl);
336  }
337
338  /**
339   * Sets the "Time to Live" value for a socket.  The value must be between
340   * 0 and 255, inclusive.
341   *
342   * @param ttl The new TTL value
343   *
344   * @exception IOException If an error occurs
345   *
346   * @since 1.2
347   */
348  public void setTimeToLive(int ttl) throws IOException
349  {
350    if (isClosed())
351      throw new SocketException("socket is closed");
352
353    if (ttl < 0 || ttl > 255)
354      throw new IllegalArgumentException("Invalid ttl: " + ttl);
355
356    getImpl().setTimeToLive(ttl);
357  }
358
359  /**
360   * Joins the specified multicast group.
361   *
362   * @param mcastaddr The address of the group to join
363   *
364   * @exception IOException If an error occurs
365   * @exception SecurityException If a security manager exists and its
366   * checkMulticast method doesn't allow the operation
367   */
368  public void joinGroup(InetAddress mcastaddr) throws IOException
369  {
370    if (isClosed())
371      throw new SocketException("socket is closed");
372
373    if (! mcastaddr.isMulticastAddress())
374      throw new IOException("Not a Multicast address");
375
376    SecurityManager s = System.getSecurityManager();
377    if (s != null)
378      s.checkMulticast(mcastaddr);
379
380    getImpl().join(mcastaddr);
381  }
382
383  /**
384   * Leaves the specified multicast group
385   *
386   * @param mcastaddr The address of the group to leave
387   *
388   * @exception IOException If an error occurs
389   * @exception SecurityException If a security manager exists and its
390   * checkMulticast method doesn't allow the operation
391   */
392  public void leaveGroup(InetAddress mcastaddr) throws IOException
393  {
394    if (isClosed())
395      throw new SocketException("socket is closed");
396
397    if (! mcastaddr.isMulticastAddress())
398      throw new IOException("Not a Multicast address");
399
400    SecurityManager s = System.getSecurityManager();
401    if (s != null)
402      s.checkMulticast(mcastaddr);
403
404    getImpl().leave(mcastaddr);
405  }
406
407  /**
408   * Joins the specified mulitcast group on a specified interface.
409   *
410   * @param mcastaddr The multicast address to join
411   * @param netIf The local network interface to receive the multicast
412   * messages on or null to defer the interface set by #setInterface or
413   * #setNetworkInterface
414   *
415   * @exception IOException If an error occurs
416   * @exception IllegalArgumentException If address type is not supported
417   * @exception SecurityException If a security manager exists and its
418   * checkMulticast method doesn't allow the operation
419   *
420   * @see MulticastSocket#setInterface(InetAddress addr)
421   * @see MulticastSocket#setNetworkInterface(NetworkInterface netIf)
422   *
423   * @since 1.4
424   */
425  public void joinGroup(SocketAddress mcastaddr, NetworkInterface netIf)
426    throws IOException
427  {
428    if (isClosed())
429      throw new SocketException("socket is closed");
430
431    if (! (mcastaddr instanceof InetSocketAddress))
432      throw new IllegalArgumentException("SocketAddress type not supported");
433
434    InetSocketAddress tmp = (InetSocketAddress) mcastaddr;
435
436    if (! tmp.getAddress().isMulticastAddress())
437      throw new IOException("Not a Multicast address");
438
439    SecurityManager s = System.getSecurityManager();
440    if (s != null)
441      s.checkMulticast(tmp.getAddress());
442
443    getImpl().joinGroup(mcastaddr, netIf);
444  }
445
446  /**
447   * Leaves the specified mulitcast group on a specified interface.
448   *
449   * @param mcastaddr The multicast address to leave
450   * @param netIf The local networki interface or null to defer to the
451   * interface set by setInterface or setNetworkInterface
452   *
453   * @exception IOException If an error occurs
454   * @exception IllegalArgumentException If address type is not supported
455   * @exception SecurityException If a security manager exists and its
456   * checkMulticast method doesn't allow the operation
457   *
458   * @see MulticastSocket#setInterface(InetAddress addr)
459   * @see MulticastSocket#setNetworkInterface(NetworkInterface netIf)
460   *
461   * @since 1.4
462   */
463  public void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf)
464    throws IOException
465  {
466    if (isClosed())
467      throw new SocketException("socket is closed");
468
469    InetSocketAddress tmp = (InetSocketAddress) mcastaddr;
470
471    if (! tmp.getAddress().isMulticastAddress())
472      throw new IOException("Not a Multicast address");
473
474    SecurityManager s = System.getSecurityManager();
475    if (s != null)
476      s.checkMulticast(tmp.getAddress());
477
478    getImpl().leaveGroup(mcastaddr, netIf);
479  }
480
481  /**
482   * Sends a packet of data to a multicast address with a TTL that is
483   * different from the default TTL on this socket.  The default TTL for
484   * the socket is not changed.
485   *
486   * @param packet The packet of data to send
487   * @param ttl The TTL for this packet
488   *
489   * @exception IOException If an error occurs
490   * @exception SecurityException If a security manager exists and its
491   * checkConnect or checkMulticast method doesn't allow the operation
492   *
493   * @deprecated
494   */
495  public synchronized void send(DatagramPacket packet, byte ttl)
496    throws IOException
497  {
498    if (isClosed())
499      throw new SocketException("socket is closed");
500
501    SecurityManager s = System.getSecurityManager();
502    if (s != null)
503      {
504        InetAddress addr = packet.getAddress();
505        if (addr.isMulticastAddress())
506          s.checkPermission(new SocketPermission(addr.getHostName()
507                                                 + packet.getPort(),
508                                                 "accept,connect"));
509        else
510          s.checkConnect(addr.getHostAddress(), packet.getPort());
511      }
512
513    int oldttl = getImpl().getTimeToLive();
514    getImpl().setTimeToLive(((int) ttl) & 0xFF);
515    getImpl().send(packet);
516    getImpl().setTimeToLive(oldttl);
517  }
518}