Company Logo

Annex 3 Socket Source Code

Below is socket source code that uses the Annex spooling service to send relay codes that enable an embeded product to be rebooted from a remote location or to down load its battery backed up RAM.

rreboot.h

#ifndef NULL
#define NULL 0
#endif
#define CNULL (char *)NULL

#ifndef BUFSIZ
#define BUFSIZ 512
#endif

#if		NDPTG > 0
#define MAX_SERIAL_PORTS 999	/* allow for DPTG ports */
#else
#define MAX_SERIAL_PORTS 72	/* allow for ANNEX 3 */
#endif

#define SERIAL 1
#define PARALLEL 2
#define AF_INET         2

rreboot.c

/*
 *	Include Files
 */

#include <sys/types.h>
#include "port/port.h"
#include "../libannex/api_if.h"
#include <netinet/in.h>
#include <strings.h>
#include <netdb.h>
#include <fcntl.h>

#include <signal.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include "rreboot.h"

/*
 *	External Definitions
 */

extern int errno;
extern char *getenv();

#ifndef BSDI
#ifndef FREEBSD
#ifndef SYS_V
#ifndef AIX
#ifndef LINUX
extern char *sprintf();
#endif
#endif
#endif
#endif
#endif


/*
 *	Global Data Declarations
 */

int debug = 0;			/* debugging level */
int so;				/* token for socket number */

char *prog;			/* name of this program */

char *file_printing;

INT32 bytes;			/* total bytes to send to printer */

/* Last message read from or sent to the Annex */
char buffer[BUFSIZ];
int buffer_count;

int block_send = 0;		/* if set, send in 1-255 byte blocks */
int do_oob_ack = 0;		/* if set, use old out-of-band ack */

char *sock_errors[] = {
#define SEND_FF 0
    "file \"%s\" aborted while sending formfeed to Annex",
#define SEND_DATA 1
    "file \"%s\" aborted while sending data to Annex",
#define SEND_FINAL_FF 2
    "error sending final formfeed to Annex",
#define SEND_PORTSET 3
    "error during Annex port select",
#define ACK_PORTSET 4
    "error during wait for ACK after port select",
#define ACK_FINAL 5
    "error during wait for final ACK"
};

/*
 *	Macro Definitions
 */

#ifndef BADSIG
#ifndef SIG_ERR
#define BADSIG (int (*)())-1
#else
#define BADSIG SIG_ERR
#endif
#endif

#define	ARGVAL (*++(*argv) || (--argc && *++argv))
#define ARGSTR(s)	*argv += strlen((s) = *argv);

#define SEND(buf,len,msg) \
    if (api_send(so,(buf),(len),API_NOFLAGS,app_nam,TRUE) != (len)) { \
    	fatal(CNULL, sock_errors[(msg)], file_printing); \
    }

#define SENDOOB(buf,len,msg) \
    if (api_send(so, (buf), (len), API_OOB, app_nam, TRUE) != (len)) { \
    	fatal(CNULL, sock_errors[(msg)], file_printing); \
    }

#define	RECV(buf,len,msg,ret) \
    if((ret = api_recv(so,(buf),(len),API_NOFLAGS,TRUE,app_nam)) < 0) { \
	if (debug > 1) \
	    printf("Error:  api_recv returned %d.\n",ret); \
	    fatal(CNULL, sock_errors[(msg)], file_printing); \
    } else if (debug > 1) \
	printf("Received %d bytes.  %02X %02X ...\n",ret,(buf)[0],(buf)[1]);

/*
 * Annex LPD protocol definitions
 */
#define ANNEX_SPOOL_CMD '\011'
#define ACK '\0'
#define NACK '\001'
#define FRAME '\002'

#define OFF		0	/* timer off */
#define ACK_WAIT	10	/* seconds to wait for initial ACK */
#define ACK_TIMEOUT	30	/* seconds to timeout final ACK */

/*
 * usage: display usage info and exit
 */
usage()
{
    fprintf(stderr,
	"Usage: %s -r#; relay_number1_thru_26\n",prog);
	exit(1);
}

/* 
 * fatal: print error message and give up
 */
