// $Log: PlainDatagramSocketImpl.java,v $
// Revision 1.3  2000/04/03 08:05:34  chelius
// Javadoc
//
// Revision 1.2  2000/02/14 10:35:25  chelius
// Modifications mineures. Reste QoS et Doc a faire.
//
/*
 * @(#)PlainDatagramSocketImpl.java	1.18 98/07/07
 *
 * Copyright 1996-1998 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.FileDescriptor;
import java.io.IOException;
import java.io.InterruptedIOException;

/**
 * Concrete datagram and multicast socket implementation base class.
 * Note: This is not a public class, so that applets cannot call
 * into the implementation directly and hence cannot bypass the
 * security checks present in the DatagramSocket and MulticastSocket
 * classes.
 *
 * @author Pavani Diwanji
 * @author  Guillaume Chelius (IPv6 modifications)
 */

class PlainDatagramSocketImpl extends DatagramSocketImpl
{

    /* timeout value for receive() */
    private int timeout = 0;

    /* flow label */
    private int flow_label = 0; 

    /* traffic class value */
    private int traffic_class = 0;

    /**
     * Load net6 library into runtime.
     */
    static {
	java.security.AccessController.doPrivileged(
		  new sun.security.action.LoadLibraryAction("net6"));
	init();
    }

    /**
     * Creates a datagram socket
     */
    protected synchronized void create() throws SocketException {
	fd = new FileDescriptor();
	datagramSocketCreate();
    }

    /**
     * Binds a datagram socket to a local port.
     */
    protected synchronized native void bind(int lport, InetAddress laddr)
        throws SocketException;

    /**
     * Sends a datagram packet. The packet contains the data and the
     * destination address to send the packet to.
     * @param packet to be sent.
     */
    protected native void send(DatagramPacket p) throws IOException;

    /**
     * Peek at the packet to see who it is from.
     * @param return the address which the packet came from.
     */
    protected synchronized native int peek(InetAddress i) throws IOException;

    /**
     * Receive the datagram packet.
     * @param Packet Received.
     */
    protected synchronized native void receive(DatagramPacket p)
        throws IOException;

    /**
     * Set the HOPS option.
     * @param ttl HOPS to be set.
     */
    protected native void setHOPS(int ttl) throws IOException;

    /**
     * Get the HOPS option.
     */
    protected native int getHOPS() throws IOException;

    /**
     * Join the multicast group.
     * @param multicast address to join.
     */
    protected native void join(InetAddress inetaddr) throws IOException;

    /**
     * Leave the multicast group.
     * @param multicast address to leave.
     */
    protected native void leave(InetAddress inetaddr) throws IOException;

    /**
     * Close the socket.
     */
    protected void close() {
	if (fd != null) {
	    datagramSocketClose();
	    fd = null;
	}
    }

    protected void finalize() {
	close();
    }

    /**
     * set a value - since we only support (setting) binary options
     * here, o must be a Boolean
     */

