| Setting Up an HP-UX Mail Hub
 
Larry Reznick 
I had two sendmail(1M) problems to solve on a network
of HP-UX 
systems. First, the site name needed to be hidden. Mail
sent from 
an HP system got stamped as coming from user@localsystem
instead 
of user@centralsystem, and since local systems weren't
known 
outside the company's network, replies never arrived.
Second, each 
HP system effectively stood alone, although they could
talk with each 
other across the network. There was little centralization
of files 
or directories across the systems. However, making mail
work uniformly 
was only one small part of this problem. Users logging
in on one system 
were deposited in a home directory local to that system.
If a user 
sent mail from any HP system while logged in on that
system, the mail 
would go to the receiver's account on that same local
system. With 
eight HP systems, each collecting mail for every user
separately, 
I faced a mess. 
A network of Sun systems had been set up with greater
uniformity. 
If mail was sent to any user on a Sun, smail(8) forwarded
it to the user's Sun mail account. But smail, although
compatible 
with sendmail, wasn't set up on the HPs. With a limited
amount 
of time to finish this problem and move on to the next,
I decided 
it would be faster to fix the HPs' sendmail.cf files
than 
to hunt for a version of smail that would work on HPs. 
HP-UX uses /usr/lib instead of /etc, as its holding
directory for sendmail. No HP system would serve mail
in my 
plan. The mail hub, which happened to be a Sun, would
do that. First, 
I had to make sure that none of the slave systems was
running the 
sendmail daemon. I checked the boot script, /etc/netbsdsrc
on HP-UX, to be sure that sendmail would not restart
at the 
next reboot. HP uses many scripts for booting that don't
correspond 
with Sun's single rc.local or with System V's rc directory
organization. If you aren't sure which boot script starts
sendmail, 
you can discover it with the following command: 
 
egrep sendmail `file /etc/* | egrep text | cut -d: -f1` 
 
This makes the system search through all files identified
as text files to find out which script contains sendmail. 
If booting has already started the sendmail daemon,
I kill it on every 
system except the hub. Only systems that expect to receive
mail from 
an outside source need to run the daemon. Because none
of the HP systems 
will receive -- they'll only send -- none needs the
daemon running. 
When new mail goes out, the mailer will run sendmail
to deliver 
the message. 
My next step is to arrange for every /usr/lib/sendmail.cf
to be identical. The following command line shows the
differences 
between two files across two systems (assuming a user
already logged 
in to system1): 
 
remsh system2 cat /usr/lib/sendmail.cf | diff /usr/lib/sendmail.cf - 
 
