// $Id: MulticastANEPSocket.java,v 1.1 2000/02/14 19:01:45 sdalu Exp $

/*
 * @(#)MulticastSocket.java	1.31 99/04/22
 *
 * Copyright 1995-1999 by Sun Microsystems, Inc.,
 * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
 * All rights reserved.
 * 
 * This software is the confidential and proprietary information
 * of Sun Microsystems, Inc. ("Confidential Information").  You
 * shall not disclose such Confidential Information and shall use
 * it only in accordance with the terms of the license agreement
 * you entered into with Sun.
 */

package java.net;

import java.io.IOException;
import java.io.InterruptedIOException;

/**
 * The multicast anep socket class is useful for sending
 * and receiving IP multicast packets.  A MulticastANEPSocket is
 * an (ANDP) ANEPSocket, with additional capabilities for
 * joining "groups" of other multicast hosts on the internet.
 * <P>
 * One would join a
 * multicast group by first creating a MulticastANEPSocket with the desired
 * type, then invoking the <CODE>joinGroup(InetAddress groupAddr)</CODE>
 * method:
 * <PRE>
 * // join a Multicast group and send the group salutations
 * ...
 * byte[] msg = {'H', 'e', 'l', 'l', 'o'};
 * InetAddress group = InetAddress.getByName("228.5.6.7");
 * MulticastANEPSocket s = new MulticastANEPSocket(6789);
 * s.joinGroup(group);
 * ANEPPacket hi = new ANEPPacket(6789, msg, 0, msg.length, group);
 * s.send(hi);
 * // get their responses!
 * byte[] buf = new byte[1000];
 * ANEPPacket recv = new ANEPPacket(buf, 0, buf.length);
 * s.receive(recv);
 * ...
 * // OK, I'm done talking - leave the group...
 * s.leaveGroup(group);
 * </PRE>
 *
 * When one sends a message to a multicast group, <B>all</B> subscribing
 * recipients to that host and port receive the message (within the
 * time-to-live range of the packet, see below).  The socket needn't
 * be a member of the multicast group to send messages to it.
 * <P>
 * When a socket subscribes to a multicast group/port, it receives
 * datagrams sent by other hosts to the group/port, as do all other
 * members of the group and port.  A socket relinquishes membership
 * in a group by the leaveGroup(InetAddress addr) method.  <B>
 * Multiple MulticastSocket's</B> may subscribe to a multicast group
 * and port concurrently, and they will all receive group datagrams.
 * <P>
 * Currently applets are not allowed to use multicast sockets.
 *
 * @version $Revision: 1.1 $
 * @author Stephane D'Alu
 * @author  Guillaume Chelius for IPv6 modifications
 */
public
class MulticastANEPSocket extends ANEPSocket {
    /**
     * Create a multicast socket.
     * 
     * <p>If there is a security manager, 
     * its <code>checkListen</code> method is first called
     * with 0 as its argument to ensure the operation is allowed. 
     * This could result in a SecurityException.
     * 
     * @exception  SecurityException  if a security manager exists and its  
     *             <code>checkListen</code> method doesn't allow the operation.
     * @see SecurityManager#checkListen
     */
    public MulticastANEPSocket() throws IOException {
	create();
	impl.setOption(SocketOptions.SO_REUSEADDR, new Integer(-1));
    }

    /**
     * Create a multicast socket and bind it to a specific type.
     * 
     * @param type packet type
     */
    public MulticastANEPSocket(int type) throws IOException {
        ANEPPacket.checkType(type);
	create();
	impl.setOption(SocketOptions.SO_REUSEADDR, new Integer(-1));
	bind(type, InetAddress.anyLocalAddress);
    }


    /**
     * The lock on the socket's TTL. This is for set/getTTL and
     * send(packet,ttl).
     */
    private Object ttlLock = new Object();

    /**
     * Set the default time-to-live for multicast packets sent out
     * on this socket.  The TTL sets the IP time-to-live for
     * <code>ANEPPackets</code> sent to a MulticastGroup, which
     * specifies how many "hops" that the packet will be forwarded
     * on the network before it expires.
     *
     * <p>The ttl is an <b>unsigned</b> 8-bit quantity, and so <B>must</B> be
     * in the range <code> 0 <= ttl <= 0xFF </code>.
     *
     * @param ttl the time-to-live
     *
     * @deprecated use the setTimeToLive method instead, which uses
     * <b>int</b> instead of <b>byte</b> as the type for ttl.
     */
    public void setTTL(byte ttl) throws IOException {
	setHOPS((int) ttl);
    }


