libspf2
1.2.10
|
00001 /* 00002 * This program is free software; you can redistribute it and/or modify 00003 * it under the terms of either: 00004 * 00005 * a) The GNU Lesser General Public License as published by the Free 00006 * Software Foundation; either version 2.1, or (at your option) any 00007 * later version, 00008 * 00009 * OR 00010 * 00011 * b) The two-clause BSD license. 00012 * 00013 * These licenses can be found with the distribution in the file LICENSES 00014 */ 00015 00016 #include "spf_sys_config.h" 00017 00018 #ifdef STDC_HEADERS 00019 # include <stdio.h> /* stdin / stdout */ 00020 # include <stdlib.h> /* malloc / free */ 00021 #endif 00022 00023 00024 #ifdef HAVE_STRING_H 00025 # include <string.h> /* strstr / strdup */ 00026 #else 00027 # ifdef HAVE_STRINGS_H 00028 # include <strings.h> /* strstr / strdup */ 00029 # endif 00030 #endif 00031 00032 #ifdef HAVE_MEMORY_H 00033 #include <memory.h> 00034 #endif 00035 #if TIME_WITH_SYS_TIME 00036 # include <sys/time.h> 00037 # include <time.h> 00038 #else 00039 # if HAVE_SYS_TIME_H 00040 # include <sys/time.h> 00041 # else 00042 # include <time.h> 00043 # endif 00044 #endif 00045 #ifdef HAVE_NETDB_H 00046 # include <netdb.h> 00047 #endif 00048 #include <ctype.h> 00049 00050 00051 #include "spf.h" 00052 #include "spf_dns.h" 00053 #include "spf_internal.h" 00054 #include "spf_dns_internal.h" 00055 #include "spf_dns_zone.h" 00056 00057 00073 typedef struct 00074 { 00075 SPF_dns_rr_t **zone; 00076 int num_zone; /* This one really is an int. */ 00077 int zone_buf_len; /* This one really is an int. */ 00078 SPF_dns_rr_t *nxdomain; 00079 } SPF_dns_zone_config_t; 00080 00081 00082 00083 static inline SPF_dns_zone_config_t *SPF_voidp2spfhook( void *hook ) 00084 { return (SPF_dns_zone_config_t *)hook; } 00085 static inline void *SPF_spfhook2voidp( SPF_dns_zone_config_t *spfhook ) 00086 { return (void *)spfhook; } 00087 00088 00089 00090 00095 static SPF_dns_rr_t * 00096 SPF_dns_zone_find(SPF_dns_server_t *spf_dns_server, 00097 const char *domain, ns_type rr_type, 00098 int exact) 00099 { 00100 SPF_dns_zone_config_t *spfhook; 00101 int i; 00102 00103 spfhook = SPF_voidp2spfhook(spf_dns_server->hook); 00104 00105 if (spf_dns_server->debug) 00106 SPF_debugf("zone: Searching for RR %s (%d)", domain, rr_type); 00107 00108 /* If the record we want or are adding starts with '*.' then it must match 00109 * exactly. */ 00110 if (exact || strncmp(domain, "*.", 2) == 0) { 00111 for (i = 0; i < spfhook->num_zone; i++) { 00112 if (spfhook->zone[i]->rr_type == rr_type 00113 && strcasecmp(spfhook->zone[i]->domain, domain) == 0) 00114 return spfhook->zone[i]; 00115 } 00116 if (spf_dns_server->debug) 00117 SPF_debugf("%s", "zone: Exact not found"); 00118 } 00119 else { 00120 /* We are looking up a record, so lookup-matching semantics apply. */ 00121 size_t domain_len = strlen(domain); 00122 /* Real resolver would strip trailing '.', so we have to. 00123 * FIXME: doesn't handle wildcard cases - we don't use 00124 * those in test suite. */ 00125 if (domain_len && domain[domain_len - 1] == '.') 00126 --domain_len; 00127 00128 for (i = 0; i < spfhook->num_zone; i++) { 00129 if (spfhook->zone[i]->rr_type != rr_type 00130 && spfhook->zone[i]->rr_type != ns_t_any) { 00131 if (spf_dns_server->debug) 00132 SPF_debugf("zone: Ignoring record rrtype %d", 00133 spfhook->zone[i]->rr_type); 00134 continue; 00135 } 00136 00137 if (strncmp(spfhook->zone[i]->domain, "*.", 2) == 0) { 00138 size_t zdomain_len = strlen(spfhook->zone[i]->domain) - 2; 00139 if ((zdomain_len <= domain_len) 00140 && strncasecmp( 00141 spfhook->zone[i]->domain + 2, 00142 domain + (domain_len - zdomain_len), 00143 zdomain_len) == 0) 00144 return spfhook->zone[i]; 00145 } 00146 else if (strncasecmp( 00147 spfhook->zone[i]->domain, 00148 domain, 00149 domain_len) == 0 && 00150 strlen(spfhook->zone[i]->domain) == domain_len) { 00151 return spfhook->zone[i]; 00152 } 00153 } 00154 if (spf_dns_server->debug) 00155 SPF_debugf("%s", "zone: Non-exact not found"); 00156 } 00157 00158 return NULL; 00159 } 00160 00161 00162 00163 static SPF_dns_rr_t * 00164 SPF_dns_zone_lookup(SPF_dns_server_t *spf_dns_server, 00165 const char *domain, ns_type rr_type, int should_cache) 00166 { 00167 SPF_dns_zone_config_t *spfhook; 00168 SPF_dns_rr_t *spfrr; 00169 00170 spfrr = SPF_dns_zone_find(spf_dns_server, domain, rr_type, FALSE); 00171 if (spfrr) { 00172 SPF_dns_rr_dup(&spfrr, spfrr); 00173 return spfrr; 00174 } 00175 00176 if (spf_dns_server->layer_below) { 00177 return SPF_dns_lookup(spf_dns_server->layer_below, 00178 domain, rr_type, should_cache); 00179 } 00180 00181 spfhook = SPF_voidp2spfhook(spf_dns_server->hook); 00182 SPF_dns_rr_dup(&spfrr, spfhook->nxdomain); 00183 00184 return spfrr; 00185 } 00186 00187 00188 SPF_errcode_t 00189 SPF_dns_zone_add_str(SPF_dns_server_t *spf_dns_server, 00190 const char *domain, ns_type rr_type, 00191 SPF_dns_stat_t herrno, const char *data) 00192 { 00193 SPF_dns_zone_config_t *spfhook; 00194 SPF_dns_rr_t *spfrr; 00195 00196 int err; 00197 int cnt; 00198 00199 if (rr_type == ns_t_any) { 00200 if (data) 00201 SPF_error("RR type ANY can not have data."); 00202 if (herrno == NETDB_SUCCESS) 00203 SPF_error("RR type ANY must return a DNS error code."); 00204 } 00205 00206 spfhook = SPF_voidp2spfhook(spf_dns_server->hook); 00207 00208 /* try to find an existing record */ 00209 spfrr = SPF_dns_zone_find(spf_dns_server, domain, rr_type, TRUE); 00210 00211 /* create a new record */ 00212 if ( spfrr == NULL ) { 00213 /* First make sure we have space for it. */ 00214 if ( spfhook->num_zone == spfhook->zone_buf_len ) { 00215 int new_len; 00216 SPF_dns_rr_t **new_zone; 00217 int i; 00218 00219 new_len = spfhook->zone_buf_len 00220 + (spfhook->zone_buf_len >> 2) + 4; 00221 new_zone = realloc( spfhook->zone, 00222 new_len * sizeof( *new_zone ) ); 00223 if ( new_zone == NULL ) 00224 return SPF_E_NO_MEMORY; 00225 00226 for( i = spfhook->zone_buf_len; i < new_len; i++ ) 00227 new_zone[i] = NULL; 00228 00229 spfhook->zone_buf_len = new_len; 00230 spfhook->zone = new_zone; 00231 } 00232 00233 /* Now make the new record. */ 00234 spfrr = SPF_dns_rr_new_init(spf_dns_server, 00235 domain, rr_type, 24*60*60, herrno); 00236 if (spfrr == NULL) 00237 return SPF_E_NO_MEMORY; 00238 spfhook->zone[spfhook->num_zone] = spfrr; 00239 spfhook->num_zone++; 00240 00241 /* We succeeded with the add, but with no data. */ 00242 if (herrno != NETDB_SUCCESS) 00243 return SPF_E_SUCCESS; 00244 } 00245 00246 #define SPF_RR_TRY_REALLOC(rr, i, s) do { \ 00247 SPF_errcode_t __err = SPF_dns_rr_buf_realloc(rr, i, s); \ 00248 if (__err != SPF_E_SUCCESS) return __err; \ 00249 } while(0) 00250 00251 /* 00252 * initialize stuff 00253 */ 00254 cnt = spfrr->num_rr; 00255 00256 switch (rr_type) { 00257 case ns_t_a: 00258 SPF_RR_TRY_REALLOC(spfrr, cnt, sizeof( spfrr->rr[cnt]->a )); 00259 err = inet_pton( AF_INET, data, &spfrr->rr[cnt]->a ); 00260 if ( err <= 0 ) 00261 return SPF_E_INVALID_IP4; 00262 break; 00263 00264 case ns_t_aaaa: 00265 SPF_RR_TRY_REALLOC(spfrr, cnt, sizeof( spfrr->rr[cnt]->aaaa )); 00266 err = inet_pton( AF_INET6, data, &spfrr->rr[cnt]->aaaa ); 00267 if ( err <= 0 ) 00268 return SPF_E_INVALID_IP6; 00269 break; 00270 00271 case ns_t_mx: 00272 /* Caller passes priority<sp>domain. We don't use or 00273 * store priority, so discard it. */ 00274 while (isdigit(*data)) data++; 00275 while (isspace(*data)) data++; 00276 SPF_RR_TRY_REALLOC(spfrr, cnt, strlen( data ) + 1); 00277 strcpy( spfrr->rr[cnt]->mx, data ); 00278 break; 00279 00280 case ns_t_txt: 00281 case ns_t_spf: 00282 SPF_RR_TRY_REALLOC(spfrr, cnt, strlen( data ) + 1); 00283 strcpy( spfrr->rr[cnt]->txt, data ); 00284 break; 00285 00286 case ns_t_ptr: 00287 SPF_RR_TRY_REALLOC(spfrr, cnt, strlen( data ) + 1); 00288 strcpy( spfrr->rr[cnt]->ptr, data ); 00289 break; 00290 00291 case ns_t_any: 00292 if ( data ) 00293 SPF_error( "RR type ANY can not have data."); 00294 if ( herrno == NETDB_SUCCESS ) 00295 SPF_error( "RR type ANY must return a DNS error code."); 00296 SPF_error( "RR type ANY can not have multiple RR."); 00297 break; 00298 00299 default: 00300 SPF_error( "Invalid RR type" ); 00301 break; 00302 } 00303 00304 spfrr->num_rr = cnt + 1; 00305 00306 return SPF_E_SUCCESS; 00307 } 00308 00309 00310 00311 static void 00312 SPF_dns_zone_free(SPF_dns_server_t *spf_dns_server) 00313 { 00314 SPF_dns_zone_config_t *spfhook; 00315 int i; 00316 00317 SPF_ASSERT_NOTNULL(spf_dns_server); 00318 spfhook = SPF_voidp2spfhook(spf_dns_server->hook); 00319 00320 if (spfhook) { 00321 if (spfhook->zone) { 00322 for (i = 0; i < spfhook->zone_buf_len; i++) { 00323 if (spfhook->zone[i]) 00324 SPF_dns_rr_free(spfhook->zone[i]); 00325 } 00326 free(spfhook->zone); 00327 } 00328 if (spfhook->nxdomain) 00329 SPF_dns_rr_free(spfhook->nxdomain); 00330 free(spfhook); 00331 } 00332 00333 free(spf_dns_server); 00334 } 00335 00336 SPF_dns_server_t * 00337 SPF_dns_zone_new(SPF_dns_server_t *layer_below, 00338 const char *name, int debug) 00339 { 00340 SPF_dns_server_t *spf_dns_server; 00341 SPF_dns_zone_config_t *spfhook; 00342 00343 spf_dns_server = malloc(sizeof(SPF_dns_server_t)); 00344 if (spf_dns_server == NULL) 00345 return NULL; 00346 memset(spf_dns_server, 0, sizeof(SPF_dns_server_t)); 00347 00348 spf_dns_server->hook = malloc(sizeof(SPF_dns_zone_config_t)); 00349 if (spf_dns_server->hook == NULL) { 00350 free(spf_dns_server); 00351 return NULL; 00352 } 00353 memset(spf_dns_server->hook, 0, sizeof(SPF_dns_zone_config_t)); 00354 00355 if (name == NULL) 00356 name = "zone"; 00357 00358 spf_dns_server->destroy = SPF_dns_zone_free; 00359 spf_dns_server->lookup = SPF_dns_zone_lookup; 00360 spf_dns_server->get_spf = NULL; 00361 spf_dns_server->get_exp = NULL; 00362 spf_dns_server->add_cache = NULL; 00363 spf_dns_server->layer_below = layer_below; 00364 spf_dns_server->name = name; 00365 spf_dns_server->debug = debug; 00366 00367 spfhook = SPF_voidp2spfhook(spf_dns_server->hook); 00368 00369 spfhook->zone_buf_len = 32; 00370 spfhook->num_zone = 0; 00371 spfhook->zone = calloc(spfhook->zone_buf_len, sizeof(*spfhook->zone)); 00372 00373 if (spfhook->zone == NULL) { 00374 free(spfhook); 00375 free(spf_dns_server); 00376 return NULL; 00377 } 00378 00379 /* XXX This might have to return NO_DATA sometimes. */ 00380 spfhook->nxdomain = SPF_dns_rr_new_init(spf_dns_server, 00381 "", ns_t_any, 24 * 60 * 60, HOST_NOT_FOUND); 00382 if (spfhook->nxdomain == NULL) { 00383 free(spfhook->zone); 00384 free(spfhook); 00385 free(spf_dns_server); 00386 return NULL; 00387 } 00388 00389 return spf_dns_server; 00390 }