void
fatal(perr,str,arg1)
char *perr,*str,*arg1;
{
    fprintf(stderr, "%s: ", prog);
    if (str != NULL)
	fprintf(stderr,str,arg1);
    if (perr != NULL) {
	if (str != NULL)
	    fprintf(stderr, ": ");
	    perror(perr);
    } else
	fprintf(stderr, "\n");
    exit(1);
}

/*
 * gotpipe: catch SIGPIPE and die
 */
void
gotpipe(dummy)
int dummy;
{
    fatal(CNULL,
	file_printing == NULL
	? "Annex connection was lost unexpectedly"
	: "Annex connection was lost during attempt to spool \"%s\"" ,
	file_printing);
}
main(argc,argv)
int argc;
char *argv[];
{
    char *printer = NULL, *annex = "annex", *ff = NULL, *port = NULL;
    int pport = 0;
    int gotl = 0;
    int gotp = 0;
    int type = SERIAL;	/* default, if nothing specified */
    int line = 64;
    int relay= 0;
    int MAX_RELAY_PORTS = 26;
    char opt; 
    /* FILE *userfile; */
    FILE *file;
    int i;
    int energize;
    int de_energize;

    prog = (prog = (char *)rindex(argv[0],'/')) ? ++prog : *argv;
    /*
     * Crack arguments
     */
    if (debug > 0 )
	printf("Entering argument switch, %d\n",argc);
	if (argc != 2)
	   usage(); 
	if (**(++argv) == '-')	/* found a flag, deal with it */
	    while (**argv && (opt = *++*argv) != '\0')
		switch (opt) {
		    case 'r':
			    if (!ARGVAL)
			    usage();
				
			    ARGSTR(port);
			    relay = atoi(port);
			    if (relay < 1 || relay > MAX_RELAY_PORTS)
				fatal(CNULL,
				"Relay must be in range (1-26), not %s",
				port);
			    energize = relay * 2 - 1;
			    de_energize = relay * 2 - 2;
			    break;
		    default:		/* unknown argument */
			    usage();
			    break;
		}


    if (debug > 0)
	printf("Relay port %s\n",port);
    make_connection(annex);
    set_annex_line(line, type);

    if (debug > 1)
	printf("Connection set-up is complete.\n");

    if (debug > 1)
	printf("Sending energize to relay %d.\n",energize);
    buffer[0] = (char) (relay * 2 - 1);
    buffer[1] = NULL;
    send_block(buffer, strlen(buffer), SEND_DATA);

    system("sleep 1");

    if (debug > 1)
	printf("Sending denergize to relay %d.\n",de_energize);
    buffer[0] = (char) (relay * 2 - 2);
    send_block(buffer, strlen(buffer), SEND_DATA);

	/*
	 * End-of-job handshake; wait for final ACK
	 */
	if (ack_ack())
		fatal(CNULL,"Annex didn't acknowledge final data",CNULL);

	exit (0);
}

/*
 * make_connection: establish TCP connection with designated Annex
 */
