Annotation of embedaddon/dnsmasq/src/edns0.c, revision 1.1

1.1     ! misho       1: /* dnsmasq is Copyright (c) 2000-2016 Simon Kelley
        !             2: 
        !             3:    This program is free software; you can redistribute it and/or modify
        !             4:    it under the terms of the GNU General Public License as published by
        !             5:    the Free Software Foundation; version 2 dated June, 1991, or
        !             6:    (at your option) version 3 dated 29 June, 2007.
        !             7:  
        !             8:    This program is distributed in the hope that it will be useful,
        !             9:    but WITHOUT ANY WARRANTY; without even the implied warranty of
        !            10:    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        !            11:    GNU General Public License for more details.
        !            12:      
        !            13:    You should have received a copy of the GNU General Public License
        !            14:    along with this program.  If not, see <http://www.gnu.org/licenses/>.
        !            15: */
        !            16: 
        !            17: #include "dnsmasq.h"
        !            18: 
        !            19: unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t  *len, unsigned char **p, int *is_sign, int *is_last)
        !            20: {
        !            21:   /* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it. 
        !            22:      also return length of pseudoheader in *len and pointer to the UDP size in *p
        !            23:      Finally, check to see if a packet is signed. If it is we cannot change a single bit before
        !            24:      forwarding. We look for TSIG in the addition section, and TKEY queries (for GSS-TSIG) */
        !            25:   
        !            26:   int i, arcount = ntohs(header->arcount);
        !            27:   unsigned char *ansp = (unsigned char *)(header+1);
        !            28:   unsigned short rdlen, type, class;
        !            29:   unsigned char *ret = NULL;
        !            30: 
        !            31:   if (is_sign)
        !            32:     {
        !            33:       *is_sign = 0;
        !            34: 
        !            35:       if (OPCODE(header) == QUERY)
        !            36:        {
        !            37:          for (i = ntohs(header->qdcount); i != 0; i--)
        !            38:            {
        !            39:              if (!(ansp = skip_name(ansp, header, plen, 4)))
        !            40:                return NULL;
        !            41:              
        !            42:              GETSHORT(type, ansp); 
        !            43:              GETSHORT(class, ansp);
        !            44:              
        !            45:              if (class == C_IN && type == T_TKEY)
        !            46:                *is_sign = 1;
        !            47:            }
        !            48:        }
        !            49:     }
        !            50:   else
        !            51:     {
        !            52:       if (!(ansp = skip_questions(header, plen)))
        !            53:        return NULL;
        !            54:     }
        !            55:     
        !            56:   if (arcount == 0)
        !            57:     return NULL;
        !            58:   
        !            59:   if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount), header, plen)))
        !            60:     return NULL; 
        !            61:   
        !            62:   for (i = 0; i < arcount; i++)
        !            63:     {
        !            64:       unsigned char *save, *start = ansp;
        !            65:       if (!(ansp = skip_name(ansp, header, plen, 10)))
        !            66:        return NULL; 
        !            67: 
        !            68:       GETSHORT(type, ansp);
        !            69:       save = ansp;
        !            70:       GETSHORT(class, ansp);
        !            71:       ansp += 4; /* TTL */
        !            72:       GETSHORT(rdlen, ansp);
        !            73:       if (!ADD_RDLEN(header, ansp, plen, rdlen))
        !            74:        return NULL;
        !            75:       if (type == T_OPT)
        !            76:        {
        !            77:          if (len)
        !            78:            *len = ansp - start;
        !            79: 
        !            80:          if (p)
        !            81:            *p = save;
        !            82:          
        !            83:          if (is_last)
        !            84:            *is_last = (i == arcount-1);
        !            85: 
        !            86:          ret = start;
        !            87:        }
        !            88:       else if (is_sign && 
        !            89:               i == arcount - 1 && 
        !            90:               class == C_ANY && 
        !            91:               type == T_TSIG)
        !            92:        *is_sign = 1;
        !            93:     }
        !            94:   
        !            95:   return ret;
        !            96: }
        !            97:  
        !            98: 
        !            99: /* replace == 2 ->delete existing option only. */
        !           100: size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit, 
        !           101:                        unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do, int replace)
        !           102: { 
        !           103:   unsigned char *lenp, *datap, *p, *udp_len, *buff = NULL;
        !           104:   int rdlen = 0, is_sign, is_last;
        !           105:   unsigned short flags = set_do ? 0x8000 : 0, rcode = 0;
        !           106: 
        !           107:   p = find_pseudoheader(header, plen, NULL, &udp_len, &is_sign, &is_last);
        !           108:   
        !           109:   if (is_sign)
        !           110:     return plen;
        !           111: 
        !           112:   if (p)
        !           113:     {
        !           114:       /* Existing header */
        !           115:       int i;
        !           116:       unsigned short code, len;
        !           117: 
        !           118:       p = udp_len;
        !           119:       GETSHORT(udp_sz, p);
        !           120:       GETSHORT(rcode, p);
        !           121:       GETSHORT(flags, p);
        !           122: 
        !           123:       if (set_do)
        !           124:        {
        !           125:          p -= 2;
        !           126:          flags |= 0x8000;
        !           127:          PUTSHORT(flags, p);
        !           128:        }
        !           129: 
        !           130:       lenp = p;
        !           131:       GETSHORT(rdlen, p);
        !           132:       if (!CHECK_LEN(header, p, plen, rdlen))
        !           133:        return plen; /* bad packet */
        !           134:       datap = p;
        !           135: 
        !           136:        /* no option to add */
        !           137:       if (optno == 0)
        !           138:        return plen;
        !           139:          
        !           140:       /* check if option already there */
        !           141:       for (i = 0; i + 4 < rdlen;)
        !           142:        {
        !           143:          GETSHORT(code, p);
        !           144:          GETSHORT(len, p);
        !           145:          
        !           146:          /* malformed option, delete the whole OPT RR and start again. */
        !           147:          if (i + len > rdlen)
        !           148:            {
        !           149:              rdlen = 0;
        !           150:              is_last = 0;
        !           151:              break;
        !           152:            }
        !           153:          
        !           154:          if (code == optno)
        !           155:            {
        !           156:              if (replace == 0)
        !           157:                return plen;
        !           158: 
        !           159:              /* delete option if we're to replace it. */
        !           160:              p -= 4;
        !           161:              rdlen -= len + 4;
        !           162:              memcpy(p, p+len+4, rdlen - i);
        !           163:              PUTSHORT(rdlen, lenp);
        !           164:              lenp -= 2;
        !           165:            }
        !           166:          else
        !           167:            {
        !           168:              p += len;
        !           169:              i += len + 4;
        !           170:            }
        !           171:        }
        !           172: 
        !           173:       /* If we're going to extend the RR, it has to be the last RR in the packet */
        !           174:       if (!is_last)
        !           175:        {
        !           176:          /* First, take a copy of the options. */
        !           177:          if (rdlen != 0 && (buff = whine_malloc(rdlen)))
        !           178:            memcpy(buff, datap, rdlen);       
        !           179:          
        !           180:          /* now, delete OPT RR */
        !           181:          plen = rrfilter(header, plen, 0);
        !           182:          
        !           183:          /* Now, force addition of a new one */
        !           184:          p = NULL;       
        !           185:        }
        !           186:     }
        !           187:   
        !           188:   if (!p)
        !           189:     {
        !           190:       /* We are (re)adding the pseudoheader */
        !           191:       if (!(p = skip_questions(header, plen)) ||
        !           192:          !(p = skip_section(p, 
        !           193:                             ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount), 
        !           194:                             header, plen)))
        !           195:        return plen;
        !           196:       *p++ = 0; /* empty name */
        !           197:       PUTSHORT(T_OPT, p);
        !           198:       PUTSHORT(udp_sz, p); /* max packet length, 512 if not given in EDNS0 header */
        !           199:       PUTSHORT(rcode, p);    /* extended RCODE and version */
        !           200:       PUTSHORT(flags, p); /* DO flag */
        !           201:       lenp = p;
        !           202:       PUTSHORT(rdlen, p);    /* RDLEN */
        !           203:       datap = p;
        !           204:       /* Copy back any options */
        !           205:       if (buff)
        !           206:        {
        !           207:          memcpy(p, buff, rdlen);
        !           208:          free(buff);
        !           209:          p += rdlen;
        !           210:        }
        !           211:       header->arcount = htons(ntohs(header->arcount) + 1);
        !           212:     }
        !           213:   
        !           214:   if (((ssize_t)optlen) > (limit - (p + 4)))
        !           215:     return plen; /* Too big */
        !           216:   
        !           217:   /* Add new option */
        !           218:   if (optno != 0 && replace != 2)
        !           219:     {
        !           220:       PUTSHORT(optno, p);
        !           221:       PUTSHORT(optlen, p);
        !           222:       memcpy(p, opt, optlen);
        !           223:       p += optlen;  
        !           224:       PUTSHORT(p - datap, lenp);
        !           225:     }
        !           226:   return p - (unsigned char *)header;
        !           227: }
        !           228: 
        !           229: size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit)
        !           230: {
        !           231:   return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, 0, NULL, 0, 1, 0);
        !           232: }
        !           233: 
        !           234: static unsigned char char64(unsigned char c)
        !           235: {
        !           236:   return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c & 0x3f];
        !           237: }
        !           238: 
        !           239: static void encoder(unsigned char *in, char *out)
        !           240: {
        !           241:   out[0] = char64(in[0]>>2);
        !           242:   out[1] = char64((in[0]<<4) | (in[1]>>4));
        !           243:   out[2] = char64((in[1]<<2) | (in[2]>>6));
        !           244:   out[3] = char64(in[2]);
        !           245: }
        !           246: 
        !           247: static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *l3, time_t now)
        !           248: {
        !           249:   int maclen, replace = 2; /* can't get mac address, just delete any incoming. */
        !           250:   unsigned char mac[DHCP_CHADDR_MAX];
        !           251:   char encode[18]; /* handle 6 byte MACs */
        !           252: 
        !           253:   if ((maclen = find_mac(l3, mac, 1, now)) == 6)
        !           254:     {
        !           255:       replace = 1;
        !           256: 
        !           257:       if (option_bool(OPT_MAC_HEX))
        !           258:        print_mac(encode, mac, maclen);
        !           259:       else
        !           260:        {
        !           261:          encoder(mac, encode);
        !           262:          encoder(mac+3, encode+4);
        !           263:          encode[8] = 0;
        !           264:        }
        !           265:     }
        !           266: 
        !           267:   return add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encode, strlen(encode), 0, replace); 
        !           268: }
        !           269: 
        !           270: 
        !           271: static size_t add_mac(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *l3, time_t now)
        !           272: {
        !           273:   int maclen;
        !           274:   unsigned char mac[DHCP_CHADDR_MAX];
        !           275: 
        !           276:   if ((maclen = find_mac(l3, mac, 1, now)) != 0)
        !           277:     plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_MAC, mac, maclen, 0, 0); 
        !           278:     
        !           279:   return plen; 
        !           280: }
        !           281: 
        !           282: struct subnet_opt {
        !           283:   u16 family;
        !           284:   u8 source_netmask, scope_netmask;
        !           285: #ifdef HAVE_IPV6 
        !           286:   u8 addr[IN6ADDRSZ];
        !           287: #else
        !           288:   u8 addr[INADDRSZ];
        !           289: #endif
        !           290: };
        !           291: 
        !           292: static void *get_addrp(union mysockaddr *addr, const short family) 
        !           293: {
        !           294: #ifdef HAVE_IPV6
        !           295:   if (family == AF_INET6)
        !           296:     return &addr->in6.sin6_addr;
        !           297: #endif
        !           298: 
        !           299:   return &addr->in.sin_addr;
        !           300: }
        !           301: 
        !           302: static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source)
        !           303: {
        !           304:   /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
        !           305:   
        !           306:   int len;
        !           307:   void *addrp;
        !           308:   int sa_family = source->sa.sa_family;
        !           309: 
        !           310:   opt->source_netmask = 0;
        !           311:   opt->scope_netmask = 0;
        !           312: 
        !           313: #ifdef HAVE_IPV6
        !           314:   if (source->sa.sa_family == AF_INET6 && daemon->add_subnet6)
        !           315:     {
        !           316:       opt->source_netmask = daemon->add_subnet6->mask;
        !           317:       if (daemon->add_subnet6->addr_used) 
        !           318:        {
        !           319:          sa_family = daemon->add_subnet6->addr.sa.sa_family;
        !           320:          addrp = get_addrp(&daemon->add_subnet6->addr, sa_family);
        !           321:        } 
        !           322:       else 
        !           323:        addrp = &source->in6.sin6_addr;
        !           324:     }
        !           325: #endif
        !           326: 
        !           327:   if (source->sa.sa_family == AF_INET && daemon->add_subnet4)
        !           328:     {
        !           329:       opt->source_netmask = daemon->add_subnet4->mask;
        !           330:       if (daemon->add_subnet4->addr_used)
        !           331:        {
        !           332:          sa_family = daemon->add_subnet4->addr.sa.sa_family;
        !           333:          addrp = get_addrp(&daemon->add_subnet4->addr, sa_family);
        !           334:        } 
        !           335:        else 
        !           336:          addrp = &source->in.sin_addr;
        !           337:     }
        !           338:   
        !           339: #ifdef HAVE_IPV6
        !           340:   opt->family = htons(sa_family == AF_INET6 ? 2 : 1);
        !           341: #else
        !           342:   opt->family = htons(1);
        !           343: #endif
        !           344:   
        !           345:   len = 0;
        !           346:   
        !           347:   if (opt->source_netmask != 0)
        !           348:     {
        !           349:       len = ((opt->source_netmask - 1) >> 3) + 1;
        !           350:       memcpy(opt->addr, addrp, len);
        !           351:       if (opt->source_netmask & 7)
        !           352:        opt->addr[len-1] &= 0xff << (8 - (opt->source_netmask & 7));
        !           353:     }
        !           354:   
        !           355:   return len + 4;
        !           356: }
        !           357:  
        !           358: static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source)
        !           359: {
        !           360:   /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
        !           361:   
        !           362:   int len;
        !           363:   struct subnet_opt opt;
        !           364:   
        !           365:   len = calc_subnet_opt(&opt, source);
        !           366:   return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0, 0);
        !           367: }
        !           368: 
        !           369: int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer)
        !           370: {
        !           371:   /* Section 9.2, Check that subnet option in reply matches. */
        !           372:   
        !           373:   int len, calc_len;
        !           374:   struct subnet_opt opt;
        !           375:   unsigned char *p;
        !           376:   int code, i, rdlen;
        !           377:   
        !           378:    calc_len = calc_subnet_opt(&opt, peer);
        !           379:    
        !           380:    if (!(p = skip_name(pseudoheader, header, plen, 10)))
        !           381:      return 1;
        !           382:    
        !           383:    p += 8; /* skip UDP length and RCODE */
        !           384:    
        !           385:    GETSHORT(rdlen, p);
        !           386:    if (!CHECK_LEN(header, p, plen, rdlen))
        !           387:      return 1; /* bad packet */
        !           388:    
        !           389:    /* check if option there */
        !           390:    for (i = 0; i + 4 < rdlen; i += len + 4)
        !           391:      {
        !           392:        GETSHORT(code, p);
        !           393:        GETSHORT(len, p);
        !           394:        if (code == EDNS0_OPTION_CLIENT_SUBNET)
        !           395:         {
        !           396:           /* make sure this doesn't mismatch. */
        !           397:           opt.scope_netmask = p[3];
        !           398:           if (len != calc_len || memcmp(p, &opt, len) != 0)
        !           399:             return 0;
        !           400:         }
        !           401:        p += len;
        !           402:      }
        !           403:    
        !           404:    return 1;
        !           405: }
        !           406: 
        !           407: size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit, 
        !           408:                        union mysockaddr *source, time_t now, int *check_subnet)    
        !           409: {
        !           410:   *check_subnet = 0;
        !           411: 
        !           412:   if (option_bool(OPT_ADD_MAC))
        !           413:     plen  = add_mac(header, plen, limit, source, now);
        !           414:   
        !           415:   if (option_bool(OPT_MAC_B64) || option_bool(OPT_MAC_HEX))
        !           416:     plen = add_dns_client(header, plen, limit, source, now);
        !           417: 
        !           418:   if (daemon->dns_client_id)
        !           419:     plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMCPEID, 
        !           420:                            (unsigned char *)daemon->dns_client_id, strlen(daemon->dns_client_id), 0, 1);
        !           421:   
        !           422:   if (option_bool(OPT_CLIENT_SUBNET))
        !           423:     {
        !           424:       plen = add_source_addr(header, plen, limit, source); 
        !           425:       *check_subnet = 1;
        !           426:     }
        !           427:          
        !           428:   return plen;
        !           429: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>