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.
|