#include "spf.h" #define mrprint(...) snprint(m->mreg, sizeof m->mreg, __VA_ARGS__) typedef struct Mfmt Mfmt; typedef struct Macro Macro; struct Mfmt { char buf[0xff]; char *p; char *e; char mreg[0xff]; int f1; int f2; int f3; char *sender; char *domain; char *ip; char *helo; uchar ipa[IPaddrlen]; }; struct Macro { char c; void (*f)(Mfmt*); }; static void ms(Mfmt *m) { mrprint("%s", m->sender); } static void ml(Mfmt *m) { char *p; mrprint("%s", m->sender); if(p = strchr(m->mreg, '@')) *p = 0; } static void mo(Mfmt *m) { mrprint("%s", m->domain); } static void md(Mfmt *m) { mrprint("%s", m->domain); } static void mi(Mfmt *m) { uint i, c; if(isv4(m->ipa)) mrprint("%s", m->ip); else{ for(i = 0; i < 32; i++){ c = m->ipa[i / 2]; if((i & 1) == 0) c >>= 4; sprint(m->mreg+2*i, "%ux.", c & 0xf); } m->mreg[2*32 - 1] = 0; } } static int maquery(Mfmt *m, char *d, char *match, int recur) { int r; Ndbtuple *t, *n; r = 0; t = vdnsquery(d, "any", recur); for(n = t; n; n = n->entry) if(strcmp(n->attr, "ip") == 0 || strcmp(n->attr, "ipv6") == 0){ if(strcmp(n->val, match) == 0){ r = 1; break; } }else if(strcmp(n->attr, "cname") == 0) maquery(m, d, match, recur+1); ndbfree(t); return r; } static int lrcmp(char *a, char *b) { return strlen(b) - strlen(a); } static void mptrquery(Mfmt *m, char *d, int recur) { int nlist, i; char *s, buf[64], *a, *list[11]; Ndbtuple *t, *n; nlist = 0; dnreverse(buf, sizeof buf, s = strdup(m->ip)); t = vdnsquery(buf, "ptr", recur); for(n = t; n; n = n->entry) if((strcmp(n->attr, "dom") == 0 || strcmp(n->attr, "cname") == 0) && dncontains(n->val, d) && maquery(m, n->val, m->ip, recur+1)) list[nlist++] = strdup(n->val); ndbfree(t); free(s); qsort(list, nlist, sizeof *list, (int (*)(void*, void*))lrcmp); a = "unknown"; for(i = 0; i < nlist; i++) if(strcmp(list[i], d) == 0){ a = list[i]; break; }else if(dncontains(list[i], d)) a = list[i]; mrprint("%s", a); for(i = 0; i < nlist; i++) free(list[i]); } static void mp(Mfmt *m) { /* * we're supposed to do a reverse lookup on the ip & compare. * this is a very bad idea. */ // mrprint("unknown); /* simulate dns failure */ mptrquery(m, m->domain, 0); } static void mv(Mfmt *m) { if(isv4(m->ipa)) mrprint("in-addr"); else mrprint("ip6"); } static void mh(Mfmt *m) { mrprint("%s", m->helo); } static Macro tab[] = { 's', ms, /* sender */ 'l', ml, /* local part of sender */ 'o', mo, /* domain of sender */ 'd', md, /* domain */ 'i', mi, /* ip */ 'p', mp, /* validated domain name of ip */ 'v', mv, /* "in-addr" if ipv4, or "ip6" if ipv6 */ 'h', mh, /* helo/ehol domain */ }; static void reverse(Mfmt *m) { int i, n; char *p, *e, buf[100], *f[32], sep[2]; sep[0] = m->f2; sep[1] = 0; n = getfields(m->mreg, f, nelem(f), 0, sep); p = e = buf; e += sizeof buf-1; for(i = 0; i < n; i++) p = seprint(p, e, "%s.", f[n-i-1]); if(p > buf) p--; *p = 0; memmove(m->mreg, buf, p-buf+1); m->f2 = '.'; } static void chop(Mfmt *m) { int i, n; char *p, *e, buf[100], *f[32], sep[2]; sep[0] = m->f2; sep[1] = 0; n = getfields(m->mreg, f, nelem(f), 0, sep); p = e = buf; e += sizeof buf-1; if(m->f1 == 0) i = 0; else i = n - m->f1; if(i < 0) i = 0; for(; i < n; i++) p = seprint(p, e, "%s.", f[i]); if(p > buf) p--; *p = 0; memmove(m->mreg, buf, p-buf+1); m->f2 = '.'; } static void mfmtinit(Mfmt *m, char *s, char *d, char *h, char *i) { memset(m, 0, sizeof *m); m->p = m->buf; m->e = m->p + sizeof m->buf - 1; m->sender = s? s: "Unsets"; m->domain = d? d: "Unsetd"; m->helo = h? h: "Unseth"; m->ip = i? i: "127.0.0.2"; parseip(m->ipa, m->ip); } /* url escaping? rfc3986 */ static void mputc(Mfmt *m, int c) { if(m->p < m->e) *m->p++ = c; } static void mputs(Mfmt *m, char *s) { int c; while(c = *s++) mputc(m, c); } char* macro(char *f, char *sender, char *dom, char *hdom, char *ip) { int i, c; char *p; Mfmt m; mfmtinit(&m, sender, dom, hdom, ip); while(*f){ while((c = *f++) && c != '%') mputc(&m, c); if(c == 0) break; switch(*f++){ case '%': mputc(&m, '%'); break; case '-': mputs(&m, "%20"); break; case '_': mputc(&m, ' '); break; case '{': m.f1 = 0; m.f2 = '.'; m.f3 = 0; c = *f++; if(c >= 'A' && c <= 'Z') c += 0x20; for(i = 0; i < nelem(tab); i++) if(tab[i].c == c) break; if(i == nelem(tab)) return 0; for(c = *f++; c >= '0' && c <= '9'; c = *f++) m.f1 = m.f1*10 + c-'0'; if(c == 'R' || c == 'r'){ m.f3 = 'r'; c = *f++; } for(; p = strchr(".-+,_=", c); c = *f++) m.f2 = *p; if(c == '}'){ tab[i].f(&m); if(m.f1 || m.f2 != '.') chop(&m); if(m.f3 == 'r') reverse(&m); mputs(&m, m.mreg); m.mreg[0] = 0; break; } /* fallthrough */ default: return 0; } } mputc(&m, 0); return strdup(m.buf); }