[comp.sources.misc] v03i012: SUN <-> DECNET mailer

darin@decwrl.dec.com@c.UUCP (Darin Johnson) (05/09/88)

comp.sources.misc: Volume 3, Issue 12
Submitted-By: "Darin Johnson" <darin@decwrl.dec.com@c.UUCP>
Archive-Name: dnamail

This mailer handles decnet mail to/from a Sun running Sunlink/DNI.

I am posting this mailer to both comp.sys.sun and comp.sources.misc.
The reason for both groups is that I used to have problems getting sources
from the comp.sys.sun archives, and I assume other people still have
this problem.

The following is a shar file, you should know what to do...

================  Cut, fold, or mutilate here ========================
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	Makefile
#	README
#	dnamail.doc
#	dnamail.h
#	dnamail.c
#	dnamaild.c
#	dnamisc.c
#	dnaerror.c
#	main.diff
#	sub.diff
# This archive created: Sun May  8 13:56:04 1988
export PATH; PATH=/bin:$PATH
echo shar: extracting "'Makefile'" '(415 characters)'
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
#
DISTRIB = Makefile README dnamail.doc dnamail.h dnamail.c dnamaild.c dnamisc.c \
	  dnaerror.c main.diff sub.diff

OBJS = dnaerror.o dnamisc.o

all: dnamail dnamaild

dnamail: dnamail.o $(OBJS)
	cc -o dnamail dnamail.o $(OBJS)

dnamaild: dnamaild.o $(OBJS)
	cc -o dnamaild dnamaild.o $(OBJS)

dnamail.o dnamaild.o dnamisc.o: dnamail.h

shar: $(DISTRIB)
	shar -cv $(DISTRIB) > dnamail.shar

clean:
	rm -f *.o core
SHAR_EOF
if test 415 -ne "`wc -c < 'Makefile'`"
then
	echo shar: error transmitting "'Makefile'" '(should have been 415 characters)'
fi
chmod +x 'Makefile'
fi # end of overwriting check
echo shar: extracting "'README'" '(877 characters)'
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
cat << \SHAR_EOF > 'README'
OK, here is my mailer for use with Sunlink/DNI.  You may use this with
or without sendmail, although using it with sendmail is much more convenient
(although it requires changes to the sendmail configuration files.)

1) Read dnamail.doc for instructions

2)
	The patch files should work with the default 'sendmail.cf' files
	off of the Sun distribution tape.  If not, you can install the
	patches by hand.  Be SURE to keep the ".orig" files around in
	case of problems.

4)
	Let me know of any problems you have setting this up, or of any bugs.
        This way I can either fix the code, or fix the documentation.

	Note the copyright restrictions (as small as they are).  Of course, 
        if Sun is negotiable, I may let them have it ;-)

Darin Johnson 
Lockheed Missiles & Space
	      (...lll-lcc.arpa!leadsv!laic!darin)
	      (...ucbvax!sun!sunncal!leadsv!laic!darin)
SHAR_EOF
if test 877 -ne "`wc -c < 'README'`"
then
	echo shar: error transmitting "'README'" '(should have been 877 characters)'
fi
chmod +x 'README'
fi # end of overwriting check
echo shar: extracting "'dnamail.doc'" '(9031 characters)'
if test -f 'dnamail.doc'
then
	echo shar: will not over-write existing file "'dnamail.doc'"
