/*****************************************************************************/
/* File name: Mapper.java                                                    */
/* Purpose: to provide RPC portmapping service                               */
/*                                                                           */
/* Last modified: 01.08.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*                                                                           */
/* This program is distributed in the hope that it will be useful, but       */
/* WITHOUT ANY WARRANTY; without even the implied warranty of                */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General */
/* Public License for more details.                                          */
/*                                                                           */
/* This software is distributed under the terms                              */
/* of the GNU General Public License as published                            */
/* by the Free Software Foundation; either version 2, or (at your option)    */
/* any later version.                                                        */
/* See file COPYING for your rights (GNU GPL)                                */
/*****************************************************************************/

package pm;


import java.net.*;
import java.io.*;
import java.util.*;
import rpc.*;
import xdr.*;


public
class

Mapper
        extends rpc.ParserResponder
                                   implements Runnable
{

  private final static String RCSID = "$Header: /d/NFS/pm/RCS/Mapper.java,v 1.10 1999/08/02 16:49:05 vklimov Exp $";

  protected Registry registry = null;


  public

  Mapper( java.lang.String propertiesFilePath )

                                           throws  SocketException
/*****************************************************************************/
/* Function name: Mapper                                                     */
/* File name: Mapper.java                                                    */
/* Purpose: constructor for the Mapper class                                 */
/* Throws: SocketException                                                   */
/* Parameters: propertiesFilePath                                            */
/* Returns:                                                                  */
/* Last modified: 10.07.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*****************************************************************************/
    {
      super( Constants.PORT_MAPPER_PORT,
             propertiesFilePath          );
                                                           /* Mapper */
/*---------------------------------------------------------------------------*/

      registry = new Registry();

    }
/*end of Mapper*/


  public
  void

  registerProgramMappingTupleAndSendRPCReplyMessage(
    ParserResponderReturnArguments
      parserResponderReturnArguments                )
/*****************************************************************************/
/* Function name: registerProgramMappingTupleAndSendRPCReplyMessage          */
/* File name: Mapper.java                                                    */
/* Purpose: to register a program's mapping - to memorize port number        */
/*          for a given (program, version, protocol) tuple and send          */
/*          RPC reply message - send XDR true if registered OK,              */
/*                                       false - otherwise                   */
/* Parameters: parserResponderReturnArguments                                */
/* Returns:                                                                  */
/* Last modified: 01.08.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*****************************************************************************/
    {
      boolean registeredOK;
      byte[] ibuf = null;
      InetAddress ipAddressWhereToSend = null;
      int portToSendTo = 0;
      DatagramPacket udpPacket = null;
      Message rpcMessage = null;
      Mapping mapping = null;
      Message rpcMessageWithParametersToRemoteProcedure = null;
      byte[] byteArrayContainingParametersToRemoteProcedure = null;

                   /* registerProgramMappingTupleAndSendRPCReplyMessage */
/*---------------------------------------------------------------------------*/

      rpcMessageWithParametersToRemoteProcedure = new
        Message(
          parserResponderReturnArguments.
            byteArrayContainingParametersToRemoteProcedure );

      mapping = new
        Mapping(
          rpcMessageWithParametersToRemoteProcedure );

      if ( (logTransactions) &&
           (log != null)
         )
        {
          log.write( 
            "Mapper, registerProgramMappingTupleAndSendRPCReplyMessage\n" +
            "transaction ID=" +
            parserResponderReturnArguments.
              transactionId +
            " asked for mapping:\n" +
            mapping.toString()       );
        }
      else
        ;
    /*end of if*/

      registeredOK =
        registry.put( mapping );

      rpcMessage =
        createRPCMessageAndPutSuccessPrefix(
          Constants.
            PMAPPROC_SET_UDP_PACKET_DATA_LENGTH,
          parserResponderReturnArguments         );

      ibuf = rpcMessage.getData();

      if (registeredOK)
        {

          if ( (logTransactions) &&
               (log != null)
             )
            {
              log.write( 
                "Mapper, registerProgramMappingTupleAndSendRPCReplyMessage\n" +
                "transaction ID=" +
                parserResponderReturnArguments.
                  transactionId +
                " registered OK" );
            }
          else
            ;
        /*end of if*/

          rpcMessage.
            putUnsignedInteger( (long) xdr.Constants.TRUE );
        }
      else
        {

          if ( (logTransactions) &&
               (log != null)
             )
            {
              log.write( 
                "Mapper, registerProgramMappingTupleAndSendRPCReplyMessage\n" +
                "transaction ID=" +
                parserResponderReturnArguments.
                  transactionId +
                " NOT registered, have it already!" );
            }
          else
            ;
        /*end of if*/

          rpcMessage.
            putUnsignedInteger( (long) xdr.Constants.FALSE );
        }
    /*end of if on registeredOK*/

      ipAddressWhereToSend =
        parserResponderReturnArguments.
          inRPCMessage.getInetAddress();
      portToSendTo =
        parserResponderReturnArguments.
          inRPCMessage.getPort();

      udpPacket = new
        DatagramPacket( ibuf, ibuf.length,
                        ipAddressWhereToSend,
                        portToSendTo          );

      try
        {
          udpSocket.
            send( udpPacket,
                  parserResponderReturnArguments.transactionId,
                  "registerProgramMappingTupleAndSendRPCReplyMessage" );
        }
      catch( IOException e )
        {
          System.out.println(
            "Mapper, " +
            "registerProgramMappingTupleAndSendRPCReplyMessage: " +
            "IOException in sending reply\n" +
            e.getMessage()                              );
        }
    /*end of try-catch*/

    }
/*end of registerProgramMappingTupleAndSendRPCReplyMessage*/


  public
  void

  unregisterProgramMappingTupleAndSendRPCReplyMessage(
    ParserResponderReturnArguments
      parserResponderReturnArguments                   )
/*****************************************************************************/
/* Function name: unregisterProgramMappingTupleAndSendRPCReplyMessage        */
/* File name: Mapper.java                                                    */
/* Purpose: to unregister a program's mapping                                */
/*          protocol and port values of the tuple are ignored                */
/*          send RPC reply message - send XDR true if unregistered OK,       */
/*                                            false - otherwise              */
/* Parameters: parserResponderReturnArguments                                */
/* Returns:                                                                  */
/* Last modified: 01.08.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*****************************************************************************/
    {
      boolean unregisteredOK;
      byte[] ibuf = null;
      InetAddress ipAddressWhereToSend = null;
      int portToSendTo = 0;
      DatagramPacket udpPacket = null;
      Message rpcMessage = null;
      Mapping mapping = null;
      Message rpcMessageWithParametersToRemoteProcedure = null;
      byte[] byteArrayContainingParametersToRemoteProcedure = null;

                   /* unregisterProgramMappingTupleAndSendRPCReplyMessage */
/*---------------------------------------------------------------------------*/

      rpcMessageWithParametersToRemoteProcedure = new
        Message(
          parserResponderReturnArguments.
            byteArrayContainingParametersToRemoteProcedure );

      mapping = new
        Mapping(
          rpcMessageWithParametersToRemoteProcedure );

      if ( (logTransactions) &&
           (log != null)
         )
        {
          log.write( 
            "Mapper, registerProgramMappingTupleAndSendRPCReplyMessage\n" +
            "transaction ID=" +
            parserResponderReturnArguments.
              transactionId +
            " asked to unregister mapping:\n" +
            mapping.toString()       );
        }
      else
        ;
    /*end of if*/

      unregisteredOK =
        registry.remove( mapping );

      rpcMessage =
        createRPCMessageAndPutSuccessPrefix(
          Constants.
            PMAPPROC_SET_UDP_PACKET_DATA_LENGTH,
          parserResponderReturnArguments         );

      ibuf = rpcMessage.getData();

      if (unregisteredOK)
        {

          if ( (logTransactions) &&
               (log != null)
             )
            {
              log.write( 
                "Mapper, registerProgramMappingTupleAndSendRPCReplyMessage\n" +
                "transaction ID=" +
                parserResponderReturnArguments.
                  transactionId +
                " unregistered OK" );
            }
          else
            ;
        /*end of if*/

          rpcMessage.
            putUnsignedInteger( (long) xdr.Constants.TRUE );
        }
      else
        {

          if ( (logTransactions) &&
               (log != null)
             )
            {
              log.write( 
                "Mapper, registerProgramMappingTupleAndSendRPCReplyMessage\n" +
                "transaction ID=" +
                parserResponderReturnArguments.
                  transactionId +
                " NOT unregistered!" );
            }
          else
            ;
        /*end of if*/

          rpcMessage.
            putUnsignedInteger( (long) xdr.Constants.FALSE );
        }
    /*end of if on unregisteredOK*/

      ipAddressWhereToSend =
        parserResponderReturnArguments.
          inRPCMessage.getInetAddress();
      portToSendTo =
        parserResponderReturnArguments.
          inRPCMessage.getPort();

      udpPacket = new
        DatagramPacket( ibuf, ibuf.length,
                        ipAddressWhereToSend,
                        portToSendTo          );

      try
        {
          udpSocket.
            send( udpPacket,
                  parserResponderReturnArguments.transactionId,
                  "unregisterProgramMappingTupleAndSendRPCReplyMessage" );
        }
      catch( IOException e )
        {
          System.out.println(
            "Mapper, " +
            "unregisterProgramMappingTupleAndSendRPCReplyMessage: " +
            "IOException in sending reply\n" +
            e.getMessage()                              );
        }
    /*end of try-catch*/

    }
/*end of unregisterProgramMappingTupleAndSendRPCReplyMessage*/


  public
  void

  getPortAndSendRPCReplyMessage(
    ParserResponderReturnArguments
      parserResponderReturnArguments    )
/*****************************************************************************/
/* Function name: getPortAndSendRPCReplyMessage                              */
/* File name: Mapper.java                                                    */
/* Purpose: to perform actually the mapping - get port number for a given    */
/*          program, version, protocol tuple and send RPC reply message      */
/* Parameters: parserResponderReturnArguments                                */
/* Returns:                                                                  */
/* Last modified: 01.08.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*****************************************************************************/
    {
      byte[] ibuf = null;
      InetAddress ipAddressWhereToSend = null;
      int portToSendTo = 0;
      DatagramPacket udpPacket = null;
      Message rpcMessage = null;
      Mapping mapping = null;
      Message rpcMessageWithParametersToRemoteProcedure = null;
      byte[] byteArrayContainingParametersToRemoteProcedure = null;
      long portNumber = 0;

                                       /* getPortAndSendRPCReplyMessage */
/*---------------------------------------------------------------------------*/

      rpcMessageWithParametersToRemoteProcedure = new
        Message(
          parserResponderReturnArguments.
            byteArrayContainingParametersToRemoteProcedure );

      mapping = new
        Mapping(
          rpcMessageWithParametersToRemoteProcedure );

      rpcMessage =
        createRPCMessageAndPutSuccessPrefix(
          rpc.Constants.
            PROC_NULL_UDP_PACKET_DATA_LENGTH,
          parserResponderReturnArguments         );

      ibuf = rpcMessage.getData();

      portNumber =
        registry.getPort( mapping );

      if ( (logTransactions) &&
           (log != null)
         )
        {
          log.write( 
            "Mapper, getPortAndSendRPCReplyMessage\n" +
            "transaction ID=" +
            parserResponderReturnArguments.
              transactionId +
            " going to send portNumber=" + portNumber  );
        }
      else
        ;
    /*end of if*/

      rpcMessage.
        putUnsignedInteger( portNumber );

      ipAddressWhereToSend =
        parserResponderReturnArguments.
          inRPCMessage.getInetAddress();
      portToSendTo =
        parserResponderReturnArguments.
          inRPCMessage.getPort();

      udpPacket = new
        DatagramPacket( ibuf, ibuf.length,
                        ipAddressWhereToSend,
                        portToSendTo          );

      try
        {
          udpSocket.send( udpPacket,
                          parserResponderReturnArguments.transactionId,
                          "getPortAndSendRPCReplyMessage"               );
        }
      catch( IOException e )
        {
          System.out.println(
            "Mapper, " +
            "getPortAndSendRPCReplyMessage: " +
            "IOException in sending reply\n" +
            e.getMessage()                              );
        }
    /*end of try-catch*/

    }
/*end of getPortAndSendRPCReplyMessage*/


  public
  void

  dumpPortsAndSendRPCReplyMessage(
    ParserResponderReturnArguments
      parserResponderReturnArguments )
/*****************************************************************************/
/* Function name: dumpPortsAndSendRPCReplyMessage                            */
/* File name: Mapper.java                                                    */
/* Purpose: to dump all mappings that we have registered right now           */
/*          and send RPC reply message                                       */
/* Parameters: parserResponderReturnArguments                                */
/* Returns:                                                                  */
/* Last modified: 01.08.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*****************************************************************************/
    {
      int i;
      boolean continueToLoopOnMappings = true;
      byte[] ibuf = null;
      InetAddress ipAddressWhereToSend = null;
      int portToSendTo = 0;
      DatagramPacket udpPacket = null;
      Message rpcMessage = null;
      Mapping mapping = null;
      Message rpcMessageWithParametersToRemoteProcedure = null;
      byte[] byteArrayContainingParametersToRemoteProcedure = null;
      long portNumber = 0;
      int nBytesForMappings;
      Enumeration mappings;

                                       /* dumpPortsAndSendRPCReplyMessage */
/*---------------------------------------------------------------------------*/

      if ( (logTransactions) &&
           (log != null)
         )
        {
          log.write( 
            "Mapper, dumpPortsAndSendRPCReplyMessage\n" +
            "transaction ID=" +
            parserResponderReturnArguments.
              transactionId                               );
        }
      else
        ;
    /*end of if*/

      nBytesForMappings =
        Constants.N_BYTES_PER_MAPPING *
        (1 + registry.size() );

      rpcMessage =
        createRPCMessageAndPutSuccessPrefix(
          Constants.
            PMAPPROC_DUMP_UDP_DATA_PREFIX_LENGTH +
              nBytesForMappings,
          parserResponderReturnArguments         );

      ibuf = rpcMessage.getData();

      mappings =
        registry.mappings();

      i = 0;
      while( continueToLoopOnMappings &&
             mappings.hasMoreElements()  )
        {
          try
            {
              mapping = (Mapping) mappings.nextElement();

              rpcMessage.
                putUnsignedInteger( mapping.programNumber );
              rpcMessage.
                putUnsignedInteger( mapping.versionNumber );
              rpcMessage.
                putUnsignedInteger( mapping.protocolNumber );
              rpcMessage.
                putUnsignedInteger( mapping.portNumber );
            }
          catch( NoSuchElementException e )
            {
                                                       /* end of list */
              rpcMessage.
                putUnsignedInteger(
                  Constants.END_OF_MAPPING_LIST_UNSIGNED_INT );
              rpcMessage.
                putUnsignedInteger(
                  Constants.END_OF_MAPPING_LIST_UNSIGNED_INT );
              rpcMessage.
                putUnsignedInteger(
                  Constants.END_OF_MAPPING_LIST_UNSIGNED_INT );
              rpcMessage.
                putUnsignedInteger(
                  Constants.END_OF_MAPPING_LIST_UNSIGNED_INT );

              continueToLoopOnMappings = false;
            }
        /*end of try-catch*/
          i++;
        }
    /*end of while-loop*/

      ipAddressWhereToSend =
        parserResponderReturnArguments.
          inRPCMessage.getInetAddress();
      portToSendTo =
        parserResponderReturnArguments.
          inRPCMessage.getPort();

      udpPacket = new
        DatagramPacket( ibuf, ibuf.length,
                        ipAddressWhereToSend,
                        portToSendTo          );

      try
        {
          udpSocket.send( udpPacket,
                          parserResponderReturnArguments.transactionId,
                          "dumpPortsAndSendRPCReplyMessage"             );
        }
      catch( IOException e )
        {
          System.out.println(
            "Mapper, " +
            "dumpPortsAndSendRPCReplyMessage: " +
            "IOException in sending reply\n" +
            e.getMessage()                              );
        }
    /*end of try-catch*/

    }
/*end of dumpPortsAndSendRPCReplyMessage*/


  public
  void

  run()
/*****************************************************************************/
/* Function name: run                                                        */
/* File name: Mapper.java                                                    */
/* Purpose: to perform actual port mapping service                           */
/* Parameters:                                                               */
/* Returns:                                                                  */
/* Last modified: 01.08.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*****************************************************************************/
    {
      Long remoteProcedureNumberLong = null;
      int i;
      boolean thereWasAGeneralError = false;
      int dataLength = 0;
      byte[] byteBuffer = null;
      byte[] dataReceived;
      DatagramPacket udpPacket = null;
      long transactionId;
      long messageType;
      boolean nowMayProcessTheParametersToRemoteProcedure;
      ParserResponderReturnArguments parserResponderReturnArguments;
      boolean continueLoopReceivePackets = true;
                                                                 /* run */
/*---------------------------------------------------------------------------*/

      while (continueLoopReceivePackets)
        {
          byteBuffer = new byte[ Constants.UDP_PACKET_LENGTH ];

          udpPacket = new
            DatagramPacket( byteBuffer,
                            byteBuffer.length );

          try
            {
              udpSocket.receive( udpPacket );
            }
          catch( IOException e )
            {
              System.out.println(
                "udpSocket.receive: " +
                e.getMessage()          );
            }
        /*end of try-catch*/

          parserResponderReturnArguments = new
            ParserResponderReturnArguments();

          nowMayProcessTheParametersToRemoteProcedure =
            processUDPPacket(
              udpPacket,
              parserResponderReturnArguments      );

          thereWasAGeneralError =
            processGeneralErrors(
              parserResponderReturnArguments,
              Constants.PMAP_PROG,
              Constants.PMAP_VERS_LOW,
              Constants.PMAP_VERS_HIGH        );

          if (thereWasAGeneralError)
            continue;                                  /* continue ! */
          else
            ;
        /*end of if*/

          if ( nowMayProcessTheParametersToRemoteProcedure )
            {                                /* OK, do procedure, */
                                             /* process arguments */

              switch ( (int) parserResponderReturnArguments.
                               remoteProcedureNumber         )
                {
                case Constants.PMAPPROC_NULL:
                  sendProcedureNullRPCReplyMessage(
                    parserResponderReturnArguments );
                  break;
                case Constants.PMAPPROC_SET:
                  registerProgramMappingTupleAndSendRPCReplyMessage(
                    parserResponderReturnArguments                   );
                  break;
                case Constants.PMAPPROC_UNSET:
                  unregisterProgramMappingTupleAndSendRPCReplyMessage(
                    parserResponderReturnArguments                   );
                  break;
                case Constants.PMAPPROC_GETPORT:
                  getPortAndSendRPCReplyMessage(
                    parserResponderReturnArguments );
                  break;
                case Constants.PMAPPROC_DUMP:
                  dumpPortsAndSendRPCReplyMessage(
                    parserResponderReturnArguments );
                  break;
                case Constants.PMAPPROC_CALLIT:
                  sendProcedureUnavailableRPCReplyMessage(
                    parserResponderReturnArguments,
                    "PMAPPROC_CALLIT"                      );
                  break;
                default:
                  remoteProcedureNumberLong = new Long(
                    parserResponderReturnArguments.
                      remoteProcedureNumber            );
                  sendProcedureUnavailableRPCReplyMessage(
                    parserResponderReturnArguments,
                    remoteProcedureNumberLong.toString()   );
                  break;
                }
            /*end of switch*/

            }
          else /* there was an error */
            ;
        /*end of if on nowMayProcessTheParametersToRemoteProcedure*/

        }
    /*end of while-loop*/

      udpSocket.close();

  }
/*end of run*/


  public
  static
  void

  main( String[] args )
/*****************************************************************************/
/* Function name: main                                                       */
/* File name: Mapper.java                                                    */
/* Purpose: main method for the Mapper class                                 */
/* Parameters: agrument strings array                                        */
/* Returns:                                                                  */
/* Last modified: 10.07.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*****************************************************************************/
    {
      Mapper portmapper = null;
                                                          /* main */
/*---------------------------------------------------------------------------*/

      try
        {
          portmapper = new Mapper( Constants.PROPERTIES_FILE_PATH );
        }
      catch( SocketException e )
        {
          System.out.println(
            "can\'t create io.UdpSocket: " +
            e.getMessage()                      );
          System.exit( 1 );
        }
    /*end of try-catch*/

      portmapper.run();
      System.exit( 0 );

    }
/*end of main*/


} /* end of Mapper class */

/*****************************************************************************/
/* End of file: Mapper.java                                                  */
/*****************************************************************************/
