2005-10-29 16:26:44 +00:00
|
|
|
#include "common.h"
|
|
|
|
|
#include <ndb.h>
|
|
|
|
|
#include "smtp.h" /* to publish dial_string_parse */
|
2006-02-11 23:38:55 +00:00
|
|
|
#include <ip.h>
|
2005-10-29 16:26:44 +00:00
|
|
|
|
|
|
|
|
enum
|
|
|
|
|
{
|
|
|
|
|
Nmx= 16,
|
|
|
|
|
Maxstring= 256,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef struct Mx Mx;
|
|
|
|
|
struct Mx
|
|
|
|
|
{
|
|
|
|
|
char host[256];
|
|
|
|
|
char ip[24];
|
|
|
|
|
int pref;
|
|
|
|
|
};
|
|
|
|
|
static Mx mx[Nmx];
|
|
|
|
|
|
|
|
|
|
Ndb *db;
|
|
|
|
|
extern int debug;
|
|
|
|
|
|
|
|
|
|
static int mxlookup(DS*, char*);
|
|
|
|
|
static int mxlookup1(DS*, char*);
|
|
|
|
|
static int compar(void*, void*);
|
|
|
|
|
static int callmx(DS*, char*, char*);
|
|
|
|
|
static void expand_meta(DS *ds);
|
|
|
|
|
extern int cistrcmp(char*, char*);
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
mxdial(char *addr, char *ddomain, char *gdomain)
|
|
|
|
|
{
|
|
|
|
|
int fd;
|
|
|
|
|
DS ds;
|
|
|
|
|
char err[Errlen];
|
|
|
|
|
|
|
|
|
|
addr = netmkaddr(addr, 0, "smtp");
|
|
|
|
|
dial_string_parse(addr, &ds);
|
|
|
|
|
|
|
|
|
|
/* try connecting to destination or any of it's mail routers */
|
|
|
|
|
fd = callmx(&ds, addr, ddomain);
|
|
|
|
|
|
|
|
|
|
/* try our mail gateway */
|
|
|
|
|
rerrstr(err, sizeof(err));
|
2006-02-11 23:38:55 +00:00
|
|
|
if(fd < 0 && gdomain && strstr(err, "can't translate") != 0)
|
2005-10-29 16:26:44 +00:00
|
|
|
fd = dial(netmkaddr(gdomain, 0, "smtp"), 0, 0, 0);
|
|
|
|
|
|
|
|
|
|
return fd;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-11 23:38:55 +00:00
|
|
|
static int
|
|
|
|
|
timeout(void*, char *msg)
|
|
|
|
|
{
|
|
|
|
|
if(strstr(msg, "alarm"))
|
|
|
|
|
return 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2005-10-29 16:26:44 +00:00
|
|
|
/*
|
|
|
|
|
* take an address and return all the mx entries for it,
|
|
|
|
|
* most preferred first
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
callmx(DS *ds, char *dest, char *domain)
|
|
|
|
|
{
|
|
|
|
|
int fd, i, nmx;
|
|
|
|
|
char addr[Maxstring];
|
|
|
|
|
|
|
|
|
|
/* get a list of mx entries */
|
|
|
|
|
nmx = mxlookup(ds, domain);
|
|
|
|
|
if(nmx < 0){
|
|
|
|
|
/* dns isn't working, don't just dial */
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if(nmx == 0){
|
|
|
|
|
if(debug)
|
|
|
|
|
fprint(2, "mxlookup returns nothing\n");
|
|
|
|
|
return dial(dest, 0, 0, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* refuse to honor loopback addresses given by dns */
|
|
|
|
|
for(i = 0; i < nmx; i++){
|
|
|
|
|
if(strcmp(mx[i].ip, "127.0.0.1") == 0){
|
|
|
|
|
if(debug)
|
|
|
|
|
fprint(2, "mxlookup returns loopback\n");
|
|
|
|
|
werrstr("illegal: domain lists 127.0.0.1 as mail server");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* sort by preference */
|
|
|
|
|
if(nmx > 1)
|
|
|
|
|
qsort(mx, nmx, sizeof(Mx), compar);
|
|
|
|
|
|
|
|
|
|
/* dial each one in turn */
|
|
|
|
|
for(i = 0; i < nmx; i++){
|
|
|
|
|
snprint(addr, sizeof(addr), "%s/%s!%s!%s", ds->netdir, ds->proto,
|
|
|
|
|
mx[i].host, ds->service);
|
|
|
|
|
if(debug)
|
|
|
|
|
fprint(2, "mxdial trying %s\n", addr);
|
2006-02-11 23:38:55 +00:00
|
|
|
atnotify(timeout, 1);
|
|
|
|
|
alarm(10*1000);
|
2005-10-29 16:26:44 +00:00
|
|
|
fd = dial(addr, 0, 0, 0);
|
2006-02-11 23:38:55 +00:00
|
|
|
alarm(0);
|
|
|
|
|
atnotify(timeout, 0);
|
2005-10-29 16:26:44 +00:00
|
|
|
if(fd >= 0)
|
|
|
|
|
return fd;
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* call the dns process and have it try to resolve the mx request
|
|
|
|
|
*
|
|
|
|
|
* this routine knows about the firewall and tries inside and outside
|
|
|
|
|
* dns's seperately.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
mxlookup(DS *ds, char *domain)
|
|
|
|
|
{
|
|
|
|
|
int n;
|
|
|
|
|
|
|
|
|
|
/* just in case we find no domain name */
|
|
|
|
|
strcpy(domain, ds->host);
|
|
|
|
|
|
|
|
|
|
if(ds->netdir){
|
|
|
|
|
n = mxlookup1(ds, domain);
|
|
|
|
|
} else {
|
|
|
|
|
ds->netdir = "/net";
|
|
|
|
|
n = mxlookup1(ds, domain);
|
|
|
|
|
if(n == 0) {
|
|
|
|
|
ds->netdir = "/net.alt";
|
|
|
|
|
n = mxlookup1(ds, domain);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
mxlookup1(DS *ds, char *domain)
|
|
|
|
|
{
|
|
|
|
|
char buf[1024];
|
|
|
|
|
char dnsname[Maxstring];
|
|
|
|
|
char *fields[4];
|
|
|
|
|
int i, n, fd, nmx;
|
|
|
|
|
|
|
|
|
|
snprint(dnsname, sizeof dnsname, "%s/dns", ds->netdir);
|
|
|
|
|
|
|
|
|
|
fd = open(dnsname, ORDWR);
|
|
|
|
|
if(fd < 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
nmx = 0;
|
|
|
|
|
snprint(buf, sizeof(buf), "%s mx", ds->host);
|
|
|
|
|
if(debug)
|
|
|
|
|
fprint(2, "sending %s '%s'\n", dnsname, buf);
|
|
|
|
|
n = write(fd, buf, strlen(buf));
|
|
|
|
|
if(n < 0){
|
|
|
|
|
rerrstr(buf, sizeof buf);
|
|
|
|
|
if(debug)
|
|
|
|
|
fprint(2, "dns: %s\n", buf);
|
|
|
|
|
if(strstr(buf, "dns failure")){
|
|
|
|
|
/* if dns fails for the mx lookup, we have to stop */
|
|
|
|
|
close(fd);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
/*
|
|
|
|
|
* get any mx entries
|
|
|
|
|
*/
|
|
|
|
|
seek(fd, 0, 0);
|
|
|
|
|
while(nmx < Nmx && (n = read(fd, buf, sizeof(buf)-1)) > 0){
|
|
|
|
|
buf[n] = 0;
|
|
|
|
|
if(debug)
|
|
|
|
|
fprint(2, "dns mx: %s\n", buf);
|
|
|
|
|
n = getfields(buf, fields, 4, 1, " \t");
|
|
|
|
|
if(n < 4)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if(strchr(domain, '.') == 0)
|
|
|
|
|
strcpy(domain, fields[0]);
|
|
|
|
|
|
|
|
|
|
strncpy(mx[nmx].host, fields[3], sizeof(mx[n].host)-1);
|
|
|
|
|
mx[nmx].pref = atoi(fields[2]);
|
|
|
|
|
nmx++;
|
|
|
|
|
}
|
|
|
|
|
if(debug)
|
|
|
|
|
fprint(2, "dns mx; got %d entries\n", nmx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* no mx record? try name itself.
|
|
|
|
|
*/
|
|
|
|
|
/*
|
|
|
|
|
* BUG? If domain has no dots, then we used to look up ds->host
|
|
|
|
|
* but return domain instead of ds->host in the list. Now we return
|
|
|
|
|
* ds->host. What will this break?
|
|
|
|
|
*/
|
|
|
|
|
if(nmx == 0){
|
|
|
|
|
mx[0].pref = 1;
|
|
|
|
|
strncpy(mx[0].host, ds->host, sizeof(mx[0].host));
|
|
|
|
|
nmx++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* look up all ip addresses
|
|
|
|
|
*/
|
|
|
|
|
for(i = 0; i < nmx; i++){
|
|
|
|
|
seek(fd, 0, 0);
|
|
|
|
|
snprint(buf, sizeof buf, "%s ip", mx[i].host);
|
|
|
|
|
mx[i].ip[0] = 0;
|
|
|
|
|
if(write(fd, buf, strlen(buf)) < 0)
|
|
|
|
|
goto no;
|
|
|
|
|
seek(fd, 0, 0);
|
|
|
|
|
if((n = read(fd, buf, sizeof buf-1)) < 0)
|
|
|
|
|
goto no;
|
|
|
|
|
buf[n] = 0;
|
|
|
|
|
if(getfields(buf, fields, 4, 1, " \t") < 3)
|
|
|
|
|
goto no;
|
|
|
|
|
strncpy(mx[i].ip, fields[2], sizeof(mx[i].ip)-1);
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
no:
|
|
|
|
|
/* remove mx[i] and go around again */
|
|
|
|
|
nmx--;
|
|
|
|
|
mx[i] = mx[nmx];
|
|
|
|
|
i--;
|
|
|
|
|
}
|
|
|
|
|
return nmx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
compar(void *a, void *b)
|
|
|
|
|
{
|
|
|
|
|
return ((Mx*)a)->pref - ((Mx*)b)->pref;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* break up an address to its component parts */
|
|
|
|
|
void
|
|
|
|
|
dial_string_parse(char *str, DS *ds)
|
|
|
|
|
{
|
|
|
|
|
char *p, *p2;
|
|
|
|
|
|
|
|
|
|
strncpy(ds->buf, str, sizeof(ds->buf));
|
|
|
|
|
ds->buf[sizeof(ds->buf)-1] = 0;
|
|
|
|
|
|
|
|
|
|
p = strchr(ds->buf, '!');
|
|
|
|
|
if(p == 0) {
|
|
|
|
|
ds->netdir = 0;
|
|
|
|
|
ds->proto = "net";
|
|
|
|
|
ds->host = ds->buf;
|
|
|
|
|
} else {
|
|
|
|
|
if(*ds->buf != '/'){
|
|
|
|
|
ds->netdir = 0;
|
|
|
|
|
ds->proto = ds->buf;
|
|
|
|
|
} else {
|
|
|
|
|
for(p2 = p; *p2 != '/'; p2--)
|
|
|
|
|
;
|
|
|
|
|
*p2++ = 0;
|
|
|
|
|
ds->netdir = ds->buf;
|
|
|
|
|
ds->proto = p2;
|
|
|
|
|
}
|
|
|
|
|
*p = 0;
|
|
|
|
|
ds->host = p + 1;
|
|
|
|
|
}
|
|
|
|
|
ds->service = strchr(ds->host, '!');
|
|
|
|
|
if(ds->service)
|
|
|
|
|
*ds->service++ = 0;
|
|
|
|
|
if(*ds->host == '$')
|
|
|
|
|
expand_meta(ds);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0 /* jpc */
|
|
|
|
|
static void
|
|
|
|
|
expand_meta(DS *ds)
|
|
|
|
|
{
|
|
|
|
|
char buf[128], cs[128], *net, *p;
|
|
|
|
|
int fd, n;
|
|
|
|
|
|
|
|
|
|
net = ds->netdir;
|
|
|
|
|
if(!net)
|
|
|
|
|
net = "/net";
|
|
|
|
|
|
|
|
|
|
if(debug)
|
|
|
|
|
fprint(2, "expanding %s!%s\n", net, ds->host);
|
|
|
|
|
snprint(cs, sizeof(cs), "%s/cs", net);
|
|
|
|
|
if((fd = open(cs, ORDWR)) == -1){
|
|
|
|
|
if(debug)
|
|
|
|
|
fprint(2, "open %s: %r\n", cs);
|
|
|
|
|
syslog(0, "smtp", "cannot open %s: %r", cs);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
snprint(buf, sizeof(buf), "!ipinfo %s", ds->host+1); // +1 to skip $
|
|
|
|
|
if(write(fd, buf, strlen(buf)) <= 0){
|
|
|
|
|
if(debug)
|
|
|
|
|
fprint(2, "write %s: %r\n", cs);
|
|
|
|
|
syslog(0, "smtp", "%s to %s - write failed: %r", buf, cs);
|
|
|
|
|
close(fd);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
seek(fd, 0, 0);
|
|
|
|
|
if((n = read(fd, ds->expand, sizeof(ds->expand)-1)) < 0){
|
|
|
|
|
if(debug)
|
|
|
|
|
fprint(2, "read %s: %r\n", cs);
|
|
|
|
|
syslog(0, "smtp", "%s - read failed: %r", cs);
|
|
|
|
|
close(fd);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
|
|
ds->expand[n] = 0;
|
|
|
|
|
if((p = strchr(ds->expand, '=')) == nil){
|
|
|
|
|
if(debug)
|
|
|
|
|
fprint(2, "response %s: %s\n", cs, ds->expand);
|
|
|
|
|
syslog(0, "smtp", "%q from %s - bad response: %r", ds->expand, cs);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
ds->host = p+1;
|
|
|
|
|
|
|
|
|
|
/* take only first one returned (quasi-bug) */
|
|
|
|
|
if((p = strchr(ds->host, ' ')) != nil)
|
|
|
|
|
*p = 0;
|
|
|
|
|
}
|
|
|
|
|
#endif /* jpc */
|
|
|
|
|
|
2006-02-11 23:38:55 +00:00
|
|
|
/* XXX */
|
2005-10-29 16:26:44 +00:00
|
|
|
static void
|
|
|
|
|
expand_meta(DS *ds)
|
|
|
|
|
{
|
|
|
|
|
Ndb *db;
|
|
|
|
|
Ndbs s;
|
|
|
|
|
char *sys, *smtpserver;
|
|
|
|
|
|
|
|
|
|
sys = sysname();
|
|
|
|
|
db = ndbopen(unsharp("#9/ndb/local"));
|
|
|
|
|
fprint(2,"%s",ds->host);
|
|
|
|
|
smtpserver = ndbgetvalue(db, &s, "sys", sys, "smtp", nil);
|
|
|
|
|
snprint(ds->host,128,"%s",smtpserver);
|
|
|
|
|
fprint(2," exanded to %s\n",ds->host);
|
|
|
|
|
|
|
|
|
|
}
|