else
cat << \SHAR_EOF > 'dnamail.doc'
1) Intro

  Dnamail and dnamaild allow a Sun computer with the Sunlink/DNI software
  to send and receive mail from VMS computers (may work with other DECnet
  computers, but hasn't been tested).

  These programs cooperate with the sendmail program, so that users
  do not need to use any special commands to send and receive mail.
  This means that 'mailtool' may be used to read and compose messages.
  Dnamail may also be used independently of sendmail, although message
  editing is lacking.

2) Installation

  >>	The DNI software has a bug that needs to be fixed before
  >>	dnamail will work.  The bug is related to failure to read/write
  >>	zero-length records.  You can get the patch from Sun 
  >>	(bug #1007228), and possibly from the sun archives at rice.edu.
  >>	You could possibly get them from me, but save this as a last resort.

  Look over 'Makefile' and 'dnamail.h'.  If you want to have dnamaild
  run 'standalone', then uncomment the STANDALONE definition in
  dnamail.h.  When dnamaild is compiled with the STANDALONE option, then
  it will not automatically be called from dnaserver, but must be run
  before you expect any mail to show up.  It will handle one mail connection
  and then exit.  Obviously, this option is not too useful, but it helps
  if you are trying to debug.

  Do 'make all'.  This should make the executables 'dnamail' and 'dnamaild'.

  Put the executables into /usr/sunlink/dna, or use a symbolic link.

  Add the following line to /usr/sunlink/dna/dnaserver.reg (if you want
  to run dnamaild 'standalone', then you shouldn't do this):

     27	MAIL	/usr/sunlink/dna/dnamaild

  Modify sendmail.cf appropriately and restart sendmail.

  Test...  Before making sendmail.cf changes, you can try
  running dnamail directly - just type /usr/sunlink/dna/dnamail.
  After making the sendmail.cf changes, you can test by just sending
  mail.

3) Sendmail.cf changes

  In order to have dnamail and dnamaild cooperate with sendmail, changes
  must be made to /usr/lib/sendmail.cf. 

  >>  If you have made changes to sendmail.cf before, or no someone
  >>  who has, then the modifications will be relatively straightforward.
  >>  If you don't fit into the above category, then I suggest that you
  >>  read the 'Sendmail Installation and Operation' tutorial in the
  >>  Sun system administrators guide to familiarize yourself with
  >>  sendmail.

  On the Sun installation tape(s), sendmail.cf came in two forms,
  sendmail.main.cf and sendmail.subsidiary.cf.  With dnamail, there
  are four combinations - main, subsidiary, sub_decnet, and main_decnet.
  Remember to make a symbolic link to the correct file on each machine
  (see the Sun system administrators manual).

  'diff' output is included in this distribution showing the changes to
  be made to the original sendmail.main.cf and sendmail.subsidiary.cf
  so that they will handle DECnet.  If you have not changed your
  original .cf much from the original SMI-3.2 version, then the patches
  may work as is.  If not, then use the patches and the following
  instructions as a guide.  Even if patch works, you still need
  to read these instructions (two other files need changing).

  Briefly - use patch to create new version of the .cf files.  Then for
  every Sun that runs DNI, uncomment the 'DSdnahost' line for it (use
  a private copy!).

  REMEMBER:  Make backups of the original .cf files!!  It also helps
             to comment each of the lines you changed.

  Here are the basic changes.  Anything that looks like multiple spaces
  should actually be a tab.

  - in /etc/hosts, add 'dnahost' as an alternate name for the machine
    you want to be the DECnet relay.  For example, my site has:

	192.9.200.1	nova dnahost

    If you have more than one machine running Sunlink/DNI, then pick
    one machine to be the relay for machines without Sunlink/DNI.

  - Edit a file called "/usr/sunlink/dna/dnahosts" to contain a list of
    all VMS/DECnet nodes you want to send mail to, one node per line.
    You do not need to have ALL the VMS machines listed, just the ones
    you want to send mail to (a listed machine can deliver mail to a machine
    that you didn't list).

  - Add the following lines somewhere near the beginning of sendmail.main.cf
    and sendmail.subsidiary.cf.

	# Major decnet relay (relayed by #ether)
	DSdnahost
	# get list of decnet machines we want to be able to send mail to
	FS/usr/sunlink/dna/dnahosts

    The DS line defines a macro, $S, to be the name of the machine that will
    actually connect to DECnet.  Any machine that can't handle DECnet mail
    will forward any DECnet mail to this machine to handle it instead.
    If all your machines are on DECnet, then you don't need to do this.
    If you already have a $S macro (search for ^DS in vi), then choose an
    unused macro instead.

    The FS line defines a class, $S, that defines the VMS nodes
    we can send mail to with dnamail.
   
  - Add 'dna' as a trusted user.  Look for the following lines in
    sendmail.main.cf and sendmail.subsidiary.cf:

	Troot
	Tdaemon
	Tuucp

    and add the following line to these:

	Tdna

  - In sendmail.main.cf (only), look for the following lines (in ruleset 0):

	# resolve UUCP domain
	R<@$-.uucp>:$+		$#uucp	$@$1 $:$2	@host.uucp:...

    and add these next lines BEFORE them:

        ####################################################
        #  If $S (decnet gateway) is defined, then forward to $S, else
        #  resolve to dna mailer.
        ####################################################
        R$+<@$=S>		$?S $#ether $@$S $:$2!$1 $| $#dna $@$2 $:$1 $.
        R$+<@$=S.uucp>		$?S $#ether $@$S $:$2!$1 $| $#dna $@$2 $:$1 $.

    (If you aren't using 'S' as the macro for 'dnahost', then subsitute
    in the macro letter you are using).

  - In sendmail.subsidiary.cf (only), look for the following lines
    (in ruleset 0):

        # optimize names of known ethernet hosts
        R$*<@$*$%y.LOCAL>$*	$#ether $@$3 $:$1<@$2$3>$4	user@host.here

    and add these next lines BEFORE them:

        ####################################################
        #  If $S (decnet gateway) is defined, then forward to $S, else
        #  resolve to dna mailer.
        ####################################################
        R$+<@$=S>		$?S $#ether $@$S $:$2!$1 $| $#dna $@$2 $:$1 $.
        R$+<@$=S.uucp>		$?S $#ether $@$S $:$2!$1 $| $#dna $@$2 $:$1 $.

    (If you aren't using 'S' as the macro for 'dnahost', then subsitute
    in the macro letter you are using).

  - Add the following lines to sendmail.main.cf and sendmail.subsidiary.cf
    (at the end of the file, or grouped with the other mailer definitions):

	############################################################
	############################################################
	#####
	#####		DECnet Mailer specification
	#####
	#####	Messages processed by this configuration are assumed to leave
	#####   the internet domain.  Hence, they may not necessarily correspond
	#####	to RFC822 in all details.
	#####
	############################################################
	############################################################

	Mdna,	P=/usr/sunlink/dna/dnamail, F=mnSF, S=14, R=24,
		A=dnamail -f $f -n $h $u

	S14
	# none needed
	S24
	# none needed

  - Now copy sendmail.main.cf to sendmail.main_decnet.cf.  Also copy
    sendmail.subsidiary.cf to sendmail.sub_decnet.cf.  Edit both of
    these new files and change the following line:

	DSdnahost

    to:

	# DSdnahost

    (this comments the line out).  This one line should be the only change.
    Now for each machine that is a valid DECnet node, make a link from
    sendmail.cf (in /private/usr/lib for standard installations) to
    sendmail.main_decnet.cf or sendmail.sub_decnet.cf.  Depending upon
    whether the machine is a main mail machine or not, use one of the
    following commands:

	ln -s /usr/lib/sendmail.main_decnet.cf /private/usr/lib/sendmail.cf
    or
	ln -s /usr/lib/sendmail.sub_decnet.cf /private/usr/lib/sendmail.cf

  That's it!! After making the changes, you need to restart sendmail.
  Since sendmail does not have the capability of 're-reading' the 
  configuration file, you will have to stop the currently running
  sendmail, and then run it again (as root) with the same parameters
  as the old invocation (usually, /usr/lib/sendmail -bd -q1h).

4) Usage

  If the installation has gone smoothly, everything should
  be nearly transparent to users.

  To send mail from Sun to VAX, use an address like:

    vmsnode!user

  To send mail fromt VAX to Sun, use an address like:

    SUNNODE::"user"

  The quotes (") are necessary so that VMS mail does not convert
  everything to uppercase.  Longer addresses are just as easy:

    SUNNODE::"node1!node2!..."

  (In this case, leaving off the quotes causes the '!' to be treated as
  comment characters).  To set mail to be forwarded from the vax, you will
  have to use 3 quotes instead of one:

    SET FORWARD SOMESUN::"""someuser"""
SHAR_EOF
if test 9031 -ne "`wc -c < 'dnamail.doc'`"
then
	echo shar: error transmitting "'dnamail.doc'" '(should have been 9031 characters)'
fi
chmod +x 'dnamail.doc'
fi # end of overwriting check
echo shar: extracting "'dnamail.h'" '(460 characters)'
if test -f 'dnamail.h'
then
	echo shar: will not over-write existing file "'dnamail.h'"
else
cat << \SHAR_EOF > 'dnamail.h'
  /* these lines get compiled into every object file */
static char *version = "DnaMail (v1.1)";
static char *CopyRight 
	= "Copyright (C) D. B. Johnson, Feb., 1988; Lockheed Missiles & Space";

/* uncomment this if you want to run standalone */
/* #define STANDALONE */

/* local defines */
#define MAXLINE 256
#define MAIL_OBJECT 27
#define TMPFILE "/tmp/dnamail.XXXXXX"

#define TRUE 1
#define FALSE 0

/* external routines */
char *copystr(), *mailtime();
SHAR_EOF
if test 460 -ne "`wc -c < 'dnamail.h'`"
then
	echo shar: error transmitting "'dnamail.h'" '(should have been 460 characters)'
fi
chmod +x 'dnamail.h'
fi # end of overwriting check
echo shar: extracting "'dnamail.c'" '(11700 characters)'
if test -f 'dnamail.c'
then
	echo shar: will not over-write existing file "'dnamail.c'"
else
cat << \SHAR_EOF > 'dnamail.c'
/*************************************************************
 * DNAMAIL - Deliver mail to remote DECnet nodes.
 * Use interactively or with sendmail interface
 *
 * Copyright
 *  Darin Johnson, Lockheed Missiles and Space
 *
 * Permission to copy and/or modify as long as reference is made
 * to the authors.  This program may not be sold.
 *************************************************************/

#include <stdio.h>
#include <fcntl.h>
#include <pwd.h>
#include <ctype.h>
#include <sysexits.h>		/* the 'proper' exits to return to sendmail */
#include <sys/ioctl.h>
#include <netdna/dna.h>
#include <sys/time.h>
#include "dnamail.h"

/* list of strings */
struct clist {
  char *str;
  struct clist *next;
};

extern int errno;

char *use_str = "USAGE: %s [-d] [-s subject] [-n node] [address-list]\n";
#define usage() printf(use_str, argv[0])
  
char buff[MAXLINE];
char *subject;
char *node;
char debug;
char ttyflag;
char *from;
char *from_o;
int num_addr;
char *to_line;
int baduser_flag;

char **to;	/* char *to[] */

int ll;			/* DECnet file descriptor */

/* open DECnet connection to mail protocol.  Descriptor is in 'll' */
get_connection() {
  OpenBlock ob;			/* info for opening DECnet link */
  int ret;			/* return status code */

  if (debug)
    fprintf(stderr, "Trying to open /dev/dna\n");

  if ((ll = open("/dev/dna", O_RDWR)) < 0) {
    dnaerror("Open failed");
    exit(EX_SOFTWARE);
  }

  if (debug)
    fprintf(stderr, "Trying to get logical link\n");

  if (ioctl(ll, SES_GET_LINK, 0)) {
    dnaerror("Error getting logical link");
    exit_with_status();
  }

  if (debug)
    fprintf(stderr, "Trying to get link to mail server on remote node\n");
  
    /* set up open block with access information */
  strcpy(ob.op_node_name, node);  /* node we want to send mail to */
  ob.op_object_nbr = MAIL_OBJECT;
  ob.op_userid[0] = ob.op_account[0] = ob.op_password[0] = NULL;
  ob.op_opt_data.im_length = 0;
  if (ioctl(ll, SES_LINK_ACCESS, &ob) < 0) {
    dnaerror("link access");
    exit_with_status();
  }
  
  if (debug)
    fprintf(stderr, "Link established...\n");
  /* ll now contains descriptor to open DECnet connection to MAIL.EXE on
     node 'node' */
}

/* convert dna errors into errors sendmail can understand.  Then exit. */
exit_with_status() {
  switch(errno) {
  case NET_RESOUR:
  case NODE_DOWN:
  case NODE_UNREACH:
    exit(EX_TEMPFAIL);
  case NODE_NAME:
    exit(EX_NOHOST);
  case OBJ_NAME:
    exit(EX_UNAVAILABLE);
  default:
    exit(EX_PROTOCOL);
  }
}

/* close up connection */
drop_connection() {
  SessionData sd;		/* misc session info */

  sd.sd_reason = 0;
  sd.sd_data.im_length = 0;
  ioctl(ll, SES_DISCONNECT, &sd);
  close(ll);
  if (debug)
    fprintf(stderr, "Connection terminated by us\n");
}

/* Sends headers.  Collects headers from message into a list.  Then
   sends subject line (which terminates VMS header) followed by other
   headers (which are treated as part of the normal message by VMS).

   The actual reason this routine exists is to search for a Subject:
   line so it can be sent as the DECnet subject line.  Of course,
   later versions of the software might do more with these headers. */
send_headers() {
  struct clist *headers, *tail;

  if (ttyflag || subject) {
        /* if we are a tty or have an explicit subject */
        /* then we only want to send the subject rather than search for it */
      send(subject);
      return;
  }
  sprintf(buff, "Received: by %s; %s", version, mailtime());
    /* initialize list of headers */
  headers = (struct clist*)malloc(sizeof(struct clist));
  headers->next = NULL;
  headers->str = copystr(buff);
  tail = headers;
    /* read in headers */
  while (gets(buff) > 0) {
      /* add onto header list */
    tail->next = (struct clist*)malloc(sizeof(struct clist));
    tail = tail->next;
    tail->next=NULL;
    tail->str = copystr(buff);
    if (strlen(buff) == 0)	/* empty line means no more headers */
      break;
    if (!strncmp(buff, "Subject: ", 9)) {	/* found the Subject: */
      subject = &tail->str[9];
    }
  }
    /* now write out header lines */
  send(subject);		/* this terminates the DECnet mail header */
  tail=headers;
    /* send list of headers - these are treated by DECnet mail as part of
       the normal message */
  while (tail) {
    send(tail->str);
    tail=tail->next;
    cfree(headers->str);
    free(headers);
    headers=tail;
  }
}

/* the actual workhorse.  Sends mail using correct DECnet mail protocol.
   Information about the protocol was derived from microfiche VMS
   listings for V4.0.  See dnamaild.c for the other half of the protocol.
 
   Protocol:
     'send' means to write a record over decnet.  A 'marker' is a record
     containing a single NULL (used to terminate list of users and
     message).
     1) send who this mail is from (becomes From: line)
     2) for each user we are sending mail to:
        a) send address
        b) get status back (tells us if address is valid or not)
     3) send a 'marker' specifying the end of step 2
     4) send To: line.  This line contains the original list
        of recipients as entered by the user.  (this line is
        treated as a 'header' by VMS)
     5) send Subj: line (this is handled by send_headers() )
     6) send each line of the message
     7) terminate message with a 'marker'.
     8) For each address that was valid in step 2, read a status value
        back to see if message actually got sent.
*/
send_message() {
  int i;

  if (debug)
    fprintf(stderr, "Send the From: line\n");
  send(from);
  
    /* for each user, send address and get status */
  baduser_flag = 0;
  for (i=0; i<num_addr; i++) {
    if (debug)
      fprintf(stderr, "Checking user <%s>\n", to[i]);
    send(to[i]);
    if (check_status()) {	/* status tell if deliverable or not */
      if (debug)
        fprintf(stderr, "\tcheck_status returned true\n");
      to[i] = NULL; 
      baduser_flag = 1;  /* remember that we had an invalid address */
    }
  }
  send_marker();	/* specifies end of address check */

  if (debug)
    fprintf(stderr, "sending To: line\n");
  send (to_line);

    /* send_headers() will send the Subj: line after parsing the headers */
  send_headers();

    /* now send actual message */
  if (debug)
    fprintf(stderr, "Sending message body\n");
  if (ttyflag)
    printf("Enter your message below.  Press CTRL/D when complete, or CTRL/C to quit:\n");
  while (gets(buff) > 0)
    send(buff);
  send_marker();  /* specifies end of message */

    /* for each address, check status to see if it was actually sent */
  for (i=0; i<num_addr; i++)
    if (to[i] && check_status())
      baduser_flag = 1;  /* so we can exit with an appropriate error */
}

/* read status back from remote node.  Since I am assuming the remote
   node is a VAX, I assume a VAX byte order.  If there was an
   error, then read in status message */
int check_status() {
#ifndef DEBUG
  long st;
    /* read longword */
  if (read(ll, &st, sizeof(long)) < 0) {
    dnaerror("CheckStatus");
    exit_with_status();
  }
    /* 0x01000000 is really 0x1 on VAX */
  if (st == 0x01000000)
    return 0;
    /* else we have an error - read error message */
  while (1) {
    if ((st=read(ll, buff, sizeof(buff))) < 0) {
      dnaerror("CheckStatus");
      exit_with_status();
    }
      /* is this end of status message ? */
    if (st == 1 && buff[0] == NULL)
      break;
    buff[st] = NULL;
    fprintf(stderr, "%s\n", buff);    /* write to stderr so sendmail sees it */
  }
  return 1;
#else
  return 0;
#endif
}

/* send a string over the decnet connection */
send(s)
char *s;
{
#ifndef DEBUG
  if (write(ll, s, (s?strlen(s):0)) < 0) {
    dnaerror("SEND");
    exit_with_status();
  }
#else
printf("SEND= %s\n", (s?s:""));
#endif
}

/* send NULL over decnet link - used as marker */
send_marker() {
#ifndef DEBUG
  if (write(ll, "", 1) < 0) {
    dnaerror("SEND");
    exit_with_status();
  }
#else
printf("SEND_MARKER\n");
#endif
}

/* clean up addresses for VMS side - convert to uppercase, anything else
   needed */
char *fix_addr(addr)
char *addr;
{
  char inquote, esc;
  char *p;
    /* make sure usernames are uppercase so that MAIL.EXE doesn't yell
       at us */
  inquote = 0;
  for (p=buff; *addr; addr++) {
    if (esc = (*addr=='\\')) 
      addr++;
    if (*addr=='"' && !esc) {
      addr++;
      inquote ^= 1;
    }
    if (!inquote && islower(*addr))
      *p++ = toupper(*addr);
    else
      *p++ = *addr;
  }
  *p = NULL;
  return copystr(buff);
}

/* make one long address out of all specified on command line - append
   "node::" to beginning of each.  Return in 'to_line' */
get_addresses(argv, index, num_addresses)
char *argv[];
int index, num_addresses;
{
  int i;
  int comma_flag;
  comma_flag = 0;
  num_addr = num_addresses;
  to = (char **)malloc(sizeof(char *) * num_addr);
  for (i=0; i<num_addr; i++) {
    if (comma_flag)
      strcat(buff, " ");
    strcat(buff, node);
    strcat(buff, "::");
    strcat(buff, argv[i+index]);
    to[i] = fix_addr(argv[i+index]);
    comma_flag = 1;
  }
  to_line = copystr(buff);
}

/* parse arguments, get anything not specified, and call send_message() */
main(argc, argv)
	  int argc;
	  char *argv[];
{
  int st,			/* return status */
       i;			/* misc. */

  extern char *optarg;		/* for use with getopt() */
  extern int optind;
  char opt;
  char *copystr();
  char *t1;
  struct passwd *pw;

  subject = node = from = from_o = NULL;
  debug = ttyflag = FALSE;
  
    /* parse arguments */
  while ((opt=getopt(argc, argv, "ds:n:f:")) != EOF) {
    switch(opt) {
      case 'd':		/* debug flag */
        debug = TRUE;
	break;
      case 's':		/* subject */
        subject = optarg;
	break;
      case 'n':		/* remote node */
	node = optarg;
	break;
      case 'f':		/* not included in usage()! */
	from_o = optarg;
	break;
      default:
	usage();
	exit(EX_USAGE);
    }
  }

    /* see if we are a tty as opposed to sendmail */
  ttyflag = isatty(0); 

    /* prompt for node if not specified */
  if (!node) {
    fputs("Node: ", stdout);
    if (!gets(buff)) {
      puts("Unexpected EOF");
      exit(1);
    }
    node = copystr(buff);
  }
    /* uppercase node name */
  for (t1 = node; *t1; t1++)
    if (islower(*t1))
      *t1 = toupper(*t1);

    /* get recipients if not specified */
  if (!argv[optind]) {
    fputs("To: ", stdout);
    if (!gets(buff)) {
      puts("Unexpected EOF");
      exit(1);
    }
      /* create to[] array */
    to = (char **)malloc(sizeof(char*));	/* room for one address */
    to_line = copystr(buff);
    num_addr = 1;
    to[0] = fix_addr(buff);
  } else {
      /* build to_line and to[] from arguments */
    get_addresses(argv, optind, (argc-optind));
  }
  
    /* get subject if not specified */
  if (!subject && ttyflag) {
    fputs("Subject: ", stdout);
    if (!gets(buff)) {
      puts("Unexpected EOF");
      exit(1);
    }
    subject = copystr(buff);
  }

    /* figure out who this is from if not specified or this is a tty */
  if (!from_o) {
    t1 = (char *)getlogin();
    if (!t1) {
      pw = getpwuid(getuid());
      t1 = pw->pw_name;
    }
    from_o = t1;
  }
  sprintf(buff, "\"%s\"", from_o);
  from = copystr(buff);
  
  if (debug) {
    fprintf(stderr, "From: '%s'\n", from);
    fprintf(stderr, "To: <%s>", to_line);
  }

   /* get connection, send mail, close connection */
#ifndef DEBUG
  get_connection();
#endif
  send_message();
#ifndef DEBUG
  drop_connection();
#endif

    /* if mail wasn't sent to a user, then exit with appropriate
       error code, so sendmail can take the appropriate action */
  if (baduser_flag)
    exit(EX_NOUSER);
  else
    exit(EX_OK);
}
SHAR_EOF
if test 11700 -ne "`wc -c < 'dnamail.c'`"
then
	echo shar: error transmitting "'dnamail.c'" '(should have been 11700 characters)'
fi
chmod +x 'dnamail.c'
fi # end of overwriting check
echo shar: extracting "'dnamaild.c'" '(10282 characters)'
if test -f 'dnamaild.c'
then
	echo shar: will not over-write existing file "'dnamaild.c'"
else
cat << \SHAR_EOF > 'dnamaild.c'
/*************************************************************
* DNAMAILD - handle remote mail from DECnet nodes.
*
* To use:
*   If compiled with -DSTANDALONE, then just execute this program
*   whenever you want to receive mail (only handles one connection).
*
*   Without the STANDALONE option, add a line like the following to
*   /usr/sunlink/dna/dnaserver.reg:
*
*     27  MAIL  /usr/sunlink/dna/dnamaild
*
*   (Remember to add the line "Tdna" to sendmail.cf, since
*   this program gets run as user "dna".  This is not needed if
*   compiled with STANDALONE option.)
*
* Copyright
*  Darin Johnson, Lockheed Missiles and Space
*
* Permission to copy and/or modify as long as reference is made
* to the authors.  This program may not be sold.
*************************************************************/

#include	<stdio.h>
#include	<ctype.h>
#include	<signal.h>
#include	<fcntl.h>
#include	<sys/ioctl.h>
#include	<netdna/dna.h>
#include	"dnamail.h"

/* External variable definitions */
extern int errno;		/* External variable errno	 */

/* Global data definitions */
short ll;			/* Logical link identifier	 */
char buffer[MAXLINE];		/* Character buffer 		 */
char *my_hostname;
OpenBlock opblk;		/* OpenBlock 			 */
Image16 idata;
struct ses_io_type sesopts;
SessionData sd = {0, {0, ""}};

FILE *tmpf;
char tmp_name[32];
char *from;

  /* list of addresses */
typedef struct addr_struct {
  char *addr;
  struct addr_struct *next;
} ADDRESS;

ADDRESS *addr_head, *addr_tail;

  /* use a variable, since ioctl() wants an address to this */
short T_num = MAIL_OBJECT;

/* if we are not run from dnaserver, then we have to set up our own link */
#ifdef STANDALONE
get_connection() {		/* get decnet connection set up */
int ret;

/*
 * Before establishing a logical link, we must first
 * open the logical link device, "/dev/dna". 
 */
if ((ll = open("/dev/dna", O_RDWR)) < 0)
dna_exit("Open Fail");

if (ioctl(ll, SES_GET_LINK, 0)) 
dna_exit("Error getting a logical link");

/*
 * Next, we must register ourself as a server.
 */

ret = ioctl(ll, SES_NUM_SERVER, &T_num);
if (ret == -1)
dna_exit("Server Registration Failed");

if (ioctl(ll, SES_GET_AI, &opblk) < 0)
dna_exit("Ioctl Get AI Failed");

if (ioctl(ll, SES_ACCEPT, &sd) < 0)
dna_exit("Ioctl Accept failed");
}		/* end received Open Block */
#endif STANDALONE

/* add address onto list */
add_address(user)
char *user;
{
  ADDRESS *tmpa;
  tmpa = (ADDRESS*)malloc(sizeof(ADDRESS));
  tmpa->addr = copystr(user);
  tmpa->next = NULL;
  if (addr_tail) {
    addr_tail->next = tmpa;
    addr_tail = tmpa;
  } else {
    addr_head = addr_tail = tmpa;
  }
}

/* deliver completed message to all recipients */
deliver() {
#ifdef DEBUG
  return;
#else
  FILE *sm;
  ADDRESS *tmp, *jnk;
  char c;
  int ret;

    /* close temporary file since we are done writing to it */
  fclose(tmpf);
    /* reopen it so we can send it */
  tmpf = fopen(tmp_name, "r");
  if (!tmpf) {
    dna_exit("Can't open temp file");
  }
  tmp = addr_head;
    /* loop through each recipient */
  while (tmp!=NULL) {
      /* build command */
    sprintf(buffer, "/usr/lib/sendmail -oem -f \"%s\" %s",
		 from, tmp->addr);
#ifdef DEBUG
    fprintf(stderr, "cmd = <%s>\n", buffer);
#endif
      /* open pipe to sendmail command */
    if (sm = popen(buffer, "w")) {
      rewind(tmpf);
	/* now copy temp file to pipe */
      while ((c=getc(tmpf))!=EOF)
	putc(c, sm);
      ret = pclose(sm);
        /* check errors */
      if (ret) {
	sprintf(buffer, "sendmail can't send to %s on %s", tmp->addr, my_hostname);
	status_err(buffer);	/* send status back to remote node */
      } else
	status_ok();	/* send status back to remote node */
    } else
      dna_exit("Can't connect to sendmail");
	/* now go to next address */
    jnk = tmp;
    tmp=tmp->next;
    cfree(jnk->addr);
    free(jnk);
  }
  fclose(tmpf);
  tmpf = NULL;
#endif
}

/* clean up username from VMS side -
     VMS format is:  USERNAME		"comment"
     we want:        USERNAME		(comment) 
   There also may be quotes around the username, so they get zapped.
   I am unsure of the exact format used by VMS, but this hasn't caused
   trouble so far...
   We also append the remote node name to the front.
*/
fix_user(str)
char *str;
{
  char *fq, *lq;
  if (*str == '"') {	/* remove quotes around username */
    str++;
    fq = (char *)index(str, '"');
    *fq = ' ';
  } else {
    fq = (char *)index(str, '"');
    lq = (char *)rindex(str, '"');
    if (fq && lq && fq < lq) {
        /* convert quotes */
      *fq = '(';
      *lq = ')';
    }
  }
    /* we have to set this up for sendmail */
  from = (char *)calloc(strlen(str)+strlen(opblk.op_node_name)+5, 1);
  sprintf(from, "%s!%s", opblk.op_node_name, str);
}

/* Actually receive the mail.  Places all recipients into a list and
   saves the message/headers into a temporary file.  Mail protocol was
   derived from VMS microfiche.  See dnamail.c for other half of protocol.

   Protocol:
     1) read a record - if EOF, then exit
     2) record read in 1) is who the mail is "From:"
     3) loop until a 'marker' is read (record containing single NULL)
       a) read recipient address
       b) send back OK status (really, the status should tell if
	  this address is good and/or the user exists, but I ignore
	  this part since sendmail we mail errors back)
     4) read "To: line.  This is the line typed by the VMS mail user
	to specify the recipients.  Used only as part of the header.
     5) read "Subject:" line.  Used only as part of the header.
     6) keep reading message body until a 'marker' is seen.
     7) now send back a status for each address from 3a) above
	indicating whether the message was delivered or not.  (Note
	that we shouldn't send back a status if we sent back a bad
	status in 3b), which we don't do anyway)
     8) go to step 1), in case there are more messages being sent
	(in case VMS decides to 'cache' this link)
*/
receive_mail() {
  int len, ret;
 
  while (1) {
    len=get(buffer);
    if (len < 0) {	/* no more messages being sent */
#ifdef DEBUG
      fprintf(stderr, "DONE!!\n");
#endif
      break;
    }

      /* add our own header for tracking purposes */
    fprintf(tmpf, "Received: by %s; %s\n", version, mailtime());

#ifdef DEBUG
    fprintf(stderr, "USER = <%*s>\n", len, buffer);
#endif
      /* record read above is USER */
    buffer[len]=NULL;
    fix_user(buffer);
    fprintf(tmpf, "From: %s\n", from);
      /* get list of recipients */
    while (1) {
      len = get(buffer);
      if (len==1 && buffer[0]==NULL) {		/* end of list */
#ifdef DEBUG
	fprintf(stderr, "(MARKER)\n");
#endif
	break;
      }
#ifdef DEBUG
      fprintf(stderr, "ADDR = <%*s>\n", len, buffer);
#endif
      buffer[len] = NULL;
      add_address(buffer);	/* add address to list */
      status_ok();	/* send back OK status since we aren't checking now */
    }	  

      /* get the To: line */
    len = check_get(buffer);
#ifdef DEBUG
    fprintf(stderr, "TO = <%*s>\n", len, buffer);
#endif
    buffer[len] = NULL;
    fprintf(tmpf, "To: %s\n", buffer);
      /* get Subject: line */
    len = check_get(buffer);
#ifdef DEBUG
    fprintf(stderr, "SUBJ = <%*s>\n", len, buffer);
#endif
    buffer[len]=NULL;
    fprintf(tmpf, "Subject: %s\n", buffer);
    fprintf(tmpf, "\n");	/* mark end of headers */
      /* now get message */
    while (1) {
      len = check_get(buffer);
      if (len==1 && buffer[0]==NULL) {	/* end of message */
#ifdef DEBUG
	fprintf(stderr, "(MARKER)\n");
#endif
	break;
      }
#ifdef DEBUG
      fprintf(stderr, "TXT = <%*s>\n", len, buffer);
#endif
      buffer[len]=NULL;
      fprintf(tmpf, "%s\n", buffer);
    }
#ifdef DEBUG
    fprintf(stderr, "	Sending message\n");
#endif
      /* now try to deliver the message */
    deliver();
  }
}

/* send string over decnet link */
send(s, len)
char *s;
int len;
{
  if (write(ll, s, len) < 0)
    dna_exit("SEND");
}

/* do a get(), but check errors here */
int check_get(s)
char *s;
{
  int l;
  if ((l=get(s))<0)
    dna_exit("GET");
}

/* read a string from decnet link.  Return length of string. */
int get(s)
char *s;
{
  int len, readmask;
    /* using select() here since it was needed earlier in development,
       don't know if it is needed now */
  readmask = 1 << ll;
  if ((len=select(32, &readmask, 0, 0, 0)) < 0) {
    perror("select");
    my_exit(1);
  }
  if ((len=read(ll, s, MAXLINE)) >= 0)
    s[len] = NULL;	/* close off string */
  return len;
}

/* print out decnet error and call my_exit() */
dna_exit(str)
char *str;
{
    dnaerror(str);
    my_exit(1);
}

/* cleanup and then exit() */
my_exit(st)
int st;
{
  if (tmpf) {
    fclose(tmpf);
#ifndef DEBUG
    unlink(tmp_name);
#endif
  }
  exit(st);
}

/* send OK status up decnet link */
status_ok()
{
  long st;
  st = 0x01000000;	/* same as 0x1 on VAX */
  send(&st, sizeof(long));
}

/* send error status and message up decnet link */
status_err(msg)
char *msg;
{
  long st;
  st = 0x00000000;
  send(&st, sizeof(long));
  send(msg, strlen(msg));
  send("", 1);	/* mark end of message */
}

/* if we are standalone we call get_connection, otherwise
   dnaserver has passed number of file descriptor for logical link
   in argv */
main(argc, argv)
int argc;
char *argv[];
{
  int i;
    /* always a good practice to do this when creating temp files */
  for (i=0; i<20; i++)
    if (signal(i, SIG_IGN)!=SIG_IGN)
      signal(i, my_exit);

  gethostname(buffer, sizeof(buffer));
  my_hostname = copystr(buffer);

#ifdef STANDALONE
  get_connection();
#else
    /* set up ll to point be descriptor passed */
  if (argc<2 || (ll=atoi(argv[1]))==0) {
    fprintf(stderr, "%s: Aborting, should not be run interactively\n", argv[0]);
    exit(1);
  }
    /* accept link */
  if (ioctl(ll, SES_GET_AI, &opblk) < 0)
    dna_exit("Ioctl Get AI Failed");
  if (ioctl(ll, SES_ACCEPT, &sd) < 0)
    dna_exit("Ioctl Accept failed");
#endif

    /* create temp file */
  strcpy(tmp_name, TMPFILE),
  mktemp(tmp_name);
  tmpf = fopen(tmp_name, "w");
  addr_head = addr_tail = NULL;

    /* now read mail from link - actually we may have to wait around
       while the user types in the message, but this shouldn't bother
       us */
  receive_mail();

    /* cleanup */
  fclose(tmpf);
  unlink(tmp_name);
  close(ll);
  return;
}
SHAR_EOF
if test 10282 -ne "`wc -c < 'dnamaild.c'`"
then
	echo shar: error transmitting "'dnamaild.c'" '(should have been 10282 characters)'
fi
chmod +x 'dnamaild.c'
fi # end of overwriting check
echo shar: extracting "'dnamisc.c'" '(794 characters)'
if test -f 'dnamisc.c'
then
	echo shar: will not over-write existing file "'dnamisc.c'"
else
cat << \SHAR_EOF > 'dnamisc.c'
/*************************************************************
  miscellaneous routines
 *************************************************************/

#include <stdio.h>
#include <sys/time.h>

/* returns a newly allocated copy of a string */
char *copystr(str)
	  char *str;
{
  char *tmp, *calloc();
  if (!str)
    return NULL;
  tmp = calloc(strlen(str)+1, 1);
  strcpy(tmp, str);
  return tmp;
}

/* figure out correct time and timezone */
char *mailtime() {
  long my_time;
  char *atime;
  static char buf[32];
  struct timeval tv;
  struct timezone tzp;
  my_time = time(0L);
  atime = asctime(localtime(&my_time));
  atime[strlen(atime)-1] = '\0';	/* zap "\n" */
  gettimeofday(&tv, &tzp);
  sprintf(buf, "%s %s", atime, timezone(tzp.tz_minuteswest, tzp.tz_dsttime));
  return buf;
}
SHAR_EOF
if test 794 -ne "`wc -c < 'dnamisc.c'`"
then
	echo shar: error transmitting "'dnamisc.c'" '(should have been 794 characters)'
fi
chmod +x 'dnamisc.c'
fi # end of overwriting check
echo shar: extracting "'dnaerror.c'" '(474 characters)'
if test -f 'dnaerror.c'
then
	echo shar: will not over-write existing file "'dnaerror.c'"
else
cat << \SHAR_EOF > 'dnaerror.c'
/* Since dnaerror.c is copyrighted, this file just includes the copy	*/
/* you should already have.  You may have to change the path name if	*/
/* your setup is not standard.  If you don't have this file (even	*/
/* on the original tape), then you will have to get one from Sun,	*/
/* or derive your own.							*/
/*									*/
/* All dnaerror does, is to simulate perror(), but with DECNET errors	*/
/* if they apply.							*/

#include "/usr/sunlink/dna/tests/dnaerror.c"
SHAR_EOF
if test 474 -ne "`wc -c < 'dnaerror.c'`"
then
	echo shar: error transmitting "'dnaerror.c'" '(should have been 474 characters)'
fi
chmod +x 'dnaerror.c'
fi # end of overwriting check
echo shar: extracting "'main.diff'" '(3720 characters)'
if test -f 'main.diff'
then
	echo shar: will not over-write existing file "'main.diff'"
else
cat << \SHAR_EOF > 'main.diff'
This is a diff to convert sendmail.main.cf to use dnamail.

Changes:

  1) Define macro 'S' to be name of machine that can forward DECnet
     mail (default is 'dnahost').  Comment out this line for
     if this machine runs Sunlink/DNI.
  2) Define class 'S' to be list of machines we can send mail to
     via DECnet.
  3) Add 'dna' as a trusted user.
  4) Add 'dna' as a mailer
  5) Addresses to machines in class 'S' are mailed by #dna
     or forwarded to 'dnahost' to be mailed.

