]> git.ipfire.org Git - people/ms/dnsmasq.git/blobdiff - src/option.c
import of dnsmasq-2.21.tar.gz
[people/ms/dnsmasq.git] / src / option.c
index 4844477cef541c44388c95d91d7a429061dfbffc..4d54f4db00a009a684253f6e93fdba737495c09b 100644 (file)
@@ -21,7 +21,7 @@ struct myoption {
   int val;
 };
 
-#define OPTSTRING "yZDNLERKzowefnbvhdkqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:X:V:U:j:P:J:W:"
+#define OPTSTRING "yZDNLERKzowefnbvhdkqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:X:V:U:j:P:J:W:Y:"
 
 static struct myoption opts[] = { 
   {"version", 0, 0, 'v'},
@@ -78,6 +78,7 @@ static struct myoption opts[] = {
   {"dhcp-authoritative", 0, 0, 'K'},
   {"srv-host", 1, 0, 'W'},
   {"localise-queries", 0, 0, 'y'},
+  {"txt-record", 1, 0, 'Y'},
   {0, 0, 0, 0}
 };
 
@@ -161,15 +162,30 @@ static char *usage =
 "-U, --dhcp-vendorclass=<id>,<class> Map DHCP vendor class to option set.\n"
 "-v, --version                       Display dnsmasq version and copyright information.\n"
 "-V, --alias=addr,addr,mask          Translate IPv4 addresses from upstream servers.\n"
-"-W, --srv-host=name,port,pri,weight Specify a SRV record.\n"
+"-W, --srv-host=name,target,...      Specify a SRV record.\n"
 "-w, --help                          Display this message.\n"
 "-x, --pid-file=path                 Specify path of PID file. (defaults to " RUNFILE ").\n"
 "-X, --dhcp-lease-max=number         Specify maximum number of DHCP leases (defaults to %d).\n"
-"-y, --localise-queries              Answer DNS queries based on the interface a query was sent to."
+"-y, --localise-queries              Answer DNS queries based on the interface a query was sent to.\n"
+"-Y  --txt-record=name,txt....       Specify TXT DNS record.\n"
 "-z, --bind-interfaces               Bind only to interfaces in use.\n"
 "-Z, --read-ethers                   Read DHCP static host information from " ETHERSFILE ".\n"
 "\n";
 
+static void add_txt(struct daemon *daemon, char *name, char *txt)
+{
+  size_t len = strlen(txt);
+  struct txt_record *r = safe_malloc(sizeof(struct txt_record));
+  
+  r->name = safe_string_alloc(name);
+  r->next = daemon->txt;
+  daemon->txt = r;
+  r->class = C_CHAOS;
+  r->txt = safe_malloc(len+1);
+  r->len = len+1;
+  *(r->txt) = len;
+  memcpy((r->txt)+1, txt, len);
+}
 
 struct daemon *read_opts (int argc, char **argv)
 {
@@ -177,7 +193,7 @@ struct daemon *read_opts (int argc, char **argv)
   char *problem = NULL, *buff = safe_malloc(MAXDNAME);
   int option = 0, i;
   FILE *file_save = NULL, *f = NULL;
-  char *comma, *file_name_save = NULL, *conffile = CONFFILE;
+  char *p, *comma, *file_name_save = NULL, *conffile = CONFFILE;
   int hosts_index = 1, conffile_set = 0;
   int line_save = 0, lineno = 0;
   opterr = 0;
@@ -197,17 +213,27 @@ struct daemon *read_opts (int argc, char **argv)
   daemon->runfile =  RUNFILE;
   daemon->dhcp_max = MAXLEASES;
   daemon->edns_pktsz = EDNS_PKTSZ;
+  add_txt(daemon, "version.bind", "dnsmasq-" VERSION );
+  add_txt(daemon, "authors.bind", "Simon Kelley");
+  add_txt(daemon, "copyright.bind", COPYRIGHT);
+
   
   while (1)
     {
       problem = NULL;
 
       if (!f)
+       {
 #ifdef HAVE_GETOPT_LONG
-       option = getopt_long(argc, argv, OPTSTRING, (struct option *)opts, NULL);
+         option = getopt_long(argc, argv, OPTSTRING, (struct option *)opts, NULL);
 #else
-        option = getopt(argc, argv, OPTSTRING);
+         option = getopt(argc, argv, OPTSTRING);
 #endif
+         if (optarg)
+           for (p = optarg; *p; p++)
+             if (*p == ',')
+               *p = '\001';
+       }
       else
        { /* f non-NULL, reading from conffile. */
        reread:
@@ -228,18 +254,43 @@ struct daemon *read_opts (int argc, char **argv)
            }
          else
            {
-             char *p;
              int white;
              lineno++;
-             /* dump comments */
+             
+             /* Implement quotes, inside quotes we allow \\ \" \n and \t 
+                unquoted commas get changed to \001 also strip comments */
+             
              for (white = 1, p = buff; *p; p++)
-               if (white && *p == '#')
-                 { 
-                   *p = 0;
-                   break;
-                 }
-               else
-                 white = isspace(*p);
+               {
+                 if (*p == '"')
+                   {
+                     memmove(p, p+1, strlen(p+1)+1);
+                     for(; *p && *p != '"'; p++)
+                       if (*p == '\\' && 
+                           (p[1] == '\\' || p[1] == '"' || p[1] == 'n' || p[1] == 't'))
+                         {
+                           if (p[1] == 't')
+                             p[1] = '\t';
+                           else if (p[1] == 'n')
+                             p[1] = '\n';
+                           memmove(p, p+1, strlen(p+1)+1);
+                         }
+                     if (*p == '"') 
+                       memmove(p, p+1, strlen(p+1)+1);
+                     else
+                       complain("missing \"", lineno, conffile);
+                   }
+
+                 if (white && *p == '#')
+                   { 
+                     *p = 0;
+                     break;
+                   }
+                 white = isspace(*p); 
+                 if (*p == ',')
+                   *p = '\001';
+
+               }
              /* fgets gets end of line char too. */
              while (strlen(buff) > 0 && isspace(buff[strlen(buff)-1]))
                buff[strlen(buff)-1] = 0;
@@ -259,8 +310,7 @@ struct daemon *read_opts (int argc, char **argv)
                  option = opts[i].val;
              if (!option)
                {
-                 sprintf(buff, "bad option at line %d of %s ", lineno, conffile);
-                 complain(buff, NULL);
+                 complain("bad option", lineno, conffile);
                  continue;
                }
            }
@@ -283,16 +333,16 @@ struct daemon *read_opts (int argc, char **argv)
      
       if (!f && option == 'w')
        {
-         fprintf (stderr, usage,  CACHESIZ, EDNS_PKTSZ, MAXLEASES);
+         printf (usage,  CACHESIZ, EDNS_PKTSZ, MAXLEASES);
          exit(0);
        }
 
       if (!f && option == 'v')
         {
-          fprintf(stderr, "Dnsmasq version %s  %s\n\n", VERSION, COPYRIGHT);
-         fprintf(stderr, "This software comes with ABSOLUTELY NO WARRANTY.\n");
-         fprintf(stderr, "Dnsmasq is free software, and you are welcome to redistribute it\n");
-         fprintf(stderr, "under the terms of the GNU General Public License, version 2.\n");
+          printf("Dnsmasq version %s  %s\n\n", VERSION, COPYRIGHT);
+         printf("This software comes with ABSOLUTELY NO WARRANTY.\n");
+         printf("Dnsmasq is free software, and you are welcome to redistribute it\n");
+         printf("under the terms of the GNU General Public License, version 2.\n");
           exit(0);
         }
       
@@ -302,10 +352,7 @@ struct daemon *read_opts (int argc, char **argv)
            daemon->options |= optmap[i].flag;
            option = 0;
            if (f && optarg)
-             {
-               sprintf(buff, "extraneous parameter at line %d of %s ", lineno, conffile);
-               complain(buff, NULL);
-             }
+             complain("extraneous parameter", lineno, conffile);
            break;
          }
       
@@ -313,8 +360,7 @@ struct daemon *read_opts (int argc, char **argv)
        {
          if (f && !optarg)
            {
-             sprintf(buff, "missing parameter at line %d of %s ", lineno, conffile);
-             complain(buff, NULL);
+             complain("missing parameter", lineno, conffile);
              continue;
            }
                  
@@ -331,8 +377,7 @@ struct daemon *read_opts (int argc, char **argv)
               /* nest conffiles one deep */
               if (file_save)
                 {
-                  sprintf(buff, "nested includes not allowed at line %d of %s ", lineno, conffile);
-                  complain(buff, NULL);
+                  complain("nested includes not allowed", lineno, conffile);
                   continue;
                 }
               file_name_save = conffile;
@@ -378,13 +423,13 @@ struct daemon *read_opts (int argc, char **argv)
            case 'm':
              {
                int pref = 1;
-               struct mx_record *new;
+               struct mx_srv_record *new;
 
-               if ((comma = strchr(optarg, ',')))
+               if ((comma = strchr(optarg, '\001')))
                  {
                    char *prefstr;
                    *(comma++) = 0;
-                   if ((prefstr=strchr(comma, ',')))
+                   if ((prefstr=strchr(comma, '\001')))
                      {
                        *(prefstr++) = 0;
                        if (!atoi_check(prefstr, &pref))
@@ -403,12 +448,13 @@ struct daemon *read_opts (int argc, char **argv)
                    break;
                  }
 
-               new = safe_malloc(sizeof(struct mx_record));
+               new = safe_malloc(sizeof(struct mx_srv_record));
                new->next = daemon->mxnames;
                daemon->mxnames = new;
-               new->mxname = safe_string_alloc(optarg);
-               new->mxtarget = safe_string_alloc(comma); /* may be NULL */
-               new->preference = pref;
+               new->issrv = 0;
+               new->name = safe_string_alloc(optarg);
+               new->target = safe_string_alloc(comma); /* may be NULL */
+               new->weight = pref;
                break;
              }
 
@@ -456,7 +502,7 @@ struct daemon *read_opts (int argc, char **argv)
            case 'i':
              do {
                struct iname *new = safe_malloc(sizeof(struct iname));
-               if ((comma = strchr(optarg, ',')))
+               if ((comma = strchr(optarg, '\001')))
                  *comma++ = 0;
                new->next = daemon->if_names;
                daemon->if_names = new;
@@ -473,7 +519,7 @@ struct daemon *read_opts (int argc, char **argv)
            case 'I':
              do {
                struct iname *new = safe_malloc(sizeof(struct iname));
-               if ((comma = strchr(optarg, ',')))
+               if ((comma = strchr(optarg, '\001')))
                  *comma++ = 0;
                new->next = daemon->if_except;
                daemon->if_except = new;
@@ -502,7 +548,7 @@ struct daemon *read_opts (int argc, char **argv)
            case 'a':
              do {
                struct iname *new = safe_malloc(sizeof(struct iname));
-               if ((comma = strchr(optarg, ',')))
+               if ((comma = strchr(optarg, '\001')))
                  *comma++ = 0;
                new->next = daemon->if_addrs;
 #ifdef HAVE_IPV6
@@ -769,7 +815,7 @@ struct daemon *read_opts (int argc, char **argv)
                new->broadcast.s_addr = 0;
                new->router.s_addr = 0;
                new->netid.net = NULL;
-               new->static_only = new->filter_netid = 0;
+               new->flags = 0;
                
                problem = "bad dhcp-range";
 
@@ -777,14 +823,14 @@ struct daemon *read_opts (int argc, char **argv)
                  if (!(*cp == ' ' || *cp == '.' ||  (*cp >='0' && *cp <= '9')))
                    break;
 
-               if (*cp != ',' && (comma = strchr(optarg, ',')))
+               if (*cp != '\001' && (comma = strchr(optarg, '\001')))
                  {
                    *comma = 0;
                    if (strstr(optarg, "net:") == optarg)
                      {
                        new->netid.net = safe_string_alloc(optarg+4);
                        new->netid.next = NULL;
-                       new->filter_netid = 1;
+                       new->flags |= CONTEXT_FILTER;
                      }
                    else
                      new->netid.net = safe_string_alloc(optarg);
@@ -796,7 +842,7 @@ struct daemon *read_opts (int argc, char **argv)
                
                for (k = 1; k < 5; k++)
                  {
-                   if (!(a[k] = strchr(a[k-1], ',')))
+                   if (!(a[k] = strchr(a[k-1], '\001')))
                      break;
                    *(a[k]++) = 0;
                  }
@@ -806,7 +852,7 @@ struct daemon *read_opts (int argc, char **argv)
                else if (strcmp(a[1], "static") == 0)
                  {
                    new->end = new->start;
-                   new->static_only = 1;
+                   new->flags |= CONTEXT_STATIC;
                  }
                else if ((new->end.s_addr = inet_addr(a[1])) == (in_addr_t)-1)
                  option = '?';
@@ -821,6 +867,7 @@ struct daemon *read_opts (int argc, char **argv)
                if (option != '?' && k >= 3 && strchr(a[2], '.') &&  
                    ((new->netmask.s_addr = inet_addr(a[2])) != (in_addr_t)-1))
                  {
+                   new->flags |= CONTEXT_NETMASK;
                    leasepos = 3;
                    if (!is_same_net(new->start, new->end, new->netmask))
                      {
@@ -839,7 +886,10 @@ struct daemon *read_opts (int argc, char **argv)
 
                if (k >= 4 && strchr(a[3], '.') &&  
                    ((new->broadcast.s_addr = inet_addr(a[3])) != (in_addr_t)-1))
-                 leasepos = 4;
+                 {
+                   new->flags |= CONTEXT_BRDCAST;
+                   leasepos = 4;
+                 }
 
                if (k >= leasepos+1)
                  {
@@ -866,6 +916,10 @@ struct daemon *read_opts (int argc, char **argv)
                              }
                            
                            new->lease_time = atoi(a[leasepos]) * fac;
+                           /* Leases of a minute or less confuse
+                              some clients, notably Apple's */
+                           if (new->lease_time < 120)
+                             new->lease_time = 120;
                          }
                      }
                  }
@@ -879,7 +933,6 @@ struct daemon *read_opts (int argc, char **argv)
              {
                int j, k;
                char *a[6] = { NULL, NULL, NULL, NULL, NULL, NULL };
-               unsigned int e0, e1, e2, e3, e4, e5;
                struct dhcp_config *new = safe_malloc(sizeof(struct dhcp_config));
                struct in_addr in;
 
@@ -890,7 +943,7 @@ struct daemon *read_opts (int argc, char **argv)
                a[0] = optarg;
                for (k = 1; k < 6; k++)
                  {
-                   if (!(a[k] = strchr(a[k-1], ',')))
+                   if (!(a[k] = strchr(a[k-1], '\001')))
                      break;
                    *(a[k]++) = 0;
                  }
@@ -899,6 +952,7 @@ struct daemon *read_opts (int argc, char **argv)
                  if (strchr(a[j], ':')) /* ethernet address, netid or binary CLID */
                    {
                      char *arg = a[j];
+                     
                      if ((arg[0] == 'i' || arg[0] == 'I') &&
                          (arg[1] == 'd' || arg[1] == 'D') &&
                          arg[2] == ':')
@@ -910,32 +964,9 @@ struct daemon *read_opts (int argc, char **argv)
                              int len;
                              arg += 3; /* dump id: */
                              if (strchr(arg, ':'))
-                               {
-                                 /* decode hex in place */
-                                 char *p = arg, *q = arg, *r;
-                                 while (*p)
-                                   {
-                                     for (r = p; *r && *r != ':'; r++);
-                                     if (*r)
-                                       {
-                                         if (r != p)
-                                           {
-                                             *r = 0;
-                                             *(q++) = strtol(p, NULL, 16);
-                                           }
-                                         p = r+1;
-                                       }
-                                     else
-                                       {
-                                         if (*p)
-                                           *(q++) = strtol(p, NULL, 16);
-                                         break;
-                                       }
-                                   }
-                                 len = q - arg;
-                               }
+                               len = parse_hex(arg, arg, -1, NULL);
                              else
-                               len = strlen(arg);
+                               len = (int) strlen(arg);
                              
                              new->flags |= CONFIG_CLID;
                              new->clid_len = len;
@@ -948,17 +979,8 @@ struct daemon *read_opts (int argc, char **argv)
                          new->flags |= CONFIG_NETID;
                          new->netid.net = safe_string_alloc(arg+4);
                        }
-                     else if (sscanf(a[j], "%x:%x:%x:%x:%x:%x",
-                                     &e0, &e1, &e2, &e3, &e4, &e5) == 6)
-                       {
+                     else if (parse_hex(a[j],  new->hwaddr, 6, &new->wildcard_mask) == 6)
                          new->flags |= CONFIG_HWADDR;
-                         new->hwaddr[0] = e0;
-                         new->hwaddr[1] = e1;
-                         new->hwaddr[2] = e2;
-                         new->hwaddr[3] = e3;
-                         new->hwaddr[4] = e4;
-                         new->hwaddr[5] = e5;
-                       }
                      else
                        option = '?';
                    }
@@ -1016,6 +1038,10 @@ struct daemon *read_opts (int argc, char **argv)
                      else
                        {
                          new->lease_time = atoi(a[j]) * fac; 
+                         /* Leases of a minute or less confuse
+                            some clients, notably Apple's */
+                         if (new->lease_time < 120)
+                           new->lease_time = 120;
                          new->flags |= CONFIG_TIME;
                        }
                    }
@@ -1052,7 +1078,7 @@ struct daemon *read_opts (int argc, char **argv)
                new->netid = NULL;
                new->val = NULL;
                                
-               if ((comma = strchr(optarg, ',')))
+               if ((comma = strchr(optarg, '\001')))
                  {
                    struct dhcp_netid *np = NULL;
                    *comma++ = 0;
@@ -1069,7 +1095,7 @@ struct daemon *read_opts (int argc, char **argv)
                      new->netid->next = np;
                      np = new->netid;
                      optarg = comma;
-                     if ((comma = strchr(optarg, ',')))
+                     if ((comma = strchr(optarg, '\001')))
                        *comma++ = 0;
                    } while (optarg);
                  }
@@ -1084,10 +1110,10 @@ struct daemon *read_opts (int argc, char **argv)
                    /* dns search, RFC 3397 */
                    unsigned char *q, *r, *tail;
                    unsigned char *p = NULL;
-                   int newlen, len = 0;
+                   size_t newlen, len = 0;
                    
                    optarg = comma;
-                   if ((comma = strchr(optarg, ',')))
+                   if ((comma = strchr(optarg, '\001')))
                      *(comma++) = 0;
 
                    while (optarg && *optarg)
@@ -1099,10 +1125,8 @@ struct daemon *read_opts (int argc, char **argv)
                            break;
                          }
                        
-                       if (!(r = realloc(p, len + strlen(optarg) + 2)))
+                       if (!(p = realloc(p, len + strlen(optarg) + 2)))
                          die("could not get memory", NULL);
-                       p = memmove(r, p, len);
-                       
                        q = p + len;
                        
                        /* add string on the end in RFC1035 format */
@@ -1121,7 +1145,7 @@ struct daemon *read_opts (int argc, char **argv)
                        /* Now tail-compress using earlier names. */
                        newlen = q - p;
                        for (tail = p + len; *tail; tail += (*tail) + 1)
-                         for (r = p; r - p < len; r += (*r) + 1)
+                         for (r = p; r - p < (int)len; r += (*r) + 1)
                            if (strcmp(r, tail) == 0)
                              {
                                PUTSHORT((r - p) | 0xc000, tail); 
@@ -1132,11 +1156,11 @@ struct daemon *read_opts (int argc, char **argv)
                        len = newlen;
                    
                        optarg = comma;
-                       if (optarg && (comma = strchr(optarg, ',')))
+                       if (optarg && (comma = strchr(optarg, '\001')))
                          *(comma++) = 0;
                      }
 
-                   new->len = len;
+                   new->len = (int) len;
                    new->val = p;
                  }
                else if (comma)
@@ -1146,7 +1170,7 @@ struct daemon *read_opts (int argc, char **argv)
                    is_addr = is_hex = is_dec = 1;
                    addrs = digs = 1;
                    for (cp = comma; *cp; cp++)
-                     if (*cp == ',')
+                     if (*cp == '\001')
                        {
                          addrs++;
                          is_dec = is_hex = 0;
@@ -1168,28 +1192,9 @@ struct daemon *read_opts (int argc, char **argv)
                
                    if (is_hex && digs > 1)
                      {
-                       char *p = comma, *q, *r;
                        new->len = digs;
-                       q = new->val = safe_malloc(new->len);
-                       while (*p)
-                         {
-                           for (r = p; *r && *r != ':'; r++);
-                           if (*r)
-                             {
-                               if (r != p)
-                                 {
-                                   *r = 0;
-                                   *(q++) = strtol(p, NULL, 16);
-                                 }
-                               p = r+1;
-                             }
-                           else
-                             {
-                               if (*p)
-                                 *(q++) = strtol(p, NULL, 16);
-                               break;
-                             }
-                         }
+                       new->val = safe_malloc(new->len);
+                       parse_hex(comma, new->val, digs, NULL);
                      }
                    else if (is_dec)
                      {
@@ -1222,7 +1227,7 @@ struct daemon *read_opts (int argc, char **argv)
                        while (addrs--) 
                          {
                            cp = comma;
-                           if ((comma = strchr(cp, ',')))
+                           if ((comma = strchr(cp, '\001')))
                              *comma++ = 0;
                            in.s_addr = inet_addr(cp);
                            memcpy(op, &in, INADDRSZ);
@@ -1238,7 +1243,7 @@ struct daemon *read_opts (int argc, char **argv)
                      }
                  }
 
-               if (new->len > 256)
+               if (new->len > 255)
                  {
                    option = '?';
                    problem = "dhcp-option too long";
@@ -1266,7 +1271,7 @@ struct daemon *read_opts (int argc, char **argv)
                    struct dhcp_netid *newid = safe_malloc(sizeof(struct dhcp_netid));
                    newid->next = id;
                    id = newid;
-                   if ((comma = strchr(optarg, ',')))
+                   if ((comma = strchr(optarg, '\001')))
                      *comma++ = 0;
                    newid->net = safe_string_alloc(optarg+4);
                    optarg = comma;
@@ -1278,14 +1283,14 @@ struct daemon *read_opts (int argc, char **argv)
                  {
                    char *dhcp_file, *dhcp_sname = NULL;
                    struct in_addr dhcp_next_server;
-                   if ((comma = strchr(optarg, ',')))
+                   if ((comma = strchr(optarg, '\001')))
                      *comma++ = 0;
                    dhcp_file = safe_string_alloc(optarg);
                    dhcp_next_server.s_addr = 0;
                    if (comma)
                      {
                        optarg = comma;
-                       if ((comma = strchr(optarg, ',')))
+                       if ((comma = strchr(optarg, '\001')))
                          *comma++ = 0;
                        dhcp_sname = safe_string_alloc(optarg);
                        if (comma && (dhcp_next_server.s_addr = inet_addr(comma)) == (in_addr_t)-1)
@@ -1318,7 +1323,7 @@ struct daemon *read_opts (int argc, char **argv)
            case 'U':
            case 'j':
              {
-               if (!(comma = strchr(optarg, ',')))
+               if (!(comma = strchr(optarg, '\001')))
                  option = '?';
                else
                  {
@@ -1343,7 +1348,7 @@ struct daemon *read_opts (int argc, char **argv)
                daemon->dhcp_ignore = new;
                do {
                  struct dhcp_netid *member = safe_malloc(sizeof(struct dhcp_netid));
-                 if ((comma = strchr(optarg, ',')))
+                 if ((comma = strchr(optarg, '\001')))
                    *comma++ = 0;
                  member->next = list;
                  list = member;
@@ -1367,7 +1372,7 @@ struct daemon *read_opts (int argc, char **argv)
                a[0] = optarg;
                for (k = 1; k < 4; k++)
                  {
-                   if (!(a[k] = strchr(a[k-1], ',')))
+                   if (!(a[k] = strchr(a[k-1], '\001')))
                      break;
                    *(a[k]++) = 0;
                  }
@@ -1393,13 +1398,80 @@ struct daemon *read_opts (int argc, char **argv)
                break;
              }
 
+           case 'Y':
+             {
+               struct txt_record *new;
+               unsigned char *p, *q;
+
+               if ((comma = strchr(optarg, '\001')))
+                 *(comma) = 0;
+
+               if (!canonicalise(optarg))
+                 {
+                   option = '?';
+                   problem = "bad TXT record";
+                   break;
+                 }
+                                 
+               if ((q = comma))
+                 while (1)
+                   {
+                     size_t len;
+                     if ((p = strchr(q+1, '\001')))
+                       {
+                         if ((len = p - q - 1) > 255)
+                           { 
+                             option = '?';
+                             break;
+                           }
+                         *q = len;
+                         q = p;
+                       }
+                     else
+                       {
+                         if ((len = strlen(q+1)) > 255)
+                           option = '?';
+                         *q = len;
+                         break;
+                       }
+                   }
+               
+               if (option == '?')
+                 {
+                   problem = "TXT record string too long";
+                   break;
+                 }
+
+               new = safe_malloc(sizeof(struct txt_record));
+               new->next = daemon->txt;
+               daemon->txt = new;
+               new->class = C_IN;
+               if (comma)
+                 {
+                   new->len = q - ((unsigned char *)comma) + *q + 1;
+                   new->txt = safe_malloc(new->len);
+                   memcpy(new->txt, comma, new->len);
+                 }
+               else
+                 {
+                   static char empty[] = "";
+                   new->len = 1;
+                   new->txt = empty;
+                 }
+               
+               if (comma)
+                 *comma = 0;
+               new->name = safe_string_alloc(optarg);
+               break;
+             }
+
            case 'W':
              {
                int port = 1, priority = 0, weight = 0;
                char *name, *target = NULL;
-               struct srv_record *new;
+               struct mx_srv_record *new;
                
-               if ((comma = strchr(optarg, ',')))
+               if ((comma = strchr(optarg, '\001')))
                  *(comma++) = 0;
 
                if (!canonicalise(optarg))
@@ -1413,7 +1485,7 @@ struct daemon *read_opts (int argc, char **argv)
                if (comma)
                  {
                    optarg = comma;
-                   if ((comma = strchr(optarg, ',')))
+                   if ((comma = strchr(optarg, '\001')))
                      *(comma++) = 0;
                    if (!canonicalise(optarg))
                      {
@@ -1425,7 +1497,7 @@ struct daemon *read_opts (int argc, char **argv)
                    if (comma)
                      {
                        optarg = comma;
-                       if ((comma = strchr(optarg, ',')))
+                       if ((comma = strchr(optarg, '\001')))
                          *(comma++) = 0;
                        if (!atoi_check(optarg, &port))
                          {
@@ -1436,7 +1508,7 @@ struct daemon *read_opts (int argc, char **argv)
                        if (comma)
                          {
                            optarg = comma;
-                           if ((comma = strchr(optarg, ',')))
+                           if ((comma = strchr(optarg, '\001')))
                              *(comma++) = 0;
                            if (!atoi_check(optarg, &priority))
                              {
@@ -1447,7 +1519,7 @@ struct daemon *read_opts (int argc, char **argv)
                            if (comma)
                              {
                                optarg = comma;
-                               if ((comma = strchr(optarg, ',')))
+                               if ((comma = strchr(optarg, '\001')))
                                  *(comma++) = 0;
                                if (!atoi_check(optarg, &weight))
                                  {
@@ -1460,11 +1532,12 @@ struct daemon *read_opts (int argc, char **argv)
                      }
                  }
                
-               new = safe_malloc(sizeof(struct srv_record));
-               new->next = daemon->srvnames;
-               daemon->srvnames = new;
-               new->srvname = name;
-               new->srvtarget = target;
+               new = safe_malloc(sizeof(struct mx_srv_record));
+               new->next = daemon->mxnames;
+               daemon->mxnames = new;
+               new->issrv = 1;
+               new->name = name;
+               new->target = target;
                new->srvport = port;
                new->priority = priority;
                new->weight = weight;
@@ -1476,11 +1549,7 @@ struct daemon *read_opts (int argc, char **argv)
       if (option == '?')
        {
          if (f)
-           {
-             sprintf(buff, "%s at line %d of %s ", 
-                     problem ? problem : "error", lineno, conffile);
-             complain(buff, NULL);
-           }
+           complain( problem ? problem : "error", lineno, conffile);
          else
 #ifdef HAVE_GETOPT_LONG
            die("bad command line options: %s.", problem ? problem : "try --help");
@@ -1521,34 +1590,47 @@ struct daemon *read_opts (int argc, char **argv)
   /* only one of these need be specified: the other defaults to the host-name */
   if ((daemon->options & OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
     {
+      struct mx_srv_record *mx;
+      
       if (gethostname(buff, MAXDNAME) == -1)
        die("cannot get host-name: %s", NULL);
       
-      if (!daemon->mxnames)
+      for (mx = daemon->mxnames; mx; mx = mx->next)
+       if (!mx->issrv && hostname_isequal(mx->name, buff))
+         break;
+      
+      if ((daemon->mxtarget || (daemon->options & OPT_LOCALMX)) && !mx)
        {
-         daemon->mxnames = safe_malloc(sizeof(struct mx_record));
-         daemon->mxnames->next = NULL;
-         daemon->mxnames->mxtarget = NULL;
-         daemon->mxnames->mxname = safe_string_alloc(buff);
+         daemon->mxnames = safe_malloc(sizeof(struct mx_srv_record));
+         daemon->mxnames->next = daemon->mxnames;
+         daemon->mxnames->issrv = 0;
+         daemon->mxnames->target = NULL;
+         daemon->mxnames->name = safe_string_alloc(buff);
        }
       
       if (!daemon->mxtarget)
        daemon->mxtarget = safe_string_alloc(buff);
+
+      for (mx = daemon->mxnames; mx; mx = mx->next)
+       if (!mx->issrv && !mx->target)
+         mx->target = daemon->mxtarget;
     }
 
   if (daemon->domain_suffix)
     {
        /* add domain for any srv record without one. */
-      struct srv_record *srv;
+      struct mx_srv_record *srv;
       
-      for (srv = daemon->srvnames; srv; srv = srv->next)
-       if (strchr(srv->srvname, '.') && strchr(srv->srvname, '.') == strrchr(srv->srvname, '.'))
+      for (srv = daemon->mxnames; srv; srv = srv->next)
+       if (srv->issrv &&
+           strchr(srv->name, '.') && 
+           strchr(srv->name, '.') == strrchr(srv->name, '.'))
          {
-           strcpy(buff, srv->srvname);
+           strcpy(buff, srv->name);
            strcat(buff, ".");
            strcat(buff, daemon->domain_suffix);
-           free(srv->srvname);
-           srv->srvname = safe_string_alloc(buff);
+           free(srv->name);
+           srv->name = safe_string_alloc(buff);
          }
     }