make_connection (hostname)
char *hostname;
{
    struct hostent *host;
    struct servent *serv;
    struct sockaddr_in sname;
    static char app_nam[]="rreboot:make_connection";

    if (signal(SIGPIPE, gotpipe) == BADSIG)
    	fatal("signal",CNULL,CNULL);

    bzero ((char *)&sname, sizeof(sname));

    if (debug > 1)
    	printf("Attempting to resolve host name \"%s\".\n",hostname);
    sname.sin_addr.s_addr = inet_addr(hostname);
    if (sname.sin_addr.s_addr != -1) {
    	sname.sin_family = AF_INET;
    } else {
    	host = gethostbyname(hostname);
    	if (host) {
	    sname.sin_family = host->h_addrtype;
	    bcopy(host->h_addr,
		(caddr_t)&sname.sin_addr,
		host->h_length);
    	} else
	    fatal(CNULL,
    		"can't find host address for Annex \"%s\"",
    		hostname);
    }

#ifdef DEBUG
    sname.sin_port = htons(5555);
#else
    if (debug > 1)
    	printf("Attempting to resolve service \"printer/tcp\".\n");
    serv = getservbyname("printer","tcp");
    if (serv == NULL)
    	fatal(CNULL,"can't get service to printer",CNULL);
    sname.sin_port = serv->s_port;
    if (debug > 1)
    	printf("\t-> resolved to port %d.\n",
    		ntohs(sname.sin_port));
#endif

#ifdef EXOS
    sin->sin_family = AF_INET;
    sin->sin_addr.s_addr = INADDR_ANY;
    sin->sin_port = 0;
#endif

    if (debug > 1)
    	printf("Opening connection to API layer.\n");
    if((so = api_open(IPPROTO_TCP, &sname, app_nam, TRUE)) < 0)
        exit(1);

#ifdef TLI			/* only bind for TLI, Sockets don't need bind*/
    if (debug > 1)
    	printf("Binding API address.\n");
    switch (api_bind(so, (struct t_bind **)0, (struct sockaddr_in *)0, app_nam, TRUE)) {
        case 0:
    	break;
        case 1:
    	exit(-1);
        case 2:
    	exit(1);
        default:
    	break;
    }
#endif

    if (debug > 1)
    	printf("Connecting to host through API.\n");
    switch (api_connect(so, &sname, IPPROTO_TCP, app_nam, TRUE)) {
        case 0:
    	break;
        case 1:
    	exit(-1);
        case 2:
    	exit(1);
        default:
    	break;
    }
}
/*
 * Tell the Annex which port to print on by creating a escape sequence
 * to select which port to print on:
 *
 *	  CMD TYPE ; UNIT ; BYTES \n
 *
 * CMD is \011 is for the new-style lpd, where you select, get an ack,
 * and then shovel data.
 *
 *					serial			parallel
 * TYPE is the port type:		1			2
 * UNIT is the unit number:		[1-MAX_SERIAL_PORTS]	1
 * (TYPE & UNIT are ASCII strings)
 */

set_annex_line(line, type)
int line;
int type;
{
	static char app_nam[]="rreboot:set_annex_line";

	(void)sprintf(buffer,"%c%d;%d;%ld\n",
			ANNEX_SPOOL_CMD,	/* command byte */
			type,			/* parallel or serial */
			line,			/* unit */
			bytes);

	if (debug > 1)
		printf("Sending port designator --\n\t%s",buffer);
	SEND(buffer, strlen(buffer), SEND_PORTSET);

	/* check if Annex supports ACKing */
	if (ack_wait(ACK_PORTSET))
		fatal(CNULL, "Annex can't access requested printer",CNULL);

	/* Check if count/buffers needed and supported */
	if (buffer_count > 1) {
		if (buffer[1] == 1) {
			block_send = 1;
			if (debug > 0)
				printf("Block send mode.\n");
		} else if (debug > 0)
			printf("Stream mode.\n");
	} else {
		do_oob_ack = 1;		/* backwards compatibility */
		if (debug > 0)
			printf("Backward compatibility mode.\n");
	}
}
/*
 * Send a block of data to the Annex.
 *
 * In block_send mode, this is a pair of bytes that encode an integer
 * in big-endian format in the range 0x0001 through 0xFFFF, followed by
 * 1 to 65536 bytes.  A count of 0x0000 is reserved for end-of-all-files
 * indication.
 *
 * When not in block_send mode, data are just streamed over.
 */
send_block(ptr,len,msg)
char *ptr;
int len,msg;
{
	char blockbuf[2];
	unsigned int piece;
	static char app_nam[]="rreboot:send_block";

	if (block_send) {
		while (len > 0) {
			piece = len;
			if (piece > 65535)
				piece = 65535;
			blockbuf[0] = piece / 256;
			blockbuf[1] = piece % 256;
			SEND(blockbuf,2,msg);
			SEND(ptr,piece,msg);
			len -= piece;
			ptr += piece;
			}
		}
	else
		SEND(ptr,len,msg);
}
/************** Annex LPD protocol handshaking routines **************/

/*
 * ack_alrm: catch SIGALRM and longjmp back
 */
void
ack_alrm(dummy)
int dummy;
{
	static char app_nam[]="rreboot:ack_alrm";

#ifdef SYS_V
	signal(SIGALRM,ack_alrm);
#endif
	(void)alarm(ACK_TIMEOUT);
	if (debug > 0)
		printf("ACK timeout -- going to re-try.\n");
	SEND("",1,ACK_FINAL);	/* detect lost connections! */
}

/*
 * Wait for an inline acknowledgement from the Annex
 *
 * return 0 if we get the ACK, 1 o/w.
 */
