comparison src/context.cpp @ 414:d5a1ed33d3ae

spf code now handles mx,exists,ptr tags, multiple A records, %{i} macro
author Carl Byington <carl@five-ten-sg.com>
date Tue, 25 Apr 2017 14:48:19 -0700
parents e63c6b4835ef
children 16451edcb962
comparison
equal deleted inserted replaced
413:54809ee70bb8 414:d5a1ed33d3ae
1136 char buf[maxlen]; 1136 char buf[maxlen];
1137 log(priv->queueid, "looking for %s txt record", from); 1137 log(priv->queueid, "looking for %s txt record", from);
1138 dns_interface(*priv, from, ns_t_txt, false, NULL, buf, maxlen); 1138 dns_interface(*priv, from, ns_t_txt, false, NULL, buf, maxlen);
1139 if (*buf) { 1139 if (*buf) {
1140 log(priv->queueid, "found txt record %s", buf); 1140 log(priv->queueid, "found txt record %s", buf);
1141 char *p = strchr(buf, ' '); // must start with 'v=spf1 ' 1141 // expand some macros here - a very restricted subset of all possible spf macros
1142 // only expand the first one.
1143 char *p = strstr(buf, "%{i}");
1144 if (p) {
1145 char repl[maxlen];
1146 char adr[sizeof "255.255.255.255 "];
1147 adr[0] = '\0';
1148 inet_ntop(AF_INET, (const u_char *)&priv->ip, adr, sizeof(adr));
1149 size_t bn = strlen(buf);
1150 size_t an = strlen(adr);
1151 if ((bn - 4 + an) < maxlen) {
1152 size_t n = p - buf; // leading part length
1153 strncpy(repl, buf, n); // leading part
1154 strcpy(repl+n, adr); // replacement
1155 strcpy(repl+n+an, buf+n+4); // trailing part
1156 strcpy(buf, repl);
1157 }
1158 log(priv->queueid, "have txt record %s", buf);
1159 }
1160 //
1161 p = strchr(buf, ' '); // must start with 'v=spf1 '
1142 if (!p) return false; // broken spf 1162 if (!p) return false; // broken spf
1143 char *e = p + strlen(p); // point to trailing null 1163 char *e = p + strlen(p); // point to trailing null
1144 while (true) { 1164 while (true) {
1145 while (*p == ' ') p++; 1165 while (*p == ' ') p++;
1146 if (p >= e) break; 1166 if (p >= e) break;
1147 char *b = strchr(p, ' '); 1167 char *b = strchr(p, ' ');
1148 if (b) *b = '\0'; 1168 if (b) *b = '\0';
1149 if ((*p != '-') && (*p != '~')) { 1169 if ((*p != '-') && (*p != '~') && (*p != '?')) {
1150 if (*p == '+') p++; 1170 if (*p == '+') p++;
1151 if (strncmp(p, "ip4:", 4) == 0) { 1171 if (strncmp(p, "ip4:", 4) == 0) {
1152 p += 4; 1172 p += 4;
1153 char *s = strchr(p, '/'); 1173 char *s = strchr(p, '/');
1154 if (s) *s = '\0'; 1174 if (s) *s = '\0';
1159 if ((mask >= 16) && (mask <= 32)) { 1179 if ((mask >= 16) && (mask <= 32)) {
1160 uint32_t low = (1 << (32-mask)) - 1; 1180 uint32_t low = (1 << (32-mask)) - 1;
1161 ipy &= low ^ 0xffffffff; 1181 ipy &= low ^ 0xffffffff;
1162 if ((ipy <= ip) && (ip <= ipy + low)) { 1182 if ((ipy <= ip) && (ip <= ipy + low)) {
1163 if (s) *s = '/'; 1183 if (s) *s = '/';
1164 log(priv->queueid, "match %s", p); 1184 log(priv->queueid, "match ip4:%s", p);
1165 return true; 1185 return true;
1166 } 1186 }
1167 } 1187 }
1168 } 1188 }
1169 } 1189 }
1170 else if (strncmp(p, "a:", 2) == 0) { 1190 else if (strncmp(p, "all", 3) == 0) {
1171 p += 2; 1191 // ignore it before looking for (a or a:) below
1172 uint32_t ipy = ntohl(dns_interface(*priv, p, ns_t_a)); 1192 }
1173 if (ipy == ip) { 1193 else if (strncmp(p, "exists:", 7) == 0) {
1174 log(priv->queueid, "match %s", p); 1194 p += 7;
1195 char buf[maxlen];
1196 uint32_t ipy = ntohl(dns_interface(*priv, p, ns_t_a, false, NULL, buf, maxlen));
1197 uint32_t *a = (uint32_t *)buf;
1198 if (a[0]) {
1199 log(priv->queueid, "match exists:%s", p);
1175 return true; 1200 return true;
1176 } 1201 }
1177 } 1202 }
1178 else if (strcmp(p, "a") == 0) { 1203 else if (strncmp(p, "mx", 2) == 0) {
1179 uint32_t ipy = ntohl(dns_interface(*priv, from, ns_t_a)); 1204 const char *name = (p[2] == ':') ? p+2 : from;
1180 if (ipy == ip) { 1205 char buf[maxlen];
1181 log(priv->queueid, "match %s", from); 1206 uint32_t c = ntohl(dns_interface(*priv, name, ns_t_mx, false, NULL, buf, maxlen));
1182 return true; 1207 char *b = buf;
1208 while (*b) {
1209 log(priv->queueid, "found mx %s", b);
1210 char abuf[maxlen];
1211 uint32_t ipy = ntohl(dns_interface(*priv, b, ns_t_a, false, NULL, buf, maxlen));
1212 uint32_t *a = (uint32_t *)buf;
1213 size_t c = a[0];
1214 for (size_t i=1; i++; i<=c) {
1215 ipy = ntohl(a[i]);
1216 if (ipy == ip) {
1217 log(priv->queueid, "match mx:%s", name);
1218 return true;
1219 }
1220 }
1221 b += strlen(b) + 1;
1222 }
1223 }
1224 else if (p[0] == 'a') {
1225 const char *name = (p[1] == ':') ? p+2 : from;
1226 char buf[maxlen];
1227 uint32_t ipy = ntohl(dns_interface(*priv, name, ns_t_a, false, NULL, buf, maxlen));
1228 uint32_t *a = (uint32_t *)buf;
1229 size_t c = a[0];
1230 for (size_t i=1; i++; i<=c) {
1231 ipy = ntohl(a[i]);
1232 if (ipy == ip) {
1233 log(priv->queueid, "match a:%s", name);
1234 return true;
1235 }
1236 }
1237 }
1238 else if (priv->client_dns_name && (!priv->client_dns_forged) && (strncmp(p, "ptr", 3) == 0)) {
1239 const char *name = (p[3] == ':') ? p+4 : from;
1240 size_t n = strlen(name);
1241 size_t d = strlen(priv->client_dns_name);
1242 if (d >= n) {
1243 if ((strncmp(priv->client_dns_name+d-n, name, n) == 0) && // trailing part matches
1244 ((d == n) || (priv->client_dns_name[d-n-1] == '.'))) { // same length, or dot just before match
1245 log(priv->queueid, "match ptr:%s", priv->client_dns_name);
1246 return true;
1247 }
1183 } 1248 }
1184 } 1249 }
1185 else if ((level < 5) && (strncmp(p, "redirect=", 9) == 0)) { 1250 else if ((level < 5) && (strncmp(p, "redirect=", 9) == 0)) {
1186 p += 9; 1251 p += 9;
1187 if (resolve_spf(p, ip, priv, level+1)) return true; 1252 if (resolve_spf(p, ip, priv, level+1)) return true;