spf_dns_zone.c

Go to the documentation of this file.
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;
00077     int                           zone_buf_len;
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("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("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 }

Generated on Tue Nov 4 13:27:39 2008 for libspf2 by  doxygen 1.5.4