int
ack_wait(msg)
int msg;
{
	static char app_nam[]="rreboot:ack_wait";

	if (debug > 1)
		printf("Waiting for acknowledge.\n");
	RECV(buffer,BUFSIZ,msg,buffer_count);
	return (buffer_count <= 0 || buffer[0] != ACK);
}

/*
 * Send an OOB ACK and wait for an inline ACK back.
 * Use timeout to prevent waiting forever.
 *
 * return 0 if exit handshake went ok, 1 o/w.
 *
 * If in block_send mode, then send the magic end-of-all-blocks message
 * and wait for ACK (no OOB).  If old (pre R6.2) Annex detected in
 * set-up, then send an OOB message to terminate the data.  This part is
 * a little unclean.
 */

ack_ack()
{
	int gotack;
	static char app_nam[]="rreboot:ack_ack";

	if (block_send) {
		char endblock[2];

		if (debug > 0)
			printf("Sending End-Of-Blocks message.\n");
	/* send end-of-all-blocks message */
		endblock[0] = endblock[1] = '\0';
		SEND(endblock,2,ACK_FINAL);
		}
	else if (do_oob_ack) {
#ifdef MSG_OOB
		if (debug > 0)
			printf("Sending OOB message.\n");
	/* try to get the OOB past the end of the data for SysV */
		sleep(7);
	/* send OOB ACK to indicate EOF */
		SENDOOB("",1,ACK_FINAL);
	/* clear for recv's on broken 4.2 systems */
		SEND("",1,ACK_FINAL);
#else
		if (debug > 0)
			printf("No OOB possible -- just closing.\n");
	/* if no OOB available, then just trust that it got there. */
		return 0;
#endif
		}
	if (debug > 0)
		printf("Waiting for final ACK.\n");
	if (signal(SIGALRM, ack_alrm) == BADSIG)
		fatal("signal",CNULL,CNULL);
	(void)alarm(ACK_TIMEOUT);
	gotack = ack_wait(ACK_FINAL);
	(void)alarm(OFF);
	return gotack;
}

Below is socket source code that uses the Annex spooling service to send commands to an embeded product to reload its NVRAM and then it sends a file.

lobbram.c

/*
 *	Include Files
 */

#include <sys/types.h>
#include "port/port.h"
#include "../libannex/api_if.h"
#include <netinet/in.h>
#include <strings.h>
#include <netdb.h>
#include <fcntl.h>

#include <signal.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include "rreboot.h"

/*
 *	External Definitions
 */

extern int errno;
extern char *getenv();

#ifndef BSDI
#ifndef FREEBSD
#ifndef SYS_V
#ifndef AIX
#ifndef LINUX
extern char *sprintf();
#endif
#endif
#endif
#endif
#endif

/*
 *	Global Data Declarations
 */

int debug = 0;			/* debugging level */
int so;				/* token for socket number */

char *prog;			/* name of this program */

char *file_printing;

INT32 bytes;			/* total bytes to send to printer */

/* Last message read from or sent to the Annex */
char buffer[BUFSIZ];
int buffer_count;

int block_send = 0;		/* if set, send in 1-255 byte blocks */
int do_oob_ack = 0;		/* if set, use old out-of-band ack */

char *sock_errors[] = {
#define SEND_FF 0
    "file \"%s\" aborted while sending formfeed to Annex",
#define SEND_DATA 1
    "file \"%s\" aborted while sending data to Annex",
#define SEND_FINAL_FF 2
    "error sending final formfeed to Annex",
#define SEND_PORTSET 3
    "error during Annex port select",
#define ACK_PORTSET 4
    "error during wait for ACK after port select",
#define ACK_FINAL 5
    "error during wait for final ACK"
};

/*
 *	Macro Definitions
 */

#ifndef BADSIG
#ifndef SIG_ERR
#define BADSIG (int (*)())-1
#else
#define BADSIG SIG_ERR
#endif
#endif

#define	ARGVAL (*++(*argv) || (--argc && *++argv))
#define ARGSTR(s)	*argv += strlen((s) = *argv);

#define SEND(buf,len,msg) \
    if (api_send(so,(buf),(len),API_NOFLAGS,app_nam,TRUE) != (len)) { \
    	fatal(CNULL, sock_errors[(msg)], file_printing); \
    }

