/*************************************************************************

  Copyright (c) 1997 Novell, Inc.  All Rights Reserved.

  With respect to this file, Novell hereby grants to Developer a 
  royalty-free, non-exclusive license to include this sample code 
  and derivative binaries in its product. Novell grants to Developer 
  worldwide distribution rights to market, distribute or sell this 
  sample code file and derivative binaries as a component of 
  Developer's product(s).  Novell shall have no obligations to 
  Developer or Developer's customers with respect to this code.
  
  DISCLAIMER:
  
  Novell disclaims and excludes any and all express, implied, and 
  statutory warranties, including, without limitation, warranties 
  of good title, warranties against infringement, and the implied 
  warranties of merchantibility and fitness for a particular purpose.  
  Novell does not warrant that the software will satisfy customer's 
  requirements or that the licensed works are without defect or error 
  or that the operation of the software will be uninterrupted.  
  Novell makes no warranties respecting any technical services or 
  support tools provided under the agreement, and disclaims all other 
  warranties, including the implied warranties of merchantability and 
  fitness for a particular purpose. */

/**************************************************************************
   IPXS_TL1.C
***************************************************************************

  IPXS_TL1.C works in conjunction with IPXC_TL1.C to demonstrate IPX
  connectionless mode communication between two NLMs.  IPXS_TL1.C (the
  "server" NLM) receives a message from IPXC_TL1.NLM and then returns the
  same message.

  Complete the following steps to run this example:

  1.  Load IPXS_TL1.NLM on a test server, entering the server's name as
      a command-line parameter (ex. LOAD A:IPXS_TL1 MY_TEST_SERVER). The
      server will respond with the following message:

      Server: MY_TEST_SERVER is ready ...

  2.  Load IPXC_TL1.NLM on another test server, entering the previous
      server's name and a test message as command-line paramters (ex.
      LOAD IPXC_TL1 MY_TEST_SERVER "My Test Message")

  3.  Unload IPXS_TL1.NLM when the program has finished running
      (IPXC_TL1.NLM unloads automatically).

  NOTES:
 
  1.  QMK386.EXE can be used to generate a Watcom makefile for this 
      example:     QMK386 IPXS_TL1

  2.  CALNLM32.NLM, CLXNLM32.NLM, TLI.NLM and IPXS.NLM must be running on 
      the server for this example to work properly.

**************************************************************************/ 

#include <stdio.h> 
#include <stdlib.h>
#include <string.h>
#include <nwcalls.h>
#include <nwclxcon.h>
#include <nwthread.h>   
#include <nwstring.h>
#include <fcntl.h>      
#include <nlm\tiuser.h>     
#include <nlm\nwsap.h>        
#include <nwconn.h>

#define TLI_TYPE     0x9000   /* Server type */

int    fh;
struct t_bind     *bnd;
struct t_unitdata *udata;
struct t_uderr    *uderr;
LONG   SAPhandle;

unsigned short dynamic_socket = 0x0000; /* zero value causes NetWare
                                           to open a socket dynamically 
                                           in the range 0x4000 - 0x7FFF */
void TerminateNLM(void)
{
   NWCCODE   ccode;

   if( SAPhandle )
      ShutdownAdvertising( SAPhandle );
   if( udata )
      t_free((char *)udata, T_UNITDATA);
   if( uderr )
      t_free((char *)uderr, T_UDERROR);
   if( bnd )
      t_free((char *)bnd, T_BIND);
   t_close( fh );

   /* Give the socket back to NetWare. */
   if   (dynamic_socket != 0x0000)
   {
      ccode=IpxCloseSocket(dynamic_socket);
      if (ccode)
      {
          printf("IpxCloseSocket: failed %d\n", ccode);
          exit(1);
      }
   }
}

query(void)
{
   /* Stub for simplicity */ 
}