*** sendmail.main.cf	Sat May  7 18:43:55 1988
--- sendmail.main.new	Sat May  7 19:10:07 1988
***************
*** 48,53 ****
--- 48,58 ----
  # Otherwise, the default is to use the hosts.byname map if YP
  # is running (or else the /etc/hosts file if no YP).
  
+ # define this $S if you want to forward decnet mail to $S
+ # if this node can handle decnet, then DON'T define $S
+ # DBJ
+ DSdnahost
+ 
  #################################################
  #
  #	General configuration information
***************
*** 91,96 ****
--- 96,104 ----
  DVSMI-3.2
  
  
+ # get list of decnet machines we can send mail to
+ FS/usr/sunlink/dna/dnahosts
+ 
  ##########################
  ###   Special macros   ###
  ##########################
***************
*** 154,163 ****
  #########################
  ###   Trusted users   ###
  #########################
! 
  Troot
  Tdaemon
  Tuucp
  
  #############################
  ###   Format of headers   ###
--- 162,174 ----
  #########################
  ###   Trusted users   ###
  #########################
! #
! # added dna, since dnamaild gets run as that user..
! #
  Troot
  Tdaemon
  Tuucp
+ Tdna
  
  #############################
  ###   Format of headers   ###
***************
*** 269,274 ****
--- 280,287 ----
  S6
  R$*<@$*$=D>$*		$1<@$2LOCAL>$4			convert local domain
  R$*<@$*$=D.$=U>$*	$1<@$2LOCAL>$5			or full domain name
