#include <sys_defs.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <errno.h>
#include <netdb.h>
#include <string.h>
#include <htable.h>
#include <msg.h>
#include <mymalloc.h>
#include <stringops.h>
#include <myaddrinfo.h>
#include <sock_addr.h>
#include <inet_proto.h>
#include <split_at.h>
#include <mail_proto.h>
#include <valid_mailhost_addr.h>
#include <mail_params.h>
#include <haproxy_srvr.h>
#include "smtpd.h"
static INET_PROTO_INFO *proto_info;
static int smtpd_peer_sockaddr_to_hostaddr(SMTPD_STATE *state)
{
const char *myname = "smtpd_peer_sockaddr_to_hostaddr";
struct sockaddr *sa = (struct sockaddr *) &(state->sockaddr);
SOCKADDR_SIZE sa_length = state->sockaddr_len;
if (sa->sa_family == AF_INET
#ifdef AF_INET6
|| sa->sa_family == AF_INET6
#endif
) {
MAI_HOSTADDR_STR client_addr;
MAI_SERVPORT_STR client_port;
MAI_HOSTADDR_STR server_addr;
MAI_SERVPORT_STR server_port;
int aierr;
char *colonp;
if (strchr((char *) proto_info->sa_family_list, sa->sa_family) == 0)
msg_fatal("cannot handle socket type %s with \"%s = %s\"",
#ifdef AF_INET6
sa->sa_family == AF_INET6 ? "AF_INET6" :
#endif
sa->sa_family == AF_INET ? "AF_INET" :
"other", VAR_INET_PROTOCOLS, var_inet_protocols);
if (geteuid() != var_owner_uid || getuid() != var_owner_uid) {
msg_error("incorrect SMTP server privileges: uid=%lu euid=%lu",
(unsigned long) getuid(), (unsigned long) geteuid());
msg_fatal("the Postfix SMTP server must run with $%s privileges",
VAR_MAIL_OWNER);
}
if ((aierr = sockaddr_to_hostaddr(sa, sa_length, &client_addr,
&client_port, 0)) != 0)
msg_fatal("%s: cannot convert client address/port to string: %s",
myname, MAI_STRERROR(aierr));
state->port = mystrdup(client_port.buf);
#ifdef HAS_IPV6
if (strchr(client_addr.buf, '%') != 0)
msg_panic("%s: address %s has datalink suffix",
myname, client_addr.buf);
#endif
#ifdef HAS_IPV6
if (sa->sa_family == AF_INET6) {
if (strchr((char *) proto_info->sa_family_list, AF_INET) != 0
&& IN6_IS_ADDR_V4MAPPED(&SOCK_ADDR_IN6_ADDR(sa))
&& (colonp = strrchr(client_addr.buf, ':')) != 0) {
struct addrinfo *res0;
if (msg_verbose > 1)
msg_info("%s: rewriting V4-mapped address \"%s\" to \"%s\"",
myname, client_addr.buf, colonp + 1);
state->addr = mystrdup(colonp + 1);
state->rfc_addr = mystrdup(colonp + 1);
state->addr_family = AF_INET;
aierr = hostaddr_to_sockaddr(state->addr, (char *) 0, 0, &res0);
if (aierr)
msg_fatal("%s: cannot convert %s from string to binary: %s",
myname, state->addr, MAI_STRERROR(aierr));
sa_length = res0->ai_addrlen;
if (sa_length > sizeof(state->sockaddr))
sa_length = sizeof(state->sockaddr);
memcpy((void *) sa, res0->ai_addr, sa_length);
freeaddrinfo(res0);
}
else {
state->addr = mystrdup(client_addr.buf);
state->rfc_addr =
concatenate(IPV6_COL, client_addr.buf, (char *) 0);
state->addr_family = sa->sa_family;
}
}
else
#endif
{
state->addr = mystrdup(client_addr.buf);
state->rfc_addr = mystrdup(client_addr.buf);
state->addr_family = sa->sa_family;
}
if ((aierr = sockaddr_to_hostaddr((struct sockaddr *)
&state->dest_sockaddr,
state->dest_sockaddr_len,
&server_addr,
&server_port, 0)) != 0)
msg_fatal("%s: cannot convert server address/port to string: %s",
myname, MAI_STRERROR(aierr));
state->dest_addr = mystrdup(server_addr.buf);
state->dest_port = mystrdup(server_port.buf);
return (0);
}
else {
return (-1);
}
}
static void smtpd_peer_sockaddr_to_hostname(SMTPD_STATE *state)
{
struct sockaddr *sa = (struct sockaddr *) &(state->sockaddr);
SOCKADDR_SIZE sa_length = state->sockaddr_len;
MAI_HOSTNAME_STR client_name;
int aierr;
#define TEMP_AI_ERROR(e) \
((e) == EAI_AGAIN || (e) == EAI_MEMORY || (e) == EAI_SYSTEM)
#define REJECT_PEER_NAME(state, code) { \
myfree(state->name); \
state->name = mystrdup(CLIENT_NAME_UNKNOWN); \
state->name_status = code; \
}
if (var_smtpd_peername_lookup == 0) {
state->name = mystrdup(CLIENT_NAME_UNKNOWN);
state->reverse_name = mystrdup(CLIENT_NAME_UNKNOWN);
state->name_status = SMTPD_PEER_CODE_PERM;
state->reverse_name_status = SMTPD_PEER_CODE_PERM;
} else if ((aierr = sockaddr_to_hostname(sa, sa_length, &client_name,
(MAI_SERVNAME_STR *) 0, 0)) != 0) {
state->name = mystrdup(CLIENT_NAME_UNKNOWN);
state->reverse_name = mystrdup(CLIENT_NAME_UNKNOWN);
state->name_status = (TEMP_AI_ERROR(aierr) ?
SMTPD_PEER_CODE_TEMP : SMTPD_PEER_CODE_PERM);
state->reverse_name_status = (TEMP_AI_ERROR(aierr) ?
SMTPD_PEER_CODE_TEMP : SMTPD_PEER_CODE_PERM);
} else {
struct addrinfo *res0;
struct addrinfo *res;
state->name = mystrdup(client_name.buf);
state->reverse_name = mystrdup(client_name.buf);
state->name_status = SMTPD_PEER_CODE_OK;
state->reverse_name_status = SMTPD_PEER_CODE_OK;
aierr = hostname_to_sockaddr_pf(state->name, state->addr_family,
(char *) 0, 0, &res0);
if (aierr) {
msg_warn("hostname %s does not resolve to address %s: %s",
state->name, state->addr, MAI_STRERROR(aierr));
REJECT_PEER_NAME(state, (TEMP_AI_ERROR(aierr) ?
SMTPD_PEER_CODE_TEMP : SMTPD_PEER_CODE_FORGED));
} else {
for (res = res0; ; res = res->ai_next) {
if (res == 0) {
msg_warn("hostname %s does not resolve to address %s",
state->name, state->addr);
REJECT_PEER_NAME(state, SMTPD_PEER_CODE_FORGED);
break;
}
if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) {
msg_info("skipping address family %d for host %s",
res->ai_family, state->name);
continue;
}
if (sock_addr_cmp_addr(res->ai_addr, sa) == 0)
break;
}
freeaddrinfo(res0);
}
}
}
static void smtpd_peer_hostaddr_to_sockaddr(SMTPD_STATE *state)
{
const char *myname = "smtpd_peer_hostaddr_to_sockaddr";
struct addrinfo *res;
int aierr;
if ((aierr = hostaddr_to_sockaddr(state->addr, state->port,
SOCK_STREAM, &res)) != 0)
msg_fatal("%s: cannot convert client address/port to string: %s",
myname, MAI_STRERROR(aierr));
if (res->ai_addrlen > sizeof(state->sockaddr))
msg_panic("%s: address length > struct sockaddr_storage", myname);
memcpy((void *) &(state->sockaddr), res->ai_addr, res->ai_addrlen);
state->sockaddr_len = res->ai_addrlen;
freeaddrinfo(res);
}
static void smtpd_peer_not_inet(SMTPD_STATE *state)
{
state->name = mystrdup("localhost");
state->reverse_name = mystrdup("localhost");
#ifdef AF_INET6
if (proto_info->sa_family_list[0] == PF_INET6) {
state->addr = mystrdup("::1");
state->rfc_addr = mystrdup(IPV6_COL "::1");
} else
#endif
{
state->addr = mystrdup("127.0.0.1");
state->rfc_addr = mystrdup("127.0.0.1");
}
state->addr_family = AF_UNSPEC;
state->name_status = SMTPD_PEER_CODE_OK;
state->reverse_name_status = SMTPD_PEER_CODE_OK;
state->port = mystrdup("0");
state->dest_addr = mystrdup(state->addr);
state->dest_port = mystrdup(state->port);
}
static void smtpd_peer_no_client(SMTPD_STATE *state)
{
smtpd_peer_reset(state);
state->name = mystrdup(CLIENT_NAME_UNKNOWN);
state->reverse_name = mystrdup(CLIENT_NAME_UNKNOWN);
state->addr = mystrdup(CLIENT_ADDR_UNKNOWN);
state->rfc_addr = mystrdup(CLIENT_ADDR_UNKNOWN);
state->addr_family = AF_UNSPEC;
state->name_status = SMTPD_PEER_CODE_PERM;
state->reverse_name_status = SMTPD_PEER_CODE_PERM;
state->port = mystrdup(CLIENT_PORT_UNKNOWN);
state->dest_addr = mystrdup(SERVER_ADDR_UNKNOWN);
state->dest_port = mystrdup(SERVER_PORT_UNKNOWN);
}
static void smtpd_peer_from_pass_attr(SMTPD_STATE *state)
{
HTABLE *attr = (HTABLE *) vstream_context(state->client);
const char *cp;
if ((cp = htable_find(attr, MAIL_ATTR_ACT_CLIENT_ADDR)) == 0)
msg_fatal("missing client address from proxy");
if (strrchr(cp, ':') != 0) {
if (valid_ipv6_hostaddr(cp, DO_GRIPE) == 0)
msg_fatal("bad IPv6 client address syntax from proxy: %s", cp);
state->addr = mystrdup(cp);
state->rfc_addr = concatenate(IPV6_COL, cp, (char *) 0);
state->addr_family = AF_INET6;
} else {
if (valid_ipv4_hostaddr(cp, DO_GRIPE) == 0)
msg_fatal("bad IPv4 client address syntax from proxy: %s", cp);
state->addr = mystrdup(cp);
state->rfc_addr = mystrdup(cp);
state->addr_family = AF_INET;
}
if ((cp = htable_find(attr, MAIL_ATTR_ACT_CLIENT_PORT)) == 0)
msg_fatal("missing client port from proxy");
if (valid_hostport(cp, DO_GRIPE) == 0)
msg_fatal("bad TCP client port number syntax from proxy: %s", cp);
state->port = mystrdup(cp);
if ((cp = htable_find(attr, MAIL_ATTR_ACT_SERVER_ADDR)) == 0)
msg_fatal("missing server address from proxy");
if (valid_hostaddr(cp, DO_GRIPE) == 0)
msg_fatal("bad IPv6 server address syntax from proxy: %s", cp);
state->dest_addr = mystrdup(cp);
if ((cp = htable_find(attr, MAIL_ATTR_ACT_SERVER_PORT)) == 0)
msg_fatal("missing server port from proxy");
if (valid_hostport(cp, DO_GRIPE) == 0)
msg_fatal("bad TCP server port number syntax from proxy: %s", cp);
state->dest_port = mystrdup(cp);
smtpd_peer_hostaddr_to_sockaddr(state);
}
static void smtpd_peer_from_default(SMTPD_STATE *state)
{
state->sockaddr_len = sizeof(state->sockaddr);
state->dest_sockaddr_len = sizeof(state->dest_sockaddr);
if (getpeername(vstream_fileno(state->client),
(struct sockaddr *) &state->sockaddr,
&state->sockaddr_len) <0
|| getsockname(vstream_fileno(state->client),
(struct sockaddr *) &state->dest_sockaddr,
&state->dest_sockaddr_len) < 0) {
if (errno == ENOTSOCK)
smtpd_peer_not_inet(state);
else
smtpd_peer_no_client(state);
} else {
if (smtpd_peer_sockaddr_to_hostaddr(state) < 0)
smtpd_peer_not_inet(state);
}
}
static void smtpd_peer_from_proxy(SMTPD_STATE *state)
{
typedef struct {
const char *name;
int (*endpt_lookup) (SMTPD_STATE *);
} SMTPD_ENDPT_LOOKUP_INFO;
static const SMTPD_ENDPT_LOOKUP_INFO smtpd_endpt_lookup_info[] = {
HAPROXY_PROTO_NAME, smtpd_peer_from_haproxy,
0,
};
const SMTPD_ENDPT_LOOKUP_INFO *pp;
for (pp = smtpd_endpt_lookup_info; ; pp++) {
if (pp->name == 0)
msg_fatal("unsupported %s value: %s",
VAR_SMTPD_UPROXY_PROTO, var_smtpd_uproxy_proto);
if (strcmp(var_smtpd_uproxy_proto, pp->name) == 0)
break;
}
if (pp->endpt_lookup(state) < 0) {
smtpd_peer_no_client(state);
state->flags |= SMTPD_FLAG_HANGUP;
} else {
smtpd_peer_hostaddr_to_sockaddr(state);
}
}
void smtpd_peer_init(SMTPD_STATE *state)
{
if (proto_info == 0)
proto_info = inet_proto_info();
memset((void *) &(state->sockaddr), 0, sizeof(state->sockaddr));
state->sockaddr_len = 0;
state->name = 0;
state->reverse_name = 0;
state->addr = 0;
state->namaddr = 0;
state->rfc_addr = 0;
state->port = 0;
state->dest_addr = 0;
state->dest_port = 0;
if (vstream_context(state->client) != 0) {
smtpd_peer_from_pass_attr(state);
if (*var_smtpd_uproxy_proto != 0)
msg_warn("ignoring non-empty %s setting behind postscreen",
VAR_SMTPD_UPROXY_PROTO);
} else if (SMTPD_STAND_ALONE(state) || *var_smtpd_uproxy_proto == 0) {
smtpd_peer_from_default(state);
} else {
smtpd_peer_from_proxy(state);
}
if (state->name == 0)
smtpd_peer_sockaddr_to_hostname(state);
state->namaddr = SMTPD_BUILD_NAMADDRPORT(state->name, state->addr,
state->port);
}
void smtpd_peer_reset(SMTPD_STATE *state)
{
if (state->name)
myfree(state->name);
if (state->reverse_name)
myfree(state->reverse_name);
if (state->addr)
myfree(state->addr);
if (state->namaddr)
myfree(state->namaddr);
if (state->rfc_addr)
myfree(state->rfc_addr);
if (state->port)
myfree(state->port);
if (state->dest_addr)
myfree(state->dest_addr);
if (state->dest_port)
myfree(state->dest_port);
}