/* This module demonstrates how to use the Novell client libraries to establish an SPX connection to a network service. Once the connection is established whatever the user types is sent through the connection and whatever the other side sends is displayed to the screen. This was originally taken from one of the examples supplied with the client libs... SPXCHAT or somesuch. */ #define NUM_ECBS 6 #define MAX_RECEIVE_ECBs 6 #include #include #include #include #include #include #include #include #include #include #include #include /* API headers */ #include #include #include #include "spxtype.h" /* receive queue type; just a queue of pending ecbs */ typedef struct { ECB *ecb; } Qent; typedef union { struct { int key; } bkey; struct { char key_code; char scan; } skey; } key_press; /* ************************* FUNCTION PROTYPES *********************** */ void main( int, char **) ; void Initialize(int) ; void Usage( void ) ; int MakeConnection( void ) ; void CreateReceiveECBsandPost( void ) ; void CreateConnectionECBandHeader( void ) ; void CreateSendECBandHeader( void ) ; void CreateReceiveECBandHeader( int ) ; void CreateListenECBandHeader( void ); void SendReceiveChar( void ) ; void Terminate( void ) ; int read_keyboard (void); int spxlist (void); int spxfind (char *); int spxcon (int); void ctrlc_handler (int); void far cdecl ReceiveESRHandler( ) ; void far cdecl GotConnection( ) ; void cdecl ProcessReceivedData( ECB * ) ; void cdecl SetConnId( ECB * ) ; void die (char *, int); void q_init (void); /* ************************ Global Variables **************************** */ ECB *sendECB, *receiveECB, *connectionECB, *listenECB ; SPXHeader *receiveHeader, *sendHeader, *listenHeader; int socket; /* Socket number used by this program. */ BYTE done ; /* Loop flag to break loop. */ BYTE rec_data[MAX_RECEIVE_ECBs][576]; /* receive buffers */ Qent bufq[MAX_RECEIVE_ECBs]; /* receive queue */ Qent *write_ptr; /* where the spx driver is writing */ Qent *read_ptr; /* where the application is reading */ Qent *endq; Qent *begq; char Psname[48]; BYTE lasterr = 0; BYTE send_bf[576]; /* send frag buf in for send ecb */ WORD *send_bf_len; /* pointer to send frag size */ WORD spxConnectionID; /* spx connection id number */ char majrev = 1; char minrev = 5; volatile int Connected = 0; /* ************************* Program Start ****************************** */ void main (int argc, char *argv[]) { int rval = 0; if (argc == 1) { /* help */ Usage (); goto quit; } else { if (IPXInitialize ()) { printf ("IPX is not installed.\n"); exit (0); } if (!strncmp (argv[1], "list", 4)) { /* list or listen */ if (argc == 2) /* list */ rval = spxlist (); else { /* listen */ strcpy (Psname, argv[2]); rval = spxcon (1); } } else { strcpy (Psname, argv[1]); rval = spxcon (0); } } if (rval) printf ("Error: result = %x\n", rval); quit: exit (rval); } int spxcon (int listen) { Initialize(listen); if (listen) { CreateListenECBandHeader (); printf ("Waiting for a connection... "); SPXListenForConnection((BYTE)0, (BYTE)0, listenECB); } else { MakeConnection(); if (connectionECB->completionCode == 0) { printf("Connection Established\n"); } else { die ("Could not establish connection. ", connectionECB->completionCode); } Connected = 1; } while (!Connected) { if (bioskey(1)) goto done; } if (listen) printf ("Connected!\n"); SendReceiveChar(); done: printf ("Terminating\n"); Terminate(); if (listen) ShutdownSAP(); IPXCloseSocket (socket); return lasterr; } /***************************** Initialize ***************************** This function simply checks to see if SPX is installed, sets the socket number up and opens the socket. ***********************************************************************/ void Initialize(int listen) { BYTE maj, min; WORD max, avail; int rval; done = 0 ; if ((rval = (int)(SPXInitialize((BYTE *)&maj, (BYTE *)&min, (WORD *)&max, (WORD *)&avail))) == SPX_NOT_INSTALLED) { die ("SPX not installed. ",rval); } socket = 0; /* get a dynamic socket number */ if (listen) { /* AdvertiseService does an IPXOpenSocket */ AdvertiseService (SPXTYPE, Psname, (BYTE *)&socket); printf ("Advertising on socket %x\n", IntSwap(socket)); } else if ((rval = (int) (IPXOpenSocket((BYTE *)&socket, (BYTE) SHORT_LIVED))) != 0) { die ("Could not open IPX socket. ", rval); } q_init (); if (signal (SIGINT, ctrlc_handler) == SIG_ERR) { perror ("signal failed"); exit (1); } CreateReceiveECBsandPost() ; CreateSendECBandHeader() ; } /* initialize the receive queue */ void q_init (void) { char i; /* read_ptr should always be behind write_ptr */ write_ptr = read_ptr = begq = bufq; endq = bufq + MAX_RECEIVE_ECBs; memset (bufq, 0, sizeof (bufq)); } /* Gets called asynchronously when we're listening and we get a connection. Set the spxConnectionID. */ void cdecl SetConnId (ECB *listECB) { spxConnectionID = *(WORD *)listECB->IPXWorkspace; Connected = 1; } /********************* CreateReceiveECBsandPost ********************* This function setups up 5 ECB's and their associated SPX packet headers. It posts a listen for each of the ECB's as well. These listens include those (2) used during the establish connection as well as those used to receive the characters. ********************************************************************/ void CreateReceiveECBsandPost( void ) { char i ; for(i = 0; i < MAX_RECEIVE_ECBs; i++) { CreateReceiveECBandHeader(i) ; SPXListenForSequencedPacket(receiveECB); } } void Usage( void ) { printf ("\nSPXCON Ver. %d.%d\n-------------------\n", majrev, minrev); printf ("spxcon list list available SPX services\n"); printf ("spxcon listen listen for a connection as name\n"); printf ("spxcon establish a connection to name\n"); } /********************** MakeConnection **************************** The sendECB that could be used to send the character and is also used to establish the connection. However the fragment count MUST be one to connect. So a special ECB and header were created here. ***************************************************************/ int MakeConnection() { CreateConnectionECBandHeader() ; SPXEstablishConnection((BYTE)3, (BYTE) 0xFF, &spxConnectionID, connectionECB); while (connectionECB->inUseFlag) kbhit() ; return (connectionECB->completionCode); } /* ******************* CreateConnectionECBandHeader ******************** Creates a special ECB and Header for connection purposes. The fragment count is one, unlike the sendECB's. ********************************************************************/ char valbuf[128]; void CreateConnectionECBandHeader() { BYTE more, flags; int rval; connectionECB = (ECB *) calloc(1, sizeof(ECB)) ; sendHeader = (SPXHeader *) calloc(1, sizeof(SPXHeader)) ; IPXGetDataAddress((BYTE *) sendHeader, (WORD *) &connectionECB->fragmentDescriptor[0].address); connectionECB->fragmentDescriptor[0].size = sizeof(SPXHeader); connectionECB->fragmentCount = 1 ; connectionECB->ESRAddress = (void (far *) () ) 0 ; connectionECB->socketNumber = socket ; rval = spxfind (Psname); if (!rval && (rval = (ReadPropertyValue (Psname, SPXTYPE, "NET_ADDRESS", 1, valbuf, &more, &flags))) != 0) { if (rval == 0xfc) printf ("%s is not an advertising service\n", Psname); die ("Bindery: Read Property failure. ", rval); } memcpy (&sendHeader->destination.network, valbuf, 4); memcpy (&sendHeader->destination.node, valbuf+4, 6); memcpy (&sendHeader->destination.socket, valbuf+10, 2); } /************ CreateListenECBandHeader ******************** Creates a special ECB and Header for listening purposes. The fragment count is one, unlike the sendECB's. **********************************************************/ void CreateListenECBandHeader( void ) { listenECB = (ECB *) calloc(1, sizeof(ECB)) ; listenHeader = (SPXHeader *) calloc(1, sizeof(SPXHeader)) ; listenECB->ESRAddress = (void (far *) ()) GotConnection; listenECB->socketNumber = socket; listenECB->fragmentCount = 1; IPXGetDataAddress((BYTE *) listenHeader, (WORD *) &listenECB->fragmentDescriptor[0].address) ; listenECB->fragmentDescriptor[0].size = sizeof(SPXHeader) ; } /* ************************ CreateSendECBandHeader ******************** Note the address of sendECB->fragment[0].address had to be sent to to IPXGetDataAddress. This function is used to retrieve the segment address as well as the offset address of the fragment. ********************** CreateSendECBandHeader ********************/ void CreateSendECBandHeader( void ) { sendECB = (ECB *) calloc(1, sizeof(ECB)) ; sendHeader = (SPXHeader *) calloc(1, sizeof(SPXHeader)) ; IPXGetDataAddress((BYTE *) sendHeader, (WORD *) &sendECB->fragmentDescriptor[0].address) ; IPXGetDataAddress((BYTE *) send_bf, (WORD *) &sendECB->fragmentDescriptor[1].address) ; sendECB->fragmentDescriptor[0].size = sizeof(SPXHeader); send_bf_len = &sendECB->fragmentDescriptor[1].size; *send_bf_len = sizeof(send_bf) ; sendECB->fragmentCount = 2 ; sendECB->ESRAddress = (void (far *) ()) 0 ; sendECB->socketNumber = socket ; } /************************ CreateReceiveECBandHeader **************** Note the address (&) of receiveECB->fragmentDescriptor[1].address needs to be sent down to IPXGetDataAddress. ********************************************************************/ void CreateReceiveECBandHeader(int ecbnum) { receiveECB = (ECB *) calloc(1, sizeof(ECB)); receiveHeader = (SPXHeader *) calloc(1, sizeof(SPXHeader)); receiveECB->ESRAddress = (void (far *) () ) ReceiveESRHandler; receiveECB->socketNumber = socket; receiveECB->fragmentCount = 2; IPXGetDataAddress((BYTE *) receiveHeader, (WORD *) &receiveECB->fragmentDescriptor[0].address); IPXGetDataAddress((BYTE *) rec_data[ecbnum], (WORD *) &receiveECB->fragmentDescriptor[1].address); receiveECB->fragmentDescriptor[0].size = sizeof(SPXHeader); receiveECB->fragmentDescriptor[1].size = sizeof(rec_data[ecbnum]); } /*************************** SendReceiveChar ************************ SendReceiveChar monitors the keyboard sending any characters. If data shows up in the receive queue it is printed then nulled out. One last check of the sendECB->inUseFlag is done before leaving as the sendECB is used to take down the connection. It can't be touched until the ECB is free for use! *********************************************************************/ void SendReceiveChar() { int tkey; while (!done) { /* Empty out the queue */ if (read_ptr != write_ptr) { int length; BYTE *ptr; ECB *ecb; while ((read_ptr != write_ptr) || read_ptr->ecb) { ecb = read_ptr->ecb; length = IntSwap (((SPXHeader *) (ecb->fragmentDescriptor[0].address))->length) - sizeof (SPXHeader); ptr = (BYTE *) ecb->fragmentDescriptor[1].address; for (;length > 0; length--, ptr++) { if (*ptr == '\n') { putchar ('\r'); } putchar (*ptr); } /* Post a listen on this ECB since we're done with the data */ SPXListenForSequencedPacket(read_ptr->ecb); /* adjust queue */ read_ptr->ecb = 0; read_ptr++; if (read_ptr == endq) read_ptr = begq; /* Send any user input */ if ((tkey = bioskey(1)) != 0) { if (tkey == -1) { /* ctrl-break */ done = 1; break; } else { while(sendECB->inUseFlag) IPXRelinquishControl(); if ((lasterr = sendECB->completionCode) != 0) { done = 1; } else { if (read_keyboard()) SPXSendSequencedPacket(spxConnectionID, sendECB); } } } } } /* Send any user input */ if (!done && ((tkey = bioskey(1)) != 0)) { if (tkey == -1) { /* ctrl-break */ done = 1; } else { while(sendECB->inUseFlag) IPXRelinquishControl(); if ((lasterr = sendECB->completionCode) != 0) { done = 1; } else { if (read_keyboard()) SPXSendSequencedPacket(spxConnectionID, sendECB); } } } } if (lasterr) { printf ("SPX send error: "); switch (lasterr) { case 0xec: { printf ("connection terminated.\n"); break; } case 0xed: { printf ("connection terminated poorly.\n"); break; } case 0xee: { printf ("invalid connection.\n"); break; } case 0xfc: { printf ("socket closed.\n"); break; } case 0xfd: { printf ("malformed packet.\n"); break; } default: { printf ("unknown completion code %x.\n", lasterr); break; } } lasterr = 0; } } /* keycodes */ #define ESCAPE 0x1b #define DEL 0x7f #define BS 0x08 /* ibm scan codes */ #define UP_ARROW 0x48 #define LEFT_ARROW 0x4b #define RIGHT_ARROW 0x4d #define DOWN_ARROW 0x50 /* Read values from the keyboard buffer and stuff them into send_bf */ int read_keyboard (void) { key_press key; key.bkey.key = bioskey(0); if (key.skey.key_code == 0) { /* funky key */ switch (key.skey.scan) { case UP_ARROW: { send_bf[0] = ESCAPE; send_bf[1] = '['; send_bf[2] = 'A'; *send_bf_len = 3; break; } case DOWN_ARROW: { send_bf[0] = ESCAPE; send_bf[1] = '['; send_bf[2] = 'B'; *send_bf_len = 3; break; } case RIGHT_ARROW: { send_bf[0] = ESCAPE; send_bf[1] = '['; send_bf[2] = 'C'; *send_bf_len = 3; break; } case LEFT_ARROW: { send_bf[0] = ESCAPE; send_bf[1] = '['; send_bf[2] = 'D'; *send_bf_len = 3; break; } default: { return 0; } } } else if (key.skey.key_code == BS) { send_bf[0] = DEL; /* send del for bs */ *send_bf_len = 1; } else { send_bf[0] = key.skey.key_code; *send_bf_len = 1; } done: return 1; } /********************** ProcessReceivedData ************************* This C function is called by the assembly routine, the ESR Hander. It should do as little as possible. Adds the usedECB to the queue and checks the completion code. *********************************************************************/ void cdecl ProcessReceivedData(ECB *usedECB) { BYTE ds; if ((lasterr = usedECB->inUseFlag) == 0) { if (usedECB->completionCode == 0) { if ((write_ptr == read_ptr) && (write_ptr->ecb)) { lasterr = 1; done = 1; } write_ptr->ecb = usedECB; write_ptr++; if (write_ptr == endq) write_ptr = begq; ds = ((SPXHeader *) (usedECB->fragmentDescriptor[0].address))->dataStreamType; if (ds == 0xFE) { /* end o' connection */ done = 1; } } else { lasterr = usedECB->completionCode; done = 1; } } else done = 1; } /****************************** Terminate ***************************** This function breaks down the connection in an orderly fashion. The IPXCloseSocket is not needed, as the socket was opened as a short lived socket. The fragment count must be 1 for the ECB used to terminate the connection. ***********************************************************************/ void Terminate() { while(sendECB->inUseFlag) /* Hang till clear */ IPXRelinquishControl(); sendECB->fragmentCount = 1 ; if (Connected) { SPXTerminateConnection( spxConnectionID, sendECB) ; while(sendECB->inUseFlag) /* Don't close socket till done */ IPXRelinquishControl(); } /*IPXCloseSocket(socket);*/ } void die (char *string, int rval) { printf ("%s", string); if (rval) printf ("result = %x\r\n", rval); exit (1); } /* Exit cleanly if the user hits ctrl-break */ void ctrlc_handler (int signal) { done = 1; Terminate (); exit (0); }