    /**
     * Set the default time-to-live for multicast packets sent out
     * on this socket.  The TTL sets the IP time-to-live for
     * <code>ANEPPackets</code> sent to a MulticastGroup, which
     * specifies how many "hops" that the packet will be forwarded
     * on the network before it expires.
     *
     * <P> The ttl <B>must</B> be in the range <code> 0 <= ttl <=
     * 255</code> or an IllegalArgumentException will be thrown.
     *
     * @param ttl the time-to-live
     */
    public void setTimeToLive(int ttl) throws IOException {
	if (ttl < 0 || ttl > 255)
	    throw new IllegalArgumentException("ttl out of range");
	setHOPS(ttl);
    }

    public void setHOPS(int ttl) throws IOException {
	if (ttl < 0 || ttl > 255)
	    throw new IllegalArgumentException("ttl out of range");
	impl.setHOPS(ttl);
    }

    /**
     * Get the default time-to-live for multicast packets sent out on
     * the socket.
     *
     * @deprecated use the getTimeToLive method instead, which returns
     * an <b>int</b> instead of a <b>byte</b>.
     */
    public byte getTTL() throws IOException {
	return (byte) getHOPS();
    }

    /**
     * Get the default time-to-live for multicast packets sent out on
     * the socket.
     */
    public int getTimeToLive() throws IOException {
	return getHOPS();
    }

    public int getHOPS() throws IOException {
	return impl.getHOPS();
    }

    /**
     * Joins a multicast group.Its behavior may be affected
     * by <code>setInterface</code>.
     * 
     * @param mcastaddr is the multicast address to join
     * 
     * @exception IOException if there is an error joining
     * or when the address is not a multicast address.
     */
    public void joinGroup(InetAddress mcastaddr) throws IOException {
	impl.join(mcastaddr);
    }

    /**
     * Leave a multicast group. Its behavior may be affected
     * by <code>setInterface</code>.
     * 
     * @param mcastaddr is the multicast address to leave
     * @exception IOException if there is an error leaving
     * or when the address is not a multicast address.
     */
    public void leaveGroup(InetAddress mcastaddr) throws IOException {
	impl.leave(mcastaddr);
    }

    /**
     * Set the multicast network interface used by methods
     * whose behavior would be affected by the value of the
     * network interface. Useful for multihomed hosts.
     */
    public void setInterface(int inf) throws SocketException {
	Integer value = new Integer(inf); 
	impl.setOption(SocketOptions.IPV6_MULTICAST_IF, value);
    }
    
    public void setInterface(InetAddress inf) throws SocketException {
	throw new SocketException("API of this option has changed !");
    }

    /**
     * Retrieve the address of the network interface used for
     * multicast packets.
     */
    public int getInterface() throws SocketException {
	Integer res = (Integer)impl.getOption(SocketOptions.IPV6_MULTICAST_IF);
	return res.intValue();
    }

    /**
     * Sends a ANEP packet to the destination, with a TTL (time-
     * to-live) other than the default for the socket.  This method
     * need only be used in instances where a particular TTL is desired;
     * otherwise it is preferable to set a TTL once on the socket, and
     * use that default TTL for all packets.  This method does <B>not
     * </B> alter the default TTL for the socket. Its behavior may be
     * affected by <code>setInterface</code>.
     *
     * @param p	is the packet to be sent. The packet should contain
     * the destination multicast ip address and the data to be sent.
     * One does not need to be the member of the group to send
     * packets to a destination multicast address.
     * @param ttl optional time to live for multicast packet.
     * default ttl is 1.
     * 
     * @exception IOException is raised if an error occurs i.e
     * error while setting ttl.
     * 
     * @see ANEPSocket#send
     * @see ANEPSocket#receive
     */
    public void send(ANEPPacket p, byte ttl) throws IOException {
	synchronized(ttlLock) {
	    synchronized(p) {
		if (connected) {
		    InetAddress packetAddress = p.getAddress();
		    if (packetAddress == null) {
			p.setAddress(connectedAddress);
			p.setType(connectedType);
		    } else if ((!packetAddress.equals(connectedAddress)) ||
			       p.getType() != connectedType) {
			throw new SecurityException("connected address " +
						    " and packet address" +
						    " differ");
		    }
		}
		int dttl = getHOPS();
		try {
		    if (ttl != dttl)
			impl.setHOPS(ttl);
		    impl.send(p);
		} finally {
		    if (ttl != dttl) {
			impl.setHOPS(dttl);
		    }
		}
	    }
	}
    }
}