+ #added by DBJ - just in case someone still uses the old form
+ R$*<$*.decnet>$*	$1<$2.uucp>$3			convert to uucp
  
  ############################################################
  ############################################################
***************
*** 366,373 ****
--- 379,408 ----
  S23
  R$+			$:$>5$1				convert to old style
  
+ ############################################################
+ ############################################################
+ #####
+ #####		DECnet Mailer specification
+ #####
+ #####	Messages processed by this configuration are assumed to leave
+ #####   the internet domain.  Hence, they may not necessarily correspond
+ #####	to RFC822 in all details.
+ #####
+ #####   added by DBJ
+ #####
+ ############################################################
+ ############################################################
  
+ Mdna,	P=/usr/sunlink/dna/dnamail, F=mnSF, S=14, R=24,
+ 	A=dnamail -f $f -n $h $u
  
+ S14
+ # none needed
+ 
+ S24
+ # none needed
+ 
+ 
  ############################################################
  ############################################################
  #####
***************
*** 415,420 ****
--- 450,470 ----
  
  # Clean up addresses for external use -- kills LOCAL, route-addr ,=>: and etc.
  R$*			$:$>9 $1			Then continue...
+ 
+ # added by DBJ
+ ####################################################
+ #  This gets shoved in the center of Ruleset 0 in 
+ #  order to handle decnet nodes.
+ #
+ #  If $S (decnet gateway) is defined, then forward to $S, else
+ #  resolve to dna mailer.
+ #
+ #  added by DBJ
+ ####################################################
+ 
+ R$+<@$=S>		$?S $#ether $@$S $:$2!$1 $| $#dna $@$2 $:$1 $.
+ R$+<@$=S.uucp>		$?S $#ether $@$S $:$2!$1 $| $#dna $@$2 $:$1 $.
+ 
  
  # resolve UUCP domain
  R<@$-.uucp>:$+		$#uucp  $@$1 $:$2		@host.uucp:...