#define SENDOOB(buf,len,msg) \
    if (api_send(so, (buf), (len), API_OOB, app_nam, TRUE) != (len)) { \
    	fatal(CNULL, sock_errors[(msg)], file_printing); \
    }

#define	RECV(buf,len,msg,ret) \
    if((ret = api_recv(so,(buf),(len),API_NOFLAGS,TRUE,app_nam)) < 0) { \
    	if (debug > 1) \
	    printf("Error:  api_recv returned %d.\n",ret); \
	    fatal(CNULL, sock_errors[(msg)], file_printing); \
    } else if (debug > 1) \
    	printf("Received %d bytes.  %02X %02X ...\n",ret,(buf)[0],(buf)[1]);

/*
 * Annex LPD protocol definitions
 */
#define ANNEX_SPOOL_CMD '\011'
#define ACK '\0'
#define NACK '\001'
#define FRAME '\002'

#define OFF		0	/* timer off */
#define ACK_WAIT	10	/* seconds to wait for initial ACK */
#define ACK_TIMEOUT	30	/* seconds to timeout final ACK */

/*
 * usage: display usage info and exit
 */
usage()
{
    fprintf(stderr, "Usage: %s file\n", prog);
    exit(1);
}

/* 
 * fatal: print error message and give up
 */
void
fatal(perr,str,arg1)
char *perr,*str,*arg1;
{
    fprintf(stderr, "%s: ", prog);
    if (str != NULL)
    	fprintf(stderr,str,arg1);
    if (perr != NULL) {
    	if (str != NULL)
	    fprintf(stderr, ": ");
    	perror(perr);
    } else
    	fprintf(stderr, "\n");
    exit(1);
}

/*
 * gotpipe: catch SIGPIPE and die
 */
void
gotpipe(dummy)
int dummy;
{
    fatal(CNULL,
    	file_printing == NULL
    	? "Annex connection was lost unexpectedly"
    	: "Annex connection was lost during attempt to spool \"%s\"" ,
    	file_printing);
}
main(argc,argv)
int argc;
char *argv[];
{
        char *printer = NULL, *annex = "annex", *ff = NULL, *port = NULL;
        int pport = 0;
        int gotl = 0;
        int gotp = 0;
        int type = SERIAL;	/* default, if nothing specified */
    int line = 63;
    char opt; 
    FILE *userfile;
    int i;
    char *cmd = "lo 0\015";

    prog = (prog = (char *)rindex(argv[0],'/')) ? ++prog : *argv;
    /*
     * Crack arguments
     */
    if (argc != 2)
       usage(); 
    
/*
 * Note that we might have problems here if the file extends itself
 * after we've counted up the bytes and the socket to the Annex doesn't
 * linger and the Annex requests non-blocked data -- the Annex will ACK
 * this program N bytes early, and it might exit before its time.
 * This is just too bad.
 */
    bytes = 0L;
    	userfile = fopen(argv[1],"r");
    	if (userfile == NULL) {
	    perror(argv[1]);
	    argv[1] = NULL;
    	}
    	else if (fseek(userfile,0L,2) < 0) {
    /* Darn.  Looks like we can't count the number of bytes. */
	    perror(argv[1]);
	    bytes = 0;
	    fclose(userfile);
    	}
    	else {
	    bytes += ftell(userfile);
	    fclose(userfile);
    	}

    if (debug > 0)
    	printf("Connecting to Annex %s port %d\n",annex,line);
    make_connection(annex);
    set_annex_line(line, type);

    if (debug > 1)
    	printf("Connection set-up is complete.  Handling %d files.\n",argc);

    /* Tell CTU to do a load of file starting at address 0, BBRAM */
    send_block(cmd, strlen(cmd), SEND_DATA);
    system("sleep 1");

    userfile = fopen(argv[1], "r");
    if (userfile == NULL)
	perror(argv[1]);
    else {
	(void)print_file(argv[1],userfile);
	(void)fclose(userfile);
    }

    exit (0);
}
/*
 * print_file: spool file over TCP connection
 */
