Revision 66d8e606a8d996ded60bc81d5edf319142a5fad9 authored by Ron Burkey on 04 October 2021, 11:49:55 UTC, committed by Ron Burkey on 04 October 2021, 11:49:55 UTC
2 parent s dfc2190 + 42c2282
Raw File
agc_utilities.c
/*
 * Copyright 2003-2005,2016 Ronald S. Burkey <info@sandroid.org>
 *
 * This file is part of yaAGC.
 *
 * yaAGC is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * yaAGC 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with yaAGC; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * In addition, as a special exception, Ronald S. Burkey gives permission to
 * link the code of this program with the Orbiter SDK library (or with
 * modified versions of the Orbiter SDK library that use the same license as
 * the Orbiter SDK library), and distribute linked combinations including
 * the two. You must obey the GNU General Public License in all respects for
 * all of the code used other than the Orbiter SDK library. If you modify
 * this file, you may extend this exception to your version of the file,
 * but you are not obligated to do so. If you do not wish to do so, delete
 * this exception statement from your version.
 *
 * Filename:	agc_utilities.c
 * Purpose:	Miscellaneous functions, useful for agc_engine or for other
 * 		yaAGC-family functions.
 * Compiler:	GNU gcc.
 * Contact:	Ron Burkey <info@sandroid.org>
 * Reference:	http://www.ibiblio.org/apollo/index.html
 * Mods:		04/21/03 RSB.	Began.
 * 		08/20/03 RSB.	Added uBit to ParseIoPacket.
 *		05/30/04 RSB	Various.
 *		07/12/04 RSB	Q is now 16 bits.
 *		01/31/05 RSB	Added the setsockopt call to
 *				EstablishSocket.
 *		02/27/05 RSB	Added the license exception, as required by
 *				the GPL, for linking to Orbiter SDK libraries.
 *		04/30/05 RSB	Added workaround for lack of Win32 close().
 *		05/14/05 RSB	Corrected website references.
 *		05/29/05 RSB	Added a couple of AGS equivalents for packet
 *				function.
 *		08/06/16 RSB    Added several error messages to help track
 *		                down why this is failing.  It turns out that
 *		                gethostbyname() no longer works (at least on
 *		                some Linux distributions) if statically
 *		                linked.  Therefore, the problem I was seeing
 *		                (namely, that yaAGC no longer listened on the
 *		                specified port on 64-bit Linux Mint and Ubuntu)
 *		                has actually been fixed in the makefile.
 *		11/18/15 RSB	Added unistd.h (for Solaris).  Added a couple
 *				of other header files for FreeBSD.
 *
 */

// ... and the project's includes.
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#ifndef WIN32
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#endif
#include <fcntl.h>
#include "yaAGCb1.h"

// Used for socket-operation error codes.
int ErrorCodes = 0;

//////////////////////////////////////////////////////////////////////////////////
// Functions for working with the data packets used for yaAGC "channel i/o".

//--------------------------------------------------------------------------------
// This function can take an i/o channel number and a 15-bit value for it, and
// constructs a 4-byte packet suitable for transmission to yaAGC via a socket.
// Space for the packet must have been allocated by the calling program.  
// Refer to the Virtual AGC Technical Manual, "I/O Specifics" subheading of the 
// "Developer Details" chapter.  Briefly, the 4 bytes are:
//      00pppppp 01pppddd 10dddddd 11dddddd
// where ppppppppp is the 9-bit channel number and ddddddddddddddd is the 15-bit
// value.  Finally, it transmits the packet. Returns 0 on success.
//
// ... Later, the 9-bit "Channel" is actually the u-bit plus an 8-bit channel
// number, but the function works the same.

int
FormIoPacket (int Channel, int Value, unsigned char *Packet)
{
  if (Channel < 0 || Channel > 077777)
    return (1);
  if (Value < 0 || Value > 077777)
    return (1);
  if (Packet == NULL)
    return (1);
  Packet[0] = Channel >> 3;
  Packet[1] = 0x40 | ((Channel << 3) & 0x38) | ((Value >> 12) & 0x07);
  Packet[2] = 0x80 | ((Value >> 6) & 0x3F);
  Packet[3] = 0xc0 | (Value & 0x3F);
  // All done.
  return (0);
}

