205 lines
5.2 KiB
C
205 lines
5.2 KiB
C
/*++
|
|
/* NAME
|
|
/* smtpd_peer 3
|
|
/* SUMMARY
|
|
/* look up peer name/address information
|
|
/* SYNOPSIS
|
|
/* #include "smtpd.h"
|
|
/*
|
|
/* void smtpd_peer_init(state)
|
|
/* SMTPD_STATE *state;
|
|
/*
|
|
/* void smtpd_peer_reset(state)
|
|
/* SMTPD_STATE *state;
|
|
/* DESCRIPTION
|
|
/* The smtpd_peer_init() routine attempts to produce a printable
|
|
/* version of the peer name and address of the specified socket.
|
|
/* Where information is unavailable, the name and/or address
|
|
/* are set to "unknown".
|
|
/*
|
|
/* smtpd_peer_init() updates the following fields:
|
|
/* .IP name
|
|
/* The client hostname. An unknown name is represented by the
|
|
/* string "unknown".
|
|
/* .IP addr
|
|
/* Printable representation of the client address.
|
|
/* .IP namaddr
|
|
/* String of the form: "name[addr]".
|
|
/* .IP peer_code
|
|
/* The peer_code result field specifies how the client name
|
|
/* information should be interpreted:
|
|
/* .RS
|
|
/* .IP 2
|
|
/* Both name lookup and name verification succeeded.
|
|
/* .IP 4
|
|
/* The name lookup or name verification failed with a recoverable
|
|
/* error (no address->name mapping or no name->address mapping).
|
|
/* .IP 5
|
|
/* The name lookup or verification failed with an unrecoverable
|
|
/* error (no address->name mapping, bad hostname syntax, no
|
|
/* name->address mapping, client address not listed for hostname).
|
|
/* .RE
|
|
/* .PP
|
|
/* smtpd_peer_reset() releases memory allocate by smtpd_peer_init().
|
|
/* LICENSE
|
|
/* .ad
|
|
/* .fi
|
|
/* The Secure Mailer license must be distributed with this software.
|
|
/* AUTHOR(S)
|
|
/* Wietse Venema
|
|
/* IBM T.J. Watson Research
|
|
/* P.O. Box 704
|
|
/* Yorktown Heights, NY 10598, USA
|
|
/*--*/
|
|
|
|
/* System library. */
|
|
|
|
#include <sys_defs.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <stdio.h> /* strerror() */
|
|
#include <errno.h>
|
|
#include <netdb.h>
|
|
#include <string.h>
|
|
|
|
/*
|
|
* Older systems don't have h_errno. Even modern systems don't have
|
|
* hstrerror().
|
|
*/
|
|
#ifdef NO_HERRNO
|
|
|
|
static int h_errno = TRY_AGAIN;
|
|
|
|
#define HSTRERROR(err) "Host not found"
|
|
|
|
#else
|
|
|
|
#define HSTRERROR(err) (\
|
|
err == TRY_AGAIN ? "Host not found, try again" : \
|
|
err == HOST_NOT_FOUND ? "Host not found" : \
|
|
err == NO_DATA ? "Host name has no address" : \
|
|
err == NO_RECOVERY ? "Name server failure" : \
|
|
strerror(errno) \
|
|
)
|
|
#endif
|
|
|
|
/* Utility library. */
|
|
|
|
#include <msg.h>
|
|
#include <mymalloc.h>
|
|
#include <valid_hostname.h>
|
|
#include <stringops.h>
|
|
|
|
/* Global library. */
|
|
|
|
|
|
/* Application-specific. */
|
|
|
|
#include "smtpd.h"
|
|
|
|
/* smtpd_peer_init - initialize peer information */
|
|
|
|
void smtpd_peer_init(SMTPD_STATE * state)
|
|
{
|
|
struct sockaddr_in sin;
|
|
SOCKADDR_SIZE len = sizeof(sin);
|
|
struct hostent *hp;
|
|
int i;
|
|
|
|
/*
|
|
* Look up the peer address information.
|
|
*/
|
|
if (getpeername(vstream_fileno(state->client),
|
|
(struct sockaddr *) & sin, &len) >= 0) {
|
|
errno = 0;
|
|
}
|
|
|
|
/*
|
|
* If peer went away, give up.
|
|
*/
|
|
if (errno == ECONNRESET || errno == ECONNABORTED) {
|
|
msg_info("errno %d %m", errno);
|
|
state->name = mystrdup("unknown");
|
|
state->addr = mystrdup("unknown");
|
|
state->peer_code = 5;
|
|
}
|
|
|
|
/*
|
|
* Look up and "verify" the client hostname.
|
|
*/
|
|
else if (errno == 0 && sin.sin_family == AF_INET) {
|
|
state->addr = mystrdup(inet_ntoa(sin.sin_addr));
|
|
hp = gethostbyaddr((char *) &(sin.sin_addr),
|
|
sizeof(sin.sin_addr), AF_INET);
|
|
if (hp == 0) {
|
|
state->name = mystrdup("unknown");
|
|
state->peer_code = (h_errno == TRY_AGAIN ? 4 : 5);
|
|
} else if (!valid_hostname(hp->h_name)) {
|
|
state->name = mystrdup("unknown");
|
|
state->peer_code = 5;
|
|
} else {
|
|
state->name = mystrdup(hp->h_name); /* hp->name is clobbered!! */
|
|
state->peer_code = 2;
|
|
|
|
/*
|
|
* Reject the hostname if it does not list the peer address.
|
|
*/
|
|
#define REJECT_PEER_NAME(state, code) { \
|
|
myfree(state->name); \
|
|
state->name = mystrdup("unknown"); \
|
|
state->peer_code = 5; \
|
|
}
|
|
|
|
hp = gethostbyname(state->name); /* clobbers hp->name!! */
|
|
if (hp == 0) {
|
|
msg_warn("%s: hostname %s verification failed: %s",
|
|
state->addr, state->name, HSTRERROR(h_errno));
|
|
REJECT_PEER_NAME(state, (h_errno == TRY_AGAIN ? 4 : 5));
|
|
} else if (hp->h_length != sizeof(sin.sin_addr)) {
|
|
msg_warn("%s: hostname %s verification failed: bad address size %d",
|
|
state->addr, state->name, hp->h_length);
|
|
REJECT_PEER_NAME(state, 5);
|
|
} else {
|
|
for (i = 0; /* void */ ; i++) {
|
|
if (hp->h_addr_list[i] == 0) {
|
|
msg_warn("%s: address not listed for hostname %s",
|
|
state->addr, state->name);
|
|
REJECT_PEER_NAME(state, 5);
|
|
break;
|
|
}
|
|
if (memcmp(hp->h_addr_list[i],
|
|
(char *) &sin.sin_addr,
|
|
sizeof(sin.sin_addr)) == 0)
|
|
break; /* keep peer name */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If it's not Internet, assume the client is local, and avoid using the
|
|
* naming service because that can hang when the machine is disconnected.
|
|
*/
|
|
else {
|
|
state->name = mystrdup("localhost");
|
|
state->addr = mystrdup("127.0.0.1"); /* XXX bogus. */
|
|
state->peer_code = 2;
|
|
}
|
|
|
|
/*
|
|
* Do the name[addr] formatting for pretty reports.
|
|
*/
|
|
state->namaddr =
|
|
concatenate(state->name, "[", state->addr, "]", (char *) 0);
|
|
}
|
|
|
|
/* smtpd_peer_reset - destroy peer information */
|
|
|
|
void smtpd_peer_reset(SMTPD_STATE * state)
|
|
{
|
|
myfree(state->name);
|
|
myfree(state->addr);
|
|
myfree(state->namaddr);
|
|
}
|