SHAR_EOF
if test 3720 -ne "`wc -c < 'main.diff'`"
then
	echo shar: error transmitting "'main.diff'" '(should have been 3720 characters)'
fi
chmod +x 'main.diff'
fi # end of overwriting check
echo shar: extracting "'sub.diff'" '(3714 characters)'
if test -f 'sub.diff'
then
	echo shar: will not over-write existing file "'sub.diff'"
else
cat << \SHAR_EOF > 'sub.diff'
This is a diff to convert sendmail.subsidiary.cf to use dnamail.

Changes:

  1) Define macro 'S' to be name of machine that can forward DECnet
     mail (default is 'dnahost').  Comment out this line for
     if this machine runs Sunlink/DNI.
  2) Define class 'S' to be list of machines we can send mail to
     via DECnet.
  3) Add 'dna' as a trusted user.
  4) Add 'dna' as a mailer
  5) Addresses to machines in class 'S' are mailed by #dna
     or forwarded to 'dnahost' to be mailed.

*** sendmail.subsidiary.cf	Sat May  7 18:43:55 1988
--- sendmail.subsidiary.new	Sat May  7 19:10:58 1988
***************
*** 36,41 ****
--- 36,46 ----
  DRmailhost
  CRmailhost
  
+ # define this $S if you want to forward decnet mail to $S
+ # if this node can handle decnet, then DON'T define $S
+ # DBJ
+ DSdnahost
+ 
  #################################################
  #
  #	General configuration information