main(int argc, char **argv)
{
   IPX_ADDR       *Addr;         /* address structure for SPX  */
   int            flags;
   NWCONN_HANDLE  connHandle, startConnHandle;
   NWCCODE        ccode;
   NWINET_ADDR    InetAddr;
   nuint          openState;
   nuint16        buffer;

   startConnHandle=0;   /* set to zero if you don't care which connection
                           should be used to resolve the name. */
   openState = NWCC_OPEN_UNLICENSED;

   if( argc != 2 ) 
   {
      printf("Usage: %s <server's name>\n", argv[0]);
      exit(1);
   }

   /* NWCallsInit() must be called before any other NetWare SDK functions */

   ccode = NWCallsInit (NULL,NULL);
   if (ccode) 
   {
      printf("NWCallsInit failed: %X\r\n", ccode);
      return;
   }

   /* Register function that will be executed when NLM exits
      or is unoaded */
   if( atexit( TerminateNLM ) != NULL )
   {
      printf("atexit failed");
      exit(1);
   }

   /* Get NetWare to assign us a dynamic socket number */
   ccode = IpxOpenSocket(&dynamic_socket);
   if (ccode)
   {
      printf("IpxOpenSocket: failed %d\n", ccode);
      return;
   }

   /* Open server's endpoint, no info needed */
   if((fh = t_open("/dev/nipx", O_RDWR, NULL)) == -1)
   {
      t_error("t_open");
      exit(1);
   }

   /* Allocate space for structures */
   bnd = (struct t_bind *)t_alloc(fh, T_BIND, T_ALL);
   udata = (struct t_unitdata *)t_alloc(fh,T_UNITDATA,T_ALL);
   uderr = (struct t_uderr *)t_alloc(fh, T_UDERROR, T_ALL);
   if( !bnd || !udata || !uderr )
   {
      t_error("t_alloc");
      exit(1);
   }

   /* Prepare bind structure and then call t_bind */
   bnd->addr.len = sizeof( IPX_ADDR );
   Addr = (IPX_ADDR *)bnd->addr.buf;

   /* Open a connection and obtain a connection handle */
   /* Note:  A connection is established to a server only for the
             convenience of reading the net address from the 
             Bindery.  This server connection is not necessary
             for the transport functions to work.      */

   ccode = NWCCOpenConnByName(
            /*  start Conn Handle */ startConnHandle,
            /*  Name of Server    */ (pnstr8) argv[1],
            /*  Name format       */ NWCC_NAME_FORMAT_BIND,
            /*  open state        */ openState,
            /*  Novell reserved   */ NWCC_RESERVED,
            /*  Connection Handle */ &connHandle);

   if (ccode)
   {
      printf( "\nNWCCOpenConnByName: failed %04x", ccode );
      exit(1);
   }

   ccode = NWCCGetConnInfo(connHandle, 
                           NWCC_INFO_CONN_NUMBER,
                           sizeof(buffer),
                           &buffer);
   if (ccode)
   {
      printf("NWCCGetConnInfo failed: %X\n",ccode);
   }

   /* Get internetwork address */
   ccode = NWGetInetAddr(connHandle, buffer, &InetAddr);
   if (ccode)
   {
      printf("NWGetInetAddr failed: %X\n",ccode);
   }

   /* Close the server connection */
   ccode = NWCCCloseConn(connHandle);
   if(ccode)
      printf("\nNWCCCloseConn: failed %04x", ccode);
   
   /* copy internetwork address to t_bind address buffer */
   memcpy(Addr->ipxa_net, InetAddr.networkAddr,
          sizeof(InetAddr.networkAddr));
   memcpy(Addr->ipxa_node, InetAddr.netNodeAddr,
          sizeof(InetAddr.netNodeAddr));

   *(WORD *)Addr->ipxa_socket = IntSwap(dynamic_socket);

   if( t_bind(fh, bnd, bnd) == -1 )
   {
      t_error("t_bind");
      exit(1);
   }

   /* Check if this is the right socket */
   if( *(WORD *)&bnd->addr.buf[10] != IntSwap(dynamic_socket) )
   {
      printf("Bound wrong address: %d != %d\n",
             *(WORD *)&bnd->addr.buf[10], IntSwap(dynamic_socket) );
      exit(1);
   }

   /* Advertise server on dynamic_socket */
   SAPhandle = AdvertiseService( TLI_TYPE, argv[1],
                                 IntSwap(dynamic_socket) );
   if( SAPhandle == NULL )
   {
      printf("AdvertiseService failed for: %s\n", argv[1]);
      exit(1);
   }

   for (;;)
   {
      printf("Server: %s is ready ...\n", argv[1]);
      /* Receive message from client */
      if( t_rcvudata(fh, udata, &flags) == -1 )
      {
         if( t_errno == TLOOK )
         {
            /* Error on previously sent datagram */
            if( t_rcvuderr(fh, uderr) == -1 )
            {
               t_error("t_rcvuderr");
               exit(1);
            }
            printf("Bad datagram, error=%d\n", uderr->error);
         }
         t_error("t_rcvudata");
         exit(1);
      }
      printf("Received: %s\n", udata->udata.buf);
      query();
      ThreadSwitch();
      if( t_sndudata(fh, udata) == -1 )
      {
         t_error("t_sndudata");
         exit(1);
      }
      printf("Sending: %s\n", udata->udata.buf);
   } 
}