//--------------------------------------------------------------------------------
// This function is the opposite of FormIoPacket:  A 4-byte packet representing
// yaAGC channel i/o can be converted to an integer channel-number and value.
// Returns 0 on success.

int
ParseIoPacket (unsigned char *Packet, int *Channel, int *Value, int *uBit)
{
  // Pick the channel number and value from the packet.
  if (0x00 != (0xc0 & Packet[0]))
    return (1);
  if (0x40 != (0xc0 & Packet[1]))
    return (1);
  if (0x80 != (0xc0 & Packet[2]))
    return (1);
  if (0xc0 != (0xc0 & Packet[3]))
    return (1);
  *Channel = ((Packet[0] & 0x1F) << 3) | ((Packet[1] >> 3) & 7);
  *Value = ((Packet[1] << 12) & 0x7000) | ((Packet[2] << 6) & 0x0FC0) |
    (Packet[3] & 0x003F);
  *uBit = (0x20 & Packet[0]);
  return (0);
}

/////////////////////////////////////////////////////////////////////////////////
// Portable functions (*NIX and Win32) for working with sockets.  

/*
  The usage is this:
  
  1. Servers:  Create a socket, suitable for clients to connect to, 
     with EstablishSocket().  Call accept(,NULL,NULL) to listen for a new
     client; the function will return whether or not there is a new
     listener, with either -1 (no new client) or else the new socket
     number (>=0); the first parameter is the socket number created by
     EstablishSocket().   The socket should be unblocked with UnblockSocket().
     
  2. Clients:  Connect to a server with CallSocket().  The function will
     return whether or not the connection succeeded, with either -1
     (failure) or else with the connection socket number.
     
  3. Either the client or server can then proceed to perform i/o using
     send() or recv() to all connected servers or clients.
  
  Server example:
  
     int ServerBaseSocket;
     #define MAX_LISTENERS 5
     int ListeningSockets[MAX_LISTENERS], NumListeners = 0;
     int PortNum = ... something ...;
     int i, j;
     
     ServerBaseSocket = EstablishSocket (PortNum, MAX_LISTENERS);
     if (ServerBaseSocket == -1)
       ... unrecoverable error ...
     // Main activity loop
     for (;;)
       {  
	 ...
	 // Periodically do this:
	 if (NumListeners < MAX_LISTENERS)
	   {
	     i = accept (ServerBaseSocket, NULL, NULL);
	     if (i != -1)
	       {
	         UnblockSocket (i);
	         Listeners[NumListeners++] = i;
	       }
	   }
	 ...
	 // Receive data.
	 for (i = 0; i < NumListeners; i++)
	   ... receive data from listener i using recv() ...
	 ...
	 // Send data.
	 for (i = 0; i < NumListeners; i++)
	   ... transmit data to listener i using send() ...
	 ...
      }
      
  Client Example:
  
    int ConnectionSocket = -1;
    
    // Main activity loop.
    for (;;)
      {
        ...
	// Try for a connection.
	if (ConnectionSocket == -1)
	  ConnectionSocket = CallSocket (Hostname, Portnum);
	...  
	// Perform i/o with send/recv:
	if (ConnectionSocket != -1)
	  ... send/recv ...
	...  
      }
      
  These examples don't illustrate what to do in case of broken 
  connections.  (I don't actually KNOW what to do.)
      
*/

//----------------------------------------------------------------------
// The following are the only two socket functions that internally differ
// between *nix and Win32.

// Initialize the socket system.  Return 0 on success.
static int SocketSystemInitialized = 0;
int
InitializeSocketSystem (void)
{
  if (SocketSystemInitialized)
    return (0);
  SocketSystemInitialized = 1;
#if defined(unix)
  return (0);
#else
  WSADATA wsaData;
  return (WSAStartup (MAKEWORD (2, 0), &wsaData));
#endif
}

// Set an existing socket to be non-blocking.
void
UnblockSocket (int SocketNum)
{
#if defined(unix)
  fcntl (SocketNum, F_SETFL, O_NONBLOCK);
#else
  unsigned long nonBlock = 1;
  ioctlsocket (SocketNum, FIONBIO, &nonBlock);
#endif
}