print_file(name,file)
char *name;
FILE *file;
{
    file_printing = name;

    if (debug > 0)
    	printf("Printing file \"%s\".\n",name);

    for (;;) {
    	buffer_count = fread(buffer, sizeof(*buffer), BUFSIZ, file);
#ifdef DEBUG
fprintf(stderr,"object size=%d, buffer size=%d, length=%d\nbuffer=>>s<<n",
sizeof(*buffer),BUFSIZ,len,len,buffer);
#endif
    	/* fread should never return <0, but... */
    	if (buffer_count <= 0)
	    break;
    	send_block(buffer, buffer_count, SEND_DATA);
	system("sleep 1");
    }

    return(0);
}
/*
 * make_connection: establish TCP connection with designated Annex
 */
make_connection (hostname)
char *hostname;
{
    struct hostent *host;
    struct servent *serv;
    struct sockaddr_in sname;
    static char app_nam[]="aprint:make_connection";

    if (signal(SIGPIPE, gotpipe) == BADSIG)
    	fatal("signal",CNULL,CNULL);

    bzero ((char *)&sname, sizeof(sname));

    if (debug > 1)
    	printf("Attempting to resolve host name \"%s\".\n",hostname);
    sname.sin_addr.s_addr = inet_addr(hostname);
    if (sname.sin_addr.s_addr != -1) {
    	sname.sin_family = AF_INET;
    } else {
    	host = gethostbyname(hostname);
    	if (host) {
	    sname.sin_family = host->h_addrtype;
	    bcopy(host->h_addr,
    		(caddr_t)&sname.sin_addr,
    		host->h_length);
    	} else
    		fatal(CNULL,
		    "can't find host address for Annex \"%s\"",
		    hostname);
    }

#ifdef DEBUG
    sname.sin_port = htons(5555);
#else
    if (debug > 1)
    	printf("Attempting to resolve service \"printer/tcp\".\n");
    serv = getservbyname("printer","tcp");
    if (serv == NULL)
    	fatal(CNULL,"can't get service to printer",CNULL);
    sname.sin_port = serv->s_port;
    if (debug > 1)
    	printf("\t-> resolved to port %d.\n",
	    ntohs(sname.sin_port));
#endif

#ifdef EXOS
    sin->sin_family = AF_INET;
    sin->sin_addr.s_addr = INADDR_ANY;
    sin->sin_port = 0;
#endif

    if (debug > 1)
    	printf("Opening connection to API layer.\n");
    if((so = api_open(IPPROTO_TCP, &sname, app_nam, TRUE)) < 0)
        exit(1);

#ifdef TLI			/* only bind for TLI, Sockets don't need bind*/
    if (debug > 1)
    	printf("Binding API address.\n");
    switch (api_bind(so, (struct t_bind **)0, (struct sockaddr_in *)0, app_nam, TRUE)) {
        case 0:
    	break;
        case 1:
    	exit(-1);
        case 2:
    	exit(1);
        default:
    	break;
    }
#endif

    if (debug > 1)
    	printf("Connecting to host through API.\n");
    switch (api_connect(so, &sname, IPPROTO_TCP, app_nam, TRUE)) {
        case 0:
    	break;
        case 1:
    	exit(-1);
        case 2:
    	exit(1);
        default:
    	break;
    }
}
/*
 * Tell the Annex which port to print on by creating a escape sequence
 * to select which port to print on:
 *
 *	  CMD TYPE ; UNIT ; BYTES \n
 *
 * CMD is \011 is for the new-style lpd, where you select, get an ack,
 * and then shovel data.
 *
 *					serial			parallel
 * TYPE is the port type:		1			2
 * UNIT is the unit number:		[1-MAX_SERIAL_PORTS]	1
 * (TYPE & UNIT are ASCII strings)
 */

