Back to index

courier  0.68.2
rfc1035mxlist.c
Go to the documentation of this file.
00001 /*
00002 ** Copyright 1998 - 2011 Double Precision, Inc.
00003 ** See COPYING for distribution information.
00004 */
00005 
00006 #include      "config.h"
00007 #include      <stdio.h>
00008 #include      <stdlib.h>
00009 #include      <string.h>
00010 #include      "rfc1035.h"
00011 #include      "rfc1035mxlist.h"
00012 
00013 void rfc1035_mxlist_free(struct rfc1035_mxlist *p)
00014 {
00015 struct rfc1035_mxlist *q;
00016 
00017        while (p)
00018        {
00019               q=p->next;
00020               if (p->hostname)     free(p->hostname);
00021               free(p);
00022               p=q;
00023        }
00024 }
00025 
00026 static int addrecord(struct rfc1035_mxlist **list, const char *mxname,
00027                    int mxpreference,
00028 
00029 #if    RFC1035_IPV6
00030                    struct in6_addr *in,
00031 #else
00032                    struct in_addr *in,
00033 #endif
00034                    int ad,
00035                    int port)
00036 {
00037 #if    RFC1035_IPV6
00038 struct sockaddr_in6 sin;
00039 #else
00040 struct sockaddr_in sin;
00041 #endif
00042 struct rfc1035_mxlist *p;
00043 
00044        if ((p=(struct rfc1035_mxlist *)malloc(sizeof(struct rfc1035_mxlist)))
00045               == 0 || (p->hostname=malloc(strlen(mxname)+1)) == 0)
00046        {
00047               if (p) free ( (char *)p);
00048               return (-1);
00049        }
00050 
00051        memset(&sin, 0, sizeof(sin));
00052 
00053 #if    RFC1035_IPV6
00054        sin.sin6_family=AF_INET6;
00055        sin.sin6_addr= *in;
00056        sin.sin6_port=htons(port);
00057        p->protocol=PF_INET6;
00058 #else
00059        sin.sin_family=AF_INET;
00060        sin.sin_addr= *in;
00061        sin.sin_port=htons(port);
00062        p->protocol=PF_INET;
00063 #endif
00064 
00065        while ( *list && (*list)->priority < mxpreference )
00066               list= &(*list)->next;
00067 
00068        p->next=*list;
00069        *list=p;
00070        p->ad=ad;
00071        p->priority=mxpreference;
00072        strcpy(p->hostname, mxname);
00073        memcpy(&p->address, &sin, sizeof(sin));
00074        return (0);
00075 }
00076 
00077 static int harvest_records(struct rfc1035_res *res,
00078        struct rfc1035_mxlist **list,
00079        struct rfc1035_reply *mxreply,
00080        int mxpreference,
00081        char *mxname,
00082        int q_type, int *found, int autoquery, int port);
00083 
00084 #define       HARVEST_AUTOQUERY    1
00085 #define       HARVEST_NODUPE              2
00086 
00087 static int add_arecords(struct rfc1035_res *res, struct rfc1035_mxlist **list,
00088        struct rfc1035_reply *mxreply,
00089        int mxpreference,
00090        char *mxname, int port)
00091 {
00092 #if    RFC1035_IPV6
00093 struct in6_addr      in;
00094 #else
00095 struct in_addr       in;
00096 #endif
00097 int    found=0;
00098 int    rc;
00099 
00100        if (rfc1035_aton(mxname, &in) == 0)
00101        {      /* Broken MX record */
00102        char   buf[RFC1035_NTOABUFSIZE];
00103 
00104               rfc1035_ntoa(&in, buf);
00105 
00106               if (addrecord(list, buf, mxpreference, &in, 0, port))
00107                      return (RFC1035_MX_INTERNAL);
00108 
00109               return (RFC1035_MX_OK);
00110        }
00111 
00112 #if    RFC1035_IPV6
00113 
00114 /*
00115        Here's the IPv6 strategy:
00116 
00117 If we have an existing MX record to work with, try to harvest
00118 both A and AAAA addresses from it.  If we find either an A or an AAAA
00119 record, stop.
00120 
00121 If we don't have an existing MX record, or we didn't find A or AAAA
00122 records, then query for A records.  Query for AAAA records only if A
00123 records weren't found.
00124 
00125 */
00126        if (mxreply)
00127        {
00128               if ((rc=harvest_records(res, list, mxreply, mxpreference,
00129                      mxname, RFC1035_TYPE_AAAA, &found, 0, port))
00130                             != RFC1035_MX_OK)
00131                      return (rc);
00132 
00133               if ((rc=harvest_records(res, list, mxreply, mxpreference,
00134                      mxname, RFC1035_TYPE_A, &found, HARVEST_NODUPE,
00135                             port))
00136                                           != RFC1035_MX_OK)
00137                      return (rc);
00138               if (found)    return (RFC1035_MX_OK);
00139        }
00140 
00141        if ((rc=harvest_records(res, list, mxreply, mxpreference, mxname,
00142               RFC1035_TYPE_A, &found, HARVEST_AUTOQUERY|HARVEST_NODUPE, port))
00143                                           != RFC1035_MX_OK)
00144               return (rc);
00145        if (found)    return (RFC1035_MX_OK);
00146 
00147        if ((rc=harvest_records(res, list, mxreply, mxpreference, mxname,
00148               RFC1035_TYPE_AAAA, &found, HARVEST_AUTOQUERY, port))
00149                      != RFC1035_MX_OK)
00150               return (rc);
00151 
00152 #else
00153        if ((rc=harvest_records(res, list, mxreply, mxpreference, mxname,
00154               RFC1035_TYPE_A, &found, HARVEST_AUTOQUERY, port))
00155                      != RFC1035_MX_OK)
00156               return (rc);
00157 #endif
00158 
00159        if (!found)   return (RFC1035_MX_HARDERR);
00160        return (RFC1035_MX_OK);
00161 }
00162 
00163 static int harvest_records(struct rfc1035_res *res,
00164        struct rfc1035_mxlist **list,
00165        struct rfc1035_reply *mxreply,
00166        int mxpreference,
00167        char *mxname,
00168        int q_type, int *found,
00169        int flags, int port)
00170 {
00171 struct rfc1035_reply *areply=0;
00172 int index;
00173 #if    RFC1035_IPV6
00174 struct in6_addr in;
00175 #else
00176 struct in_addr in;
00177 #endif
00178 
00179        index= -1;
00180 
00181        if (!mxreply || (
00182               ((index=rfc1035_replysearch_all( res, mxreply, mxname,
00183                                    q_type,
00184                                    RFC1035_CLASS_IN,
00185                                    0)) < 0 ||
00186                      mxreply->allrrs[index]->rrtype != q_type)
00187               && (flags & HARVEST_AUTOQUERY))
00188               )
00189        {
00190               index=rfc1035_resolve_cname(res, mxname,
00191                      q_type,
00192                      RFC1035_CLASS_IN, &areply, RFC1035_X_RANDOMIZE);
00193               if (index < 0)
00194               {
00195                      if (!areply)
00196                      {
00197                             if (index == RFC1035_ERR_CNAME_RECURSIVE)
00198                                    return (RFC1035_MX_BADDNS);
00199                             return (RFC1035_MX_INTERNAL);
00200                      }
00201 
00202                      if (areply->rcode == RFC1035_RCODE_NXDOMAIN ||
00203                             areply->rcode == RFC1035_RCODE_NOERROR)
00204                      {
00205                             rfc1035_replyfree(areply);
00206                             return (RFC1035_MX_OK);
00207                      }
00208                      rfc1035_replyfree(areply);
00209                      return (RFC1035_MX_SOFTERR);
00210               }
00211               mxreply=areply;
00212        }
00213 
00214        for ( ; index >= 0 ;
00215                      index=rfc1035_replysearch_all( res, mxreply, mxname,
00216                                    q_type,
00217                                    RFC1035_CLASS_IN,
00218                                    index+1))
00219        {
00220               if (mxreply->allrrs[index]->rrtype != q_type)
00221                      continue;
00222 
00223 #if RFC1035_IPV6
00224               if (q_type == RFC1035_TYPE_A)
00225               {
00226               struct rfc1035_mxlist *q;
00227 
00228                      /* Map it to an IPv4 address */
00229 
00230                      rfc1035_ipv4to6(&in,
00231                             &mxreply->allrrs[index]->rr.inaddr);
00232 
00233                      /* See if it's already here */
00234 
00235                      for (q= *list; q; q=q->next)
00236                      {
00237                      struct sockaddr_in6 sin6;
00238 
00239                             if (q->protocol != PF_INET6)
00240                                    continue;
00241                             memcpy(&sin6, &q->address, sizeof(sin6));
00242 
00243                             if (memcmp(&sin6.sin6_addr, &in, sizeof(in))
00244                                    == 0 && q->priority == mxpreference)
00245                                    break;
00246                      }
00247                      if ((flags & HARVEST_NODUPE) && q) continue;
00248               }
00249               else
00250                      in=mxreply->allrrs[index]->rr.in6addr;
00251 #else
00252               in.s_addr=mxreply->allrrs[index]->rr.inaddr.s_addr;
00253 #endif
00254               *found=1;
00255               if (addrecord(list, mxname, mxpreference, &in, mxreply->ad,
00256                            port))
00257               {
00258                      if (areply)
00259                             rfc1035_replyfree(areply);
00260                      return (RFC1035_MX_INTERNAL);
00261               }
00262        }
00263        if (areply)
00264               rfc1035_replyfree(areply);
00265 
00266        return (RFC1035_MX_OK);
00267 }
00268 
00269 static int domxlistcreate(struct rfc1035_res *res,
00270                        const char *q_name, int opts,
00271                        struct rfc1035_mxlist **list, int port)
00272 {
00273 char   namebuf[RFC1035_MAXNAMESIZE+1];
00274 struct rfc1035_reply *replyp;
00275 int    index;
00276 RFC1035_ADDR  in;
00277 int seen_softerr=0;
00278 int seen_good=0;
00279 
00280        *list=0;
00281        if (rfc1035_aton(q_name, &in) == 0)
00282               return (RFC1035_MX_HARDERR);       /* Don't gimme an IP address */
00283 
00284        namebuf[0]=0;
00285        strncat(namebuf, q_name, RFC1035_MAXNAMESIZE);
00286        if (namebuf[0] == '[')
00287        {
00288        char   *q=strchr(namebuf, ']');
00289 
00290               if (!q || q[1])      return (RFC1035_MX_HARDERR);       /* Bad addr */
00291               *q=0;
00292               if (rfc1035_aton(namebuf+1, &in))
00293                      return (RFC1035_MX_HARDERR);
00294 
00295               if (addrecord(list, q_name, -1, &in, 0, port))
00296                      return (RFC1035_MX_INTERNAL);
00297               return (RFC1035_MX_OK);
00298        }
00299 
00300        index=rfc1035_resolve_cname(res, namebuf,
00301               RFC1035_TYPE_MX,
00302               RFC1035_CLASS_IN, &replyp, RFC1035_X_RANDOMIZE);
00303 
00304        if (index < 0)
00305        {
00306               if (!replyp)
00307               {
00308                      if (index == RFC1035_ERR_CNAME_RECURSIVE)
00309                             return (RFC1035_MX_BADDNS);
00310                      return (RFC1035_MX_INTERNAL);
00311               }
00312 
00313               if (replyp->rcode == RFC1035_RCODE_NXDOMAIN ||
00314                      replyp->rcode == RFC1035_RCODE_NOERROR)
00315               {
00316                      rfc1035_replyfree(replyp);
00317                      strcpy(namebuf, q_name);
00318 
00319                      if (opts & RFC1035_MX_AFALLBACK)
00320                             return (add_arecords(res, list, 0, -1,
00321                                                namebuf, port));
00322                      return RFC1035_MX_HARDERR;
00323               }
00324 
00325               rfc1035_replyfree(replyp);
00326               return (RFC1035_MX_SOFTERR);
00327        }
00328 
00329        for ( ; index >= 0;
00330                      index=rfc1035_replysearch_all( res, replyp, namebuf,
00331                                    RFC1035_TYPE_MX,
00332                                    RFC1035_CLASS_IN,
00333                                    index+1))
00334        {
00335        char   mxname[RFC1035_MAXNAMESIZE+1];
00336 
00337               if (replyp->allrrs[index]->rrtype != RFC1035_TYPE_MX)
00338                      continue;
00339 
00340               if (rfc1035_replyhostname(replyp,
00341                      replyp->allrrs[index]->rr.mx.mx_label, mxname) == 0)
00342                      continue;
00343 
00344               switch (add_arecords(res, list, replyp,
00345                      replyp->allrrs[index]->rr.mx.preference, mxname,
00346                      port)) {
00347               case   RFC1035_MX_SOFTERR:
00348                      seen_softerr=1;
00349                      continue;
00350               case   RFC1035_MX_INTERNAL:
00351                      rfc1035_replyfree(replyp);
00352                      return (RFC1035_MX_INTERNAL);
00353               case   RFC1035_MX_BADDNS:
00354                      rfc1035_replyfree(replyp);
00355                      return (RFC1035_MX_BADDNS);
00356               default:
00357                      seen_good=1;
00358                      continue;
00359               }
00360        }
00361 
00362        rfc1035_replyfree(replyp);
00363 
00364        if (seen_good && (opts & RFC1035_MX_IGNORESOFTERR))
00365               seen_softerr=0;
00366               /* At least some A records were probably fetched */
00367 
00368        if (seen_softerr)
00369               return (RFC1035_MX_SOFTERR);
00370 
00371        if (*list)    return (RFC1035_MX_OK);
00372        return (RFC1035_MX_HARDERR);
00373 }
00374 
00375 static int domxlistcreate2(struct rfc1035_res *res,
00376                         const char *q_name,
00377                         int opts,
00378                         struct rfc1035_mxlist **list,
00379                         int port)
00380 {
00381 char   *buf;
00382 int    rc;
00383 
00384        if (strchr(q_name, '.') || strchr(q_name, ':') ||
00385               !res->rfc1035_defaultdomain)
00386               return (domxlistcreate(res, q_name, opts, list, port));
00387 
00388        if ((buf=malloc(strlen(q_name)+
00389               strlen(res->rfc1035_defaultdomain)+2)) == 0)
00390               return (-1);
00391 
00392        strcat(strcat(strcpy(buf, q_name), "."), res->rfc1035_defaultdomain);
00393 
00394        rc=domxlistcreate(res, buf, opts, list, port);
00395 
00396        free(buf);
00397        return (rc);
00398 }
00399 
00400 static int domxlistcreate3(struct rfc1035_res *res,
00401                         const char *q_name,
00402                         int opts,
00403                         struct rfc1035_mxlist **list)
00404 {
00405 char   *buf;
00406 int    rc;
00407 const char *p;
00408 
00409        p=strchr(q_name, ',');
00410 
00411        if (p == 0)   return (domxlistcreate2(res, q_name, opts, list, 25));
00412 
00413        if ((buf=malloc(p-q_name+1)) == 0)
00414               return (-1);
00415 
00416        memcpy(buf, q_name, p-q_name);
00417        buf[p-q_name]=0;
00418 
00419        rc=domxlistcreate2(res, buf, opts, list, atoi(p+1));
00420 
00421        free(buf);
00422        return (rc);
00423 }
00424 
00425 int rfc1035_mxlist_create_x(struct rfc1035_res *res,
00426                          const char *q_name,
00427                          int opts,
00428                          struct rfc1035_mxlist **list)
00429 {
00430 int    rc=domxlistcreate3(res, q_name, opts, list);
00431 
00432        if (rc != RFC1035_MX_OK)
00433        {
00434               rfc1035_mxlist_free(*list);
00435               *list=0;
00436        }
00437        return (rc);
00438 }
00439 
00440 int rfc1035_mxlist_create(struct rfc1035_res *res,
00441                        const char *q_name,
00442                        struct rfc1035_mxlist **list)
00443 {
00444        return rfc1035_mxlist_create_x(res, q_name,
00445                                    RFC1035_MX_AFALLBACK |
00446                                    RFC1035_MX_IGNORESOFTERR,
00447                                    list);
00448 }