//----------------------------------------------------------------------
// Function for creating a socket.  Copied from
// http://world.std.com/~jimf/papers/sockets/sockets.html, and then
// modified somewhat for my own purposes.  The parameters:
//
//      portnum         The port on which we're going to listen.
//      MaxClients      Max number of queued connections.  (5 is good.)
//
// Returns -1 on error, or the new socket number (>=0) if successful.

#define MAXHOSTNAME 256
int
EstablishSocket (unsigned short portnum, int MaxClients)
{
  char myname[MAXHOSTNAME + 1];
  int s, i;
  struct sockaddr_in sa;
  struct hostent *hp;

  InitializeSocketSystem ();

  memset (&sa, 0, sizeof (struct sockaddr_in));	/* clear our address */
  gethostname (myname, MAXHOSTNAME);	/* who are we? */
  hp = gethostbyname (myname);	/* get our address info */
  if (hp == NULL)		/* we don't exist !? */
    {
      char s[32];
      switch (h_errno)
      {
      case HOST_NOT_FOUND: strcpy(s, "Host not found"); break;
      case NO_ADDRESS: strcpy(s, "No address"); break;
      //case NO_DATA: strcpy(s, "No data"); break;
      case NO_RECOVERY: strcpy(s, "No recovery"); break;
      case TRY_AGAIN: strcpy(s, "Try again"); break;
      default:
        sprintf(s, "Error %d", h_errno);
        break;
      }
      fprintf(stderr, "gethostbyname (\"%s\" %d) reports %s\n", myname, portnum, s);
      ErrorCodes = 0x101;
      return (-1);
    }
  sa.sin_family = hp->h_addrtype;	/* this is our host address */
  sa.sin_port = htons (portnum);	/* this is our port number */
  if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0)	/* create socket */
    {
      ErrorCodes = 0x102;
      return (-1);
    }
    
  // Make sure to clean up after any previous disconnects of the
  // port.  Otherwise there would be a timeout until we could
  // reuse the port.
  i = 1;
  setsockopt (s, SOL_SOCKET, SO_REUSEADDR, (const char *) &i, sizeof (int));
    
  if (bind (s, (struct sockaddr *) &sa, sizeof (struct sockaddr_in)) < 0)
    {
#ifdef unix
      close (s);
#else
      closesocket (s);
#endif
      ErrorCodes = 0x103;
      return (-1);		/* bind address to socket */
    }
  listen (s, MaxClients);	/* max # of queued connects */
  // Don't want to wait when there's no incoming data.
  UnblockSocket (s);
  return (s);
}

//----------------------------------------------------------------------
// Client connection to server via socket.
// http://world.std.com/~jimf/papers/sockets/sockets.html.
// The hostname is the name of the server, either resolvable by DNS,
// or else a dotted IP number.  (The latter fails on Win32.)
// The portnum is the port-number on which the server listens.

int
CallSocket (char *hostname, unsigned short portnum)
{
  struct sockaddr_in sa;
  struct hostent *hp;
  //int a;
  int s;

  InitializeSocketSystem ();

  if ((hp = gethostbyname (hostname)) == NULL)
    {
      /* do we know the host's */
      //errno= ECONNREFUSED; /* address? */
      ErrorCodes = 0x301;
      return (-1);		/* no */
    }

  memset (&sa, 0, sizeof (sa));
  memcpy ((char *) &sa.sin_addr, hp->h_addr, hp->h_length);	/* set address */
  sa.sin_family = hp->h_addrtype;
  sa.sin_port = htons ((u_short) portnum);
  if ((s = socket (hp->h_addrtype, SOCK_STREAM, 0)) < 0)	/* get socket */
    {
      ErrorCodes = 0x302;
      return (-1);
    }
  if (connect (s, (struct sockaddr *) &sa, sizeof (sa)) < 0)
    {
      /* connect */
#ifdef unix
      close (s);
#else
      closesocket (s);
#endif
      ErrorCodes = 0x303;
      return (-1);
    }
  UnblockSocket (s);
  return (s);
}
back to top