set_annex_line(line, type)
int line;
int type;
{
    static char app_nam[]="aprint:set_annex_line";

    (void)sprintf(buffer,"%c%d;%d;%ld\n",
    	ANNEX_SPOOL_CMD,	/* command byte */
    	type,			/* parallel or serial */
    	line,			/* unit */
    	bytes);

    if (debug > 1)
    	printf("Sending port designator --\n\t%s",buffer);
    SEND(buffer, strlen(buffer), SEND_PORTSET);

    /* check if Annex supports ACKing */
    if (ack_wait(ACK_PORTSET))
    	fatal(CNULL, "Annex can't access requested printer",CNULL);

    /* Check if count/buffers needed and supported */
    if (buffer_count > 1) {
    	if (buffer[1] == 1) {
	    block_send = 1;
	    if (debug > 0)
    		printf("Block send mode.\n");
    	} else if (debug > 0)
	    printf("Stream mode.\n");
    } else {
    	do_oob_ack = 1;		/* backwards compatibility */
    	if (debug > 0)
	    printf("Backward compatibility mode.\n");
    }
}
/*
 * Send a block of data to the Annex.
 *
 * In block_send mode, this is a pair of bytes that encode an integer
 * in big-endian format in the range 0x0001 through 0xFFFF, followed by
 * 1 to 65536 bytes.  A count of 0x0000 is reserved for end-of-all-files
 * indication.
 *
 * When not in block_send mode, data are just streamed over.
 */
send_block(ptr,len,msg)
char *ptr;
int len,msg;
{
    char blockbuf[2];
    unsigned int piece;
    static char app_nam[]="aprint:send_block";

    if (block_send) {
    	while (len > 0) {
	    piece = len;
	    if (piece > 65535)
    		piece = 65535;
	    blockbuf[0] = piece / 256;
	    blockbuf[1] = piece % 256;
	    SEND(blockbuf,2,msg);
	    SEND(ptr,piece,msg);
	    len -= piece;
	    ptr += piece;
    	}
    	}
    else
    	SEND(ptr,len,msg);
}
/************** Annex LPD protocol handshaking routines **************/

/*
 * ack_alrm: catch SIGALRM and longjmp back
 */
void
ack_alrm(dummy)
int dummy;
{
    static char app_nam[]="aprint:ack_alrm";

#ifdef SYS_V
    signal(SIGALRM,ack_alrm);
#endif
    (void)alarm(ACK_TIMEOUT);
    if (debug > 0)
    	printf("ACK timeout -- going to re-try.\n");
    SEND("",1,ACK_FINAL);	/* detect lost connections! */
}

/*
 * Wait for an inline acknowledgement from the Annex
 *
 * return 0 if we get the ACK, 1 o/w.
 */
int
ack_wait(msg)
int msg;
{
    static char app_nam[]="aprint:ack_wait";

    if (debug > 1)
    	printf("Waiting for acknowledge.\n");
    RECV(buffer,BUFSIZ,msg,buffer_count);
    return (buffer_count <= 0 || buffer[0] != ACK);
}

/*
 * Send an OOB ACK and wait for an inline ACK back.
 * Use timeout to prevent waiting forever.
 *
 * return 0 if exit handshake went ok, 1 o/w.
 *
 * If in block_send mode, then send the magic end-of-all-blocks message
 * and wait for ACK (no OOB).  If old (pre R6.2) Annex detected in
 * set-up, then send an OOB message to terminate the data.  This part is
 * a little unclean.
 */

ack_ack()
{
    int gotack;
    static char app_nam[]="aprint:ack_ack";

    if (block_send) {
    	char endblock[2];

    	if (debug > 0)
	    printf("Sending End-Of-Blocks message.\n");
    /* send end-of-all-blocks message */
    	endblock[0] = endblock[1] = '\0';
    	SEND(endblock,2,ACK_FINAL);
    }
    else if (do_oob_ack) {
#ifdef MSG_OOB
    	if (debug > 0)
	    printf("Sending OOB message.\n");
    /* try to get the OOB past the end of the data for SysV */
    	sleep(7);
    /* send OOB ACK to indicate EOF */
    	SENDOOB("",1,ACK_FINAL);
    /* clear for recv's on broken 4.2 systems */
    	SEND("",1,ACK_FINAL);
#else
    	if (debug > 0)
	    printf("No OOB possible -- just closing.\n");
    /* if no OOB available, then just trust that it got there. */
    	return 0;
#endif
    	}
    if (debug > 0)
    	printf("Waiting for final ACK.\n");
    if (signal(SIGALRM, ack_alrm) == BADSIG)
    	fatal("signal",CNULL,CNULL);
    (void)alarm(ACK_TIMEOUT);
    gotack = ack_wait(ACK_FINAL);
    (void)alarm(OFF);
    return gotack;
}