***************
*** 79,84 ****
--- 84,92 ----
  DVSMI-3.2
  
  
+ # get list of decnet machines we can send mail to
+ FS/usr/sunlink/dna/dnahosts
+ 
  ##########################
  ###   Special macros   ###
  ##########################
***************
*** 142,151 ****
  #########################
  ###   Trusted users   ###
  #########################
! 
  Troot
  Tdaemon
  Tuucp
  
  #############################
  ###   Format of headers   ###
--- 150,162 ----
  #########################
  ###   Trusted users   ###
  #########################
! #
! # added dna, since dnamaild gets run as that user..
! #
  Troot
  Tdaemon
  Tuucp
+ Tdna
  
  #############################
  ###   Format of headers   ###
***************
*** 257,262 ****
--- 268,275 ----
  S6
  R$*<@$*$=D>$*		$1<@$2LOCAL>$4			convert local domain
  R$*<@$*$=D.$=U>$*	$1<@$2LOCAL>$5			or full domain name
+ #added by DBJ - just in case someone still uses the old form
+ R$*<$*.decnet>$*	$1<$2.uucp>$3			convert to uucp
  
  ############################################################
  ############################################################
***************
*** 354,360 ****
--- 367,396 ----
  S23
  R$+			$:$>5$1				convert to old style
  