     public void setOption(int optID, Object o) throws SocketException {
	 switch (optID) {
	    /* check type safety b4 going native.  These should never
	     * fail, since only java.Socket* has access to
	     * PlainSocketImpl.setOption().
	     */
	 case SO_TIMEOUT:
	     if (o == null || !(o instanceof Integer)) {
		 throw new SocketException("bad argument for SO_TIMEOUT");
	     }
	     int tmp = ((Integer) o).intValue();
	     if (tmp < 0)
		 throw new IllegalArgumentException("timeout < 0");
	     timeout = tmp;
	     return;
	 case IPV6_FLOW_LABEL:
	     if (o == null || !(o instanceof Integer)) {
		 throw new SocketException("bad argument for FLOW_LABEL");
	     }
	     int tmp_2 = ((Integer) o).intValue();
	     if (tmp_2 < 0)
		 throw new IllegalArgumentException("flow label < 0");
	     if (tmp_2 >= 0xffffff)
		 throw new IllegalArgumentException("flow label < 17377216");
	     flow_label = tmp_2;
	     return;
	 case IPV6_TRAFFIC_CLASS:
	     if (o == null || !(o instanceof Integer)) {
		 throw new SocketException("bad argument for TRAFFIC_CLASS");
	     }
	     int tmp_3 = ((Integer) o).intValue();
	     if (tmp_3 < 0)
		 throw new IllegalArgumentException("traffic class < 0");
	     if (tmp_3 >= 0xf)
		 throw new IllegalArgumentException("traffic class > 5");
	     traffic_class = tmp_3;
	     return;
	 case SO_BINDADDR:
	     throw new SocketException("Cannot re-bind Socket");
	 case SO_REUSEADDR:
	     if (o == null || !(o instanceof Integer)) {
		 throw new SocketException("bad argument for SO_REUSEADDR");
	     }
	     break;
	case IPV6_AUTH_TRANS_LEVEL: 
	case IPV6_AUTH_NETWORK_LEVEL: 
	case IPV6_ESP_TRANS_LEVEL: 
	case IPV6_ESP_NETWORK_LEVEL: 
	     if (o == null || !(o instanceof Integer) ||
		!(((Integer)o).intValue() > -1) ||
		!(((Integer)o).intValue() < 6))
		 throw new SocketException("bad parameter for IPsec option");
	     
	     break;
	 case SO_RCVBUF:
	 case SO_SNDBUF:
	     if (o == null || !(o instanceof Integer) ||
		 ((Integer)o).intValue() < 0) {
		 throw new SocketException("bad argument for SO_SNDBUF or " +
					   "SO_RCVBUF");
	     }
	     break;
	 case IPV6_MULTICAST_IF:
	     if (o == null || !(o instanceof Integer))
		 throw new SocketException("bad argument for IP_MULTICAST_IF");
	     break;
	 default:
	     throw new SocketException("invalid option: " + optID);
	 }
	 socketSetOption(optID, o);
     }

    /*
     * get option's state - set or not
     */

    public Object getOption(int optID) throws SocketException {
	Integer result = null;
	if (optID == SO_TIMEOUT) {
	    result = new Integer(timeout);
	} else 	if (optID == IPV6_TRAFFIC_CLASS) {
	    return new Integer(traffic_class);
	} else if (optID == IPV6_FLOW_LABEL) {
	    return new Integer(flow_label);
	} else {
	    byte[] ret = socketGetOption(optID);
	    
	    if (optID == IPV6_MULTICAST_IF) {
		int aux;

		aux  = ret[3] & 0xFF;
		aux |= ((ret[2] << 8) & 0xFF00);
		aux |= ((ret[1] << 16) & 0xFF0000);
		aux |= ((ret[0] << 24) & 0xFF000000);
		return (new Integer(aux));
	    } else if (optID == SO_BINDADDR) {
		InetAddress in = new InetAddress();
		for(int i = 0; i < 16; i++)
		    in.address[i] = ret[i];	     
		return in;
	    } else if (optID == SO_RCVBUF || optID == SO_SNDBUF || optID == IPV6_AUTH_NETWORK_LEVEL || optID == IPV6_AUTH_TRANS_LEVEL || optID == IPV6_ESP_NETWORK_LEVEL || optID == IPV6_ESP_TRANS_LEVEL) {
		int aux;
		
		aux  = ret[3] & 0xFF;
		aux |= ((ret[2] << 8) & 0xFF00);
		aux |= ((ret[1] << 16) & 0xFF0000);
		aux |= ((ret[0] << 24) & 0xFF000000);
		return (new Integer(aux));	
	    }
	}
	return result;
    }
    
    private native void datagramSocketCreate() throws SocketException;
    private native void datagramSocketClose();
    private native void socketSetOption(int opt, Object val)
        throws SocketException;
    private native byte[] socketGetOption(int opt) throws SocketException;

    /**
     * Perform class load-time initializations.
     */
    private native static void init();

}

