rcookie...

The client-server fortune cookie solution.

By John Weeks


Red Bullet  Introduction To The Fortune Cookie Problem

Nearly everyone that I know enjoys reading the little messages that are enclosed in fortune cookies. Fortune cookies are so popular that the computer equivalent, the fortune program, has become a standard feature of nearly every variant of UNIX.

Most variants of UNIX have a small but entertaining fortune cookie database pre-installed. Additional fortune cookie files have been posted to USENET over the years, and a large collection is on-line at UUNET. As a result, a dedicated fortune cookie fan can accumulate a rather large amount of fortune cookie data. This can become a real problem when your site has a large number of UNIX machines. It is a time sink to have to update a lot of machines, and it also consumes a great deal of disk space to have a cookie database replicated on a number of different machines.


Red Bullet  A Solution To The Fortune Cookie Problem

The obvious solution to the fortune cookie problem is to have a centralized fortune cookie repository, and a tool that can access this cookie warehouse from across the network. The remainder of this document describes the implementation of just such a system.


Red Bullet  The Fortune Cookie Protocol

The cookie protocol is very simple—the client sends a single one character message to the server, and the server sends back a single message up to 64K in length. The cookie service runs from the Internet Super-daemon (inetd) on UDP port #17.


Red Bullet  The Fortune Cookie Server Program

/* *** ------------------------------------------------------------ *** */
/* *** File:	cookied.c					    *** */
/* *** ------------------------------------------------------------ *** */
/* *** Written by Bart Massey *** */
/* *** Bug fix by Chris McNeil *** */

#include <stdio.h>
#include <syslog.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define FORTUNEBUFSIZE  (64 * 1024)
char	fbp[ FORTUNEBUFSIZE ];

/* *** Change this definition to match your local system *** */
#define FORTUNEPROG     "/usr/games/fortune -a"

/* *** ------------------------------------------------------------ *** */
/* *** Main Procedure: respond to cookie requests from the network  *** */
/* *** ------------------------------------------------------------ *** */

main( argc, argv )
  int	argc;
  char	**argv;
{
  int	i;
  FILE	*pfile;
  int	fromlen;
  struct sockaddr_in from;

  fromlen = sizeof( from );

/* *** Read the one character request from the client *** */
/* *** Specific content is ignored *** */

  if ( recvfrom( 0, fbp, FORTUNEBUFSIZE, 0, (struct sockaddr *) &from,
	&fromlen ) == -1 )
  {
    exit( 1 );
  }

/* *** Connect with the local fortune cookie program *** */
/* *** via a pipe connection. *** */

  pfile = popen( FORTUNEPROG, "r" );

  if ( !pfile )
  {
        exit( 1 );
  }

/* *** Read the fortune from the pipe char by char and save in a buffer *** */

  for ( i = 0; FORTUNEBUFSIZE; )
  {
    fbp[i] = getc( pfile ); 

    if ( fbp[i] == EOF )
    {
      fbp[i] = '\0'; 
      break;
    }
    i++;
  }

  pclose( pfile );  

/* *** Send the cookie buffer back to the client in one shot *** */

  sendto( 0, fbp, i + 1, 0, &from, fromlen );

  exit( 0 );
}

/* *** ------------------------------------------------------------ *** */
/* *** End Of File:	cookied.c				    *** */
/* *** ------------------------------------------------------------ *** */

Red Bullet  The Fortune Cookie Client Program

/* *** ------------------------------------------------------------ *** */
/* *** File:	rcookie.c					    *** */
/* *** ------------------------------------------------------------ *** */
/* *** Written by John Weeks *** */
/* *** Based on UCB "rdate" command *** */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>      
#include <netdb.h>
#include <stdio.h>

#define FORTUNEBUFSIZE  (64 * 1024)
char buf[ FORTUNEBUFSIZE ];

/* *** Change this to be your local cookie server *** */
#define DEFAULTHOST "abc.yourhost.com"

/* *** ------------------------------------------------------------ *** */
/* *** Main Procedure: check args and locate host name		    *** */
/* *** ------------------------------------------------------------ *** */

main ( argc, argv )
  int	argc;
  char	*argv[];
{
  if ( argc == 1 ) 
    DoRemoteCookie( DEFAULTHOST );
  else
    DoRemoteCookie( argv[1] );
}

/* *** ------------------------------------------------------------ *** */
/* *** DoRemoteCookie: contact remote cookie server & get fortune *** */
/* *** ------------------------------------------------------------ *** */

int DoRemoteCookie( host )
  char	*host;
{
  struct	hostent		*hname;		/* /etc/hosts table entry */
  struct	servent		*sname;		/* /etc/service file entry */
  struct	sockaddr_in	socaddr;	/* socket address */
  int		soc;				/* network file descriptor */

/* *** Look up the host name in /etc/hosts or via DNS *** */

  if ( ( hname = gethostbyname( host ) ) == NULL )
  {
    fprintf( stderr, "rcookie: Unknown host %s\n", host );
    return( -1 );
  }

/* *** Look up the cookie service in /etc/services *** */

  if ( ( sname = getservbyname( "cookie", "udp" ) ) == NULL ) 
  {
    fprintf( stderr, "rcookie: cookie/udp: unknown service\n" );
    return( -1 );
  }

/* *** Open a network socket, type DGRAM since service is UDP *** */

  if ( ( soc = socket( AF_INET, SOCK_DGRAM, 0, 0 ) ) < 0 )
  {
    perror( "rcookie" );
    return( -1 );
  }

/* *** Set up network structures in preparation for connect call *** */

  socaddr.sin_family = hname->h_addrtype;      
  bcopy( hname->h_addr, (caddr_t) &socaddr.sin_addr, hname->h_length );
  socaddr.sin_port = sname->s_port;

/* *** Connect to the cookie daemon on remote host *** */

  if ( connect( soc, (caddr_t) &socaddr, sizeof( socaddr ), 0 ) < 0 )
  {
    perror( "cookie" );
    close( soc );
    return( -1 );
  }

/* *** Write anything to the cookie daemon to wake it up *** */

  if ( write( soc, "x", 1 ) != 1 )
  {
    perror( "rcookie" );
    close( soc );
    return( -1 );
  }

/* *** Read the returned fortune cookie into a buffer *** */

  if ( read( soc, &buf[ 0 ], FORTUNEBUFSIZE ) < 1 )
  {
    perror( "rcookie" );
    close( soc );
    return( -1);
  }

/* *** Send the fortune cookie in buffer out to screen *** */

  printf( "%s", buf );

/* *** Close socket before exiting, then return good completion code *** */

  close( soc );
  return( 0 );
}

/* *** ------------------------------------------------------------ *** */
/* *** End Of File:	rcookie.c				    *** */
/* *** ------------------------------------------------------------ *** */

Red Bullet  Building And Installing The Fortune Cookie System

Under Construction


Home Return To John Weeks Hobby Page
Home Return To John Weeks Home Page
Authored by John A. Weeks III, Copyright © 1997, all rights reserved.
For further information, contact: john@johnweeks.com