+ ############################################################
+ ############################################################
+ #####
+ #####		DECnet Mailer specification
+ #####
+ #####	Messages processed by this configuration are assumed to leave
+ #####   the internet domain.  Hence, they may not necessarily correspond
+ #####	to RFC822 in all details.
+ #####
+ #####   added by DBJ
+ #####
+ ############################################################
+ ############################################################
  
+ Mdna,	P=/usr/sunlink/dna/dnamail, F=mnSF, S=14, R=24,
+ 	A=dnamail -f $f -n $h $u
+ 
+ S14
+ # none needed
+ 
+ S24
+ # none needed
+ 
+ 
  ############################################################
  ############################################################
  #####
***************
*** 407,412 ****
--- 443,463 ----
  R<@$=V.uucp>:$+		$:$>9 $1			First clean up, then...
  R<@$=V.uucp>:$+		$#uucp  $@$1 $:$2		@host.uucp:...
  R$+<@$=V.uucp>		$#uucp  $@$2 $:$1		user@host.uucp
+ 
+ # added by DBJ
+ ####################################################
+ #  This gets shoved in the center of Ruleset 0 in 
+ #  order to handle decnet nodes.
+ #
+ #  If $S (decnet gateway) is defined, then forward to $S, else
+ #  resolve to dna mailer.
+ #
+ #  added by DBJ
+ ####################################################
+ 
+ R$+<@$=S>		$?S $#ether $@$S $:$2!$1 $| $#dna $@$2 $:$1 $.
+ R$+<@$=S.uucp>		$?S $#ether $@$S $:$2!$1 $| $#dna $@$2 $:$1 $.
+ 
  
  # optimize names of known ethernet hosts
  R$*<@$*$%y.LOCAL>$*	$#ether $@$3 $:$1<@$2$3>$4	user@host.here
SHAR_EOF
if test 3714 -ne "`wc -c < 'sub.diff'`"
then
	echo shar: error transmitting "'sub.diff'" '(should have been 3714 characters)'
fi
chmod +x 'sub.diff'
fi # end of overwriting check
#	End of shell archive
exit 0
-- 
Darin Johnson (...lll-lcc.arpa!leadsv!laic!darin)
              (...ucbvax!sun!sunncal!leadsv!laic!darin)
	"All aboard the DOOMED express!"