/* Copyright (c) 1999 by Inria Lorraine.  All Rights Reserved */

/***
   NAME
     PlainRawSocketImpl
   PURPOSE
     
   NOTES
     
   HISTORY
     chelius - Jul 25, 1999: Created.
     $Log: PlainRawSocketImpl.java,v $
     Revision 1.3  2000/04/03 08:05:34  chelius
     Javadoc

     Revision 1.2  2000/02/14 10:35:26  chelius
     Modifications mineures. Reste QoS et Doc a faire.

     Revision 1.1.1.1  2000/02/08 16:31:58  chelius
     Initial import

     Revision 1.1.1.1  2000/02/01 11:10:46  chelius
     Mise sous CVS des sources de libnet6 for jdk1.2.*

     Revision 1.1.1.1  1999/07/25 15:01:57  chelius
     First release of the ipv6 packages for java

***/
package java.net;



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

/**
 * Concrete raw and Icmp 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 RawSocket and IcmpSocket
 * classes.
 *
 * @author  Guillaume Chelius (IPv6 modifications)
 */
class PlainRawSocketImpl extends RawSocketImpl {

    /* 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 raw socket
     */
    protected synchronized void create(int proto) throws SocketException {
	fd = new FileDescriptor();
	protocol = proto;
	rawSocketCreate(proto);
    }

    /**
     * Binds a raw socket to a local address.
     */
    protected synchronized native void bind(InetAddress laddr) throws SocketException;
	
    /**
     * Sends a raw packet. The packet contains the data and the
     * destination address to send the packet to.
     * @param packet to be sent.
     */
    protected native void send(RawPacket 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 void peek(InetAddress i) throws IOException;
    
    /**
     * Receive the raw packet.
     * @param Packet Received.
     */
    protected synchronized native void receive(RawPacket p) throws IOException;

    /**
     * Set the Checksum option.
     * @param offset to which the checksum should be put.
     * @since   JDK1.2
     */
    protected native void setChecksum(int offset) throws SocketException;

    /**
     * Get the Checksum option.
     * @since   JDK1.2
     */

    protected native int getChecksum() throws SocketException;

    /**
     * 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 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;
	 default:
	     throw new SocketException("invalid option: " + optID);
	 }
	 socketSetOption(optID, o);
     }

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

    public Object getOption(int optID) throws SocketException {
	if (optID == SO_TIMEOUT) {
	    return new Integer(timeout);
	}
	if (optID == IPV6_TRAFFIC_CLASS) {
	    return new Integer(traffic_class);
	}
	if (optID == IPV6_FLOW_LABEL) {
	    return new Integer(flow_label);
	}

	byte[] ret = socketGetOption(optID);
	if (optID == SO_RCVBUF || optID == SO_SNDBUF || optID == IPV6_AUTH_TRANS_LEVEL || optID == IPV6_AUTH_NETWORK_LEVEL  || optID == IPV6_ESP_TRANS_LEVEL || optID == IPV6_ESP_NETWORK_LEVEL) {
	    int result;
	    
	    result  = ret[3] & 0xFF;
	    result |= ((ret[2] << 8) & 0xFF00);
	    result |= ((ret[1] << 16) & 0xFF0000);
	    result |= ((ret[0] << 24) & 0xFF000000);
	    return (new Integer(result));
	}
	if (optID == SO_BINDADDR) {
	    InetAddress in = new InetAddress();
	    for(int i = 0; i < 16; i++)
                in.address[i] = ret[i];
	    return in;
	} else {
	    return null;
	}
    }

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

    protected synchronized void finalize() {
	close();
    }

    private native void rawSocketCreate(int proto) throws SocketException;
    private native void rawSocketClose();
    private native void socketSetOption(int opt, Object val) throws SocketException;
    private native byte[] socketGetOption(int opt) throws SocketException;
    private native static void init();    
}