This command logs onto another system (system2) 
to deliver that other system's sendmail.cf file to diff.
remsh(1), HP-UX's version of rsh(1C on Sun), runs the
right 
side of a pipeline on the local system, not the remote
system. If 
you must run the entire pipeline on the remote system,
surround the 
pipeline argument with quotation marks. Assuming that
the system you're 
currently logged onto contains the sendmail.cf you want
to 
make central, you could put this command inside a loop
that changes 
the system name following remsh. Then, if any differences
show up, you can decide whether to incorporate them
into the central 
file. When you've finished with all modifications, including
the ones 
outlined in this article, you'll rcp(1) the central
file to 
all the other systems. 
Site Hiding 
Arranging site hiding was the simplest part of my task.
HP-UX's sendmail.cf 
already provides a macro for site hiding: DY. By default,
DY is not set. The SMTP (Simple Mail Transfer Protocol)
TCP/IP 
delivery agent uses the Y definition when it sends mail
out 
(to see this use, search for the regular expression
"^Mtcp" 
in HP's sendmail.cf). Two rulesets, S11 and 
S21, associate with the tcp delivery agent via the flags
S=11 
and R=21. S11 rewrites the sender's address and S21
rewrites the receiver's address. In S11, the last rule,
labeled 
R$+, uses $Y  to add the local domain name defined 
by DY. With DY defined as 
 
DYmysite.com 
 
this rule changes mail sent from larryr@system1 
into larryr@mysite.com. People receiving such messages
may 
now simply reply to get their response back to me, no
matter what 
system I'm really logged onto -- once all the mail is
accessible 
centrally, that is. 
To put the mail files in a central location, create
one directory 
on the mail hub system. Combine all the mail files from
the various 
systems into one file on the hub system. Check with
your users and 
throw the old mail files out if you can. When in doubt,
concatenate 
the files together on the hub system and notify the
users with /etc/motd 
or news that all users should clean out their mail files.
Once all the files are combined on the hub, mount the
hub's mail directory 
or add it to the automount list. 
Routing to the Hub 
Routing mail to the mail hub turned out to be the tedious
part of 
the job. Essentially, I chose to disable local mailing
on every HP. 
I didn't want to embed the hub system's name in every
alias: I thought 
that would cause more trouble with respect to future
maintenance than 
fixing sendmail. Instead, I decided to route all mail
that 
would have been local through the tcp delivery agent
identified in 
the sendmail.cf file. Once in the tcp agent, sendmail
rulesets will route the mail to the mail hub. That plan
sounded simple, 
until I found that the rulesets didn't want to go to
the remote mail 
hub portably. 
I wanted to route the mail to mailhost, the common host
alias 
for the mail hub as defined in the NIS hosts map. By
using the mailhost 
alias, I could ensure that if the system hosting mail
should change 
in the future, the sendmail.cf files wouldn't need changing.
Instead, I could just change whichever system in the
map was aliased 
as mailhost, push the new map, and sendmail would 
get it. Everybody's happy. 
No such luck. Referring to mailhost works in the rule
that 
calls the tcp agent. Once in the tcp agent's rule, 
the @host part of the address must have a primary host
name, 
not a host alias. Give that tcp agent rule a host alias,
such 
as mailhost, and the mailer bounces it as an unknown
host. 
Not everything in HP-UX's mail system uses host aliases. 
Implementing Hub Routing 
HP's sendmail.cf defines a macro for SMTP relay: DS.
You can set the S macro to your hub's host name -- for
example, 
 
DShostname 
 
where hostname is the first name in the hosts 
map for the mail host. Don't use a host alias here,
such as mailhost: 
it won't work. Macro S gets used in ruleset 0, which
figures 
out what delivery agent to use based on the address. 
By default, any address containing a domain not resolved
by earlier 
rules in ruleset 0 get resolved by a rule that looks
like this: 
 
R$+<@$+>        $#tcp$@$2$:$1<@$2>      user@domain 
 
This confusing-looking mess divides into three fields
separated by tabs. The first field is the incoming address,
in a notation 
similar to regular expressions. The second field contains
instructions 
for translating the incoming address. Comments go in
the third field. 
The first field begins with R, which identifies it as
a rule. 
$+ matches one or more tokens up to a less-than (<)
symbol 
followed by an "at" sign (@). After the "at"
sign, 
expect one or more tokens followed by a greater-than
(>) symbol. Each 
$+ match from the first field can be referred to by
the second 
field, by means of a positional notation. $1 is the
first 
match (everything up to the less-than symbol in this
rule), and $2 
is the second (everything between the "at"
sign and the greater-than 
symbol for this rule). 
Previous rules have already turned a simple address,
larryr 
for instance, into a tcp-ready format like larryr<@system1>
and sent the address on to other rules. Rules like this
domain rule 
route the address to the delivery agent using the second
field. A 
$# specifies the delivery agent, tcp in this 
rule. sendmail must now find Mtcp and use its S= or
R= rules, depending on whether it is resolving the sender
address or the receiver address. $# is the first part
of a three-part rule. The second part uses $@ to tell
sendmail 
the name of the receiving host. The third part begins
with $:, 
to identify the receiving user. Assume that the matching
address found 
by the rule's first field is name<@host>. This
rule makes 
the delivery agent tcp, makes the host host, and makes
the user name<@host>. 
Consider that host is the name of the local system.
This rule 
contains nothing to force mail delivery to the hub system.
Delivery 
agent tcp happily delivers to the local host just as
well 
as it delivers to any host. Look two rules further down
in sendmail.cf 
and you'll find another rule with an identical first
field (R$+<@$+>), 
but it's commented out. This rule's comment in the third
field says 
that the rule relays to SMTP. Without the comment, the
rule looks 
like this: 
 
R$+<@$+>        $#tcp$@$S$:$1<@$2>      user@domain to SMTP relay 
 
Look carefully at the second field. It differs from
the 
previous rule in only one way: it uses $S instead of
$2 
as the receiving host. That's the S macro. With the
S 
macro's value set to the mail hub's host name, tcp will
route 
it to that hub system. The local system will have nothing
more to 
do with it. 
To set this up, comment out the local routing rule containing
$@$2 
and uncomment the SMTP routing rule containing $@$S
instead. 
So far so good, but local routing hasn't been eliminated. 
No More Local Routing 
At the end of ruleset 0 is the catchall rule that nothing
can get 
past: 
 
R$+     $#local$:$1     name 
 
The first field contains only $+, to match any 
incoming address that has at least one token. Every
address has at 
least one token, except an error address typically caught
early in 
sendmail processing by another rule. If some earlier
rule 
hasn't intercepted and modified the address by now,
this catchall 
rule gets it. 
According to the catchall rule's second field, an incoming
address 
is submitted to the local delivery agent ($#). No 
host ($@) is specified, only a user ($:), 
composed of the entire matching incoming address, is
specified. You 
can find the local delivery agent's handler if you search
for the 
regular expression "^Mlocal" in sendmail.cf. 
You'll need to comment out this local rule by starting
this rule line 
with a # sign. If a plain name comes along, it could
be a user name 
or a mail alias. Either way, the mail hub must receive
it and figure 
that out, not the local system. This ultimate, catchall
rule must 
submit the address to the tcp delivery agent. Add the
following 
new rule: 
 
R$+     $#tcp$@$R$:$1   Forward to mail hub 
 
This new catchall rule uses an R macro, defined 
as 
 
DRmailhost 
 
where mailhost is literally the alias name of 
the mail hub host. I recommend placing this definition
near the S 
macro's definition. 
Strictly, I could have used the S macro in this rule,
too. 
Initially, I tried setting S to mailhost and using 
$S throughout the changes outlined in this article.
Unfortunately, 
sendmail didn't cooperate. While this catchall tcp rule
did 
work with mailhost, other rules wouldn't accept that
mailhost 
alias. While debugging the problem, I defined the R
(for Remote) 
macro to use mailhost and set the S macro to the host's
primary name. This worked. Not wanting to challenge
Murphy any further, 
I kept it this way. If you're uncomfortable with yet
another macro, 
substitute $S for $R. If anyone knows a rule combination
that uses the mailhost alias throughout, please write. 
The tcp delivery agent will receive all that previously
went 
to the local delivery agent. With a receiving host specified
by $R, 
tcp will route the address to the mailhost. Local systems
using these rules will not attempt any further processing
of the address 
except according to the tcp agent's rules. 
There's no need to modify or comment out the local delivery
agent 
or its rules. Some X.400 rules still use the local agent,
but the 
HPs I administer aren't using those. 
Fixing the tcp Delivery Agent 
Find the tcp delivery agent line (search for regular
expression 
"^Mtcp"). The receiver rule, which modifies
the receiver's 
address, is identified with R=21 in my HP sendmail.cf
file. Ruleset 21 (search for regular expression "^S21")
should 
be several lines below the tcp delivery agent line. 
Ruleset 21 consists of only two rules. The first rule
handles addresses 
already containing a domain name. Such addresses didn't
come to the 
tcp delivery agent from the catchall rule that sends
addresses 
to the local agent. The second rule is important for
routing to the 
mail hub. Any addresses that used to go to the local
agent get caught 
by this second rule. Before my modification, this rule
read: 
 
R$+     $:$1<@$w>       add local domain 
 
As before, the first field ($+) acts 
as a catchall. The $: at the beginning of the second
field 
doesn't have the same interpretation as it has in the
three-part $# 
set. Here, $: means "Rewrite Once." Typically,
rules 
are applied repeatedly, as the input data needs. Using
$: 
prevents more than one application of the same rule.
Once this $: 
rule finishes rewriting the address, sendmail must go
to the 
next phase. 
A sendmail intrinsic macro, $w, is set to the local
system's host name. If larryr is the matched token,
this rule 
turns it into larryr<@localhost>, where localhost
refers to the local system's host name. When a sender
logged in on 
system1 mails to larryr, this rule changes larryr 
to larryr<@system1>. This is definitely wrong
for mail hub 
routing. This rule must refer to the mail host, not
the local host. 
To fix this, comment out the existing rule that uses
$w and 
add the following new rule: 
 
R$+     $:$1<@$S>       add mailhost 
 
The only difference in this rule is its use of $S. 
Again, I would have liked to use mailhost in $R but
that didn't work here. 
Conclusion 
Once I had finished and tested all sendmail.cf modifications,
I rcp'd /usr/lib/sendmail.cf to all the other HP systems
that will route to the mail hub. With these changes
in place, both 
mail addressing problems were solved. 
All sender addresses appear to originate from the company's
domain 
name server. Replies go back to that name server, which
knows how 
to get to the mail hub for proper routing to the user's
central mail 
file. 
All receiver addresses, whether including domain references,
using 
simple user names, or using mail aliases, are routed
to the mail hub. 
As a side effect, local systems don't have to know the
mail aliases. 
Any alias is routed to the mail hub. That mail hub resolves
the alias 
and routes the mail. When given an unknown alias, the
hub system has 
a proper sender address to return the mail. 
Afterword 
I strongly recommend the book sendmail, by Bryan Costales,
published by O'Reilly and Associates, 1993, ISBN: 1-56592-056-2.
This 
book was indispensable. Documentation on sendmail is
hard 
to find and sometimes virtually unreadable. HP-UX's
sendmail.cf 
has extensive comments that help, but often each answer
raises more 
questions. 
Costales' book gives detailed instructions on how sendmail
works, 
explains the meanings of sendmail's macros, rules, and
other 
definitions, identifies debugging methods, and includes
handy tables 
throughout. The index is thorough, with every intrinsic
macro and 
expression symbol identified. At times, the index acted
as a quick 
reference list for me. 
Finally, sendmail doesn't suffer from the SunOS bias
that several 
O'Reilly books have. As much as I like Suns, there are
other systems 
out there and I work on a variety of them. Costales'
book details 
variations in sendmail across many versions. Without
this book, I couldn't have solved the problems outlined
in this article 
as quickly. 
 
 About the Author
 
Larry Reznick has been programming professionally since
1978. He is currently 
working on systems programming in UNIX, MS-DOS, and
OS/2.
He teaches C, C++, and UNIX language courses
at American River College and at the University of California,
Davis extension. 
 
 
 |