LCOV - code coverage report
Current view: top level - libsystemd-network - dhcp6-option.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 220 309 71.2 %
Date: 2019-08-23 13:36:53 Functions: 12 13 92.3 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 140 273 51.3 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : /***
       3                 :            :   Copyright © 2014-2015 Intel Corporation. All rights reserved.
       4                 :            : ***/
       5                 :            : 
       6                 :            : #include <errno.h>
       7                 :            : #include <netinet/in.h>
       8                 :            : #include <string.h>
       9                 :            : 
      10                 :            : #include "sd-dhcp6-client.h"
      11                 :            : 
      12                 :            : #include "alloc-util.h"
      13                 :            : #include "dhcp6-internal.h"
      14                 :            : #include "dhcp6-lease-internal.h"
      15                 :            : #include "dhcp6-protocol.h"
      16                 :            : #include "dns-domain.h"
      17                 :            : #include "memory-util.h"
      18                 :            : #include "sparse-endian.h"
      19                 :            : #include "strv.h"
      20                 :            : #include "unaligned.h"
      21                 :            : 
      22                 :            : typedef struct DHCP6StatusOption {
      23                 :            :         struct DHCP6Option option;
      24                 :            :         be16_t status;
      25                 :            :         char msg[];
      26                 :            : } _packed_ DHCP6StatusOption;
      27                 :            : 
      28                 :            : typedef struct DHCP6AddressOption {
      29                 :            :         struct DHCP6Option option;
      30                 :            :         struct iaaddr iaaddr;
      31                 :            :         uint8_t options[];
      32                 :            : } _packed_ DHCP6AddressOption;
      33                 :            : 
      34                 :            : typedef struct DHCP6PDPrefixOption {
      35                 :            :         struct DHCP6Option option;
      36                 :            :         struct iapdprefix iapdprefix;
      37                 :            :         uint8_t options[];
      38                 :            : } _packed_ DHCP6PDPrefixOption;
      39                 :            : 
      40                 :            : #define DHCP6_OPTION_IA_NA_LEN (sizeof(struct ia_na))
      41                 :            : #define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd))
      42                 :            : #define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta))
      43                 :            : 
      44                 :         72 : static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode,
      45                 :            :                              size_t optlen) {
      46                 :         72 :         DHCP6Option *option = (DHCP6Option*) *buf;
      47                 :            : 
      48   [ -  +  -  + ]:         72 :         assert_return(buf, -EINVAL);
      49   [ -  +  -  + ]:         72 :         assert_return(*buf, -EINVAL);
      50   [ -  +  -  + ]:         72 :         assert_return(buflen, -EINVAL);
      51                 :            : 
      52   [ +  -  -  + ]:         72 :         if (optlen > 0xffff || *buflen < optlen + offsetof(DHCP6Option, data))
      53                 :          0 :                 return -ENOBUFS;
      54                 :            : 
      55                 :         72 :         option->code = htobe16(optcode);
      56                 :         72 :         option->len = htobe16(optlen);
      57                 :            : 
      58                 :         72 :         *buf += offsetof(DHCP6Option, data);
      59                 :         72 :         *buflen -= offsetof(DHCP6Option, data);
      60                 :            : 
      61                 :         72 :         return 0;
      62                 :            : }
      63                 :            : 
      64                 :         60 : int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
      65                 :            :                         size_t optlen, const void *optval) {
      66                 :            :         int r;
      67                 :            : 
      68   [ +  +  -  +  :         60 :         assert_return(optval || optlen == 0, -EINVAL);
                   -  + ]
      69                 :            : 
      70                 :         60 :         r = option_append_hdr(buf, buflen, code, optlen);
      71         [ -  + ]:         60 :         if (r < 0)
      72                 :          0 :                 return r;
      73                 :            : 
      74                 :         60 :         memcpy_safe(*buf, optval, optlen);
      75                 :            : 
      76                 :         60 :         *buf += optlen;
      77                 :         60 :         *buflen -= optlen;
      78                 :            : 
      79                 :         60 :         return 0;
      80                 :            : }
      81                 :            : 
      82                 :          8 : int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) {
      83                 :            :         uint16_t len;
      84                 :            :         uint8_t *ia_hdr;
      85                 :          8 :         size_t iaid_offset, ia_buflen, ia_addrlen = 0;
      86                 :            :         DHCP6Address *addr;
      87                 :            :         int r;
      88                 :            : 
      89   [ -  +  -  + ]:          8 :         assert_return(buf, -EINVAL);
      90   [ -  +  -  + ]:          8 :         assert_return(*buf, -EINVAL);
      91   [ -  +  -  + ]:          8 :         assert_return(buflen, -EINVAL);
      92   [ -  +  -  + ]:          8 :         assert_return(ia, -EINVAL);
      93                 :            : 
      94      [ +  -  - ]:          8 :         switch (ia->type) {
      95                 :          8 :         case SD_DHCP6_OPTION_IA_NA:
      96                 :          8 :                 len = DHCP6_OPTION_IA_NA_LEN;
      97                 :          8 :                 iaid_offset = offsetof(DHCP6IA, ia_na);
      98                 :          8 :                 break;
      99                 :            : 
     100                 :          0 :         case SD_DHCP6_OPTION_IA_TA:
     101                 :          0 :                 len = DHCP6_OPTION_IA_TA_LEN;
     102                 :          0 :                 iaid_offset = offsetof(DHCP6IA, ia_ta);
     103                 :          0 :                 break;
     104                 :            : 
     105                 :          0 :         default:
     106                 :          0 :                 return -EINVAL;
     107                 :            :         }
     108                 :            : 
     109         [ -  + ]:          8 :         if (*buflen < offsetof(DHCP6Option, data) + len)
     110                 :          0 :                 return -ENOBUFS;
     111                 :            : 
     112                 :          8 :         ia_hdr = *buf;
     113                 :          8 :         ia_buflen = *buflen;
     114                 :            : 
     115                 :          8 :         *buf += offsetof(DHCP6Option, data);
     116                 :          8 :         *buflen -= offsetof(DHCP6Option, data);
     117                 :            : 
     118                 :          8 :         memcpy(*buf, (char*) ia + iaid_offset, len);
     119                 :            : 
     120                 :          8 :         *buf += len;
     121                 :          8 :         *buflen -= len;
     122                 :            : 
     123         [ +  + ]:         12 :         LIST_FOREACH(addresses, addr, ia->addresses) {
     124                 :          4 :                 r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR,
     125                 :            :                                       sizeof(addr->iaaddr));
     126         [ -  + ]:          4 :                 if (r < 0)
     127                 :          0 :                         return r;
     128                 :            : 
     129                 :          4 :                 memcpy(*buf, &addr->iaaddr, sizeof(addr->iaaddr));
     130                 :            : 
     131                 :          4 :                 *buf += sizeof(addr->iaaddr);
     132                 :          4 :                 *buflen -= sizeof(addr->iaaddr);
     133                 :            : 
     134                 :          4 :                 ia_addrlen += offsetof(DHCP6Option, data) + sizeof(addr->iaaddr);
     135                 :            :         }
     136                 :            : 
     137                 :          8 :         r = option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len + ia_addrlen);
     138         [ -  + ]:          8 :         if (r < 0)
     139                 :          0 :                 return r;
     140                 :            : 
     141                 :          8 :         return 0;
     142                 :            : }
     143                 :            : 
     144                 :          8 : int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) {
     145                 :            :         uint8_t buffer[1 + DNS_WIRE_FORMAT_HOSTNAME_MAX];
     146                 :            :         int r;
     147                 :            : 
     148   [ +  -  +  -  :          8 :         assert_return(buf && *buf && buflen && fqdn, -EINVAL);
          +  -  +  -  +  
             -  -  +  -  
                      + ]
     149                 :            : 
     150                 :          8 :         buffer[0] = DHCP6_FQDN_FLAG_S; /* Request server to perform AAAA RR DNS updates */
     151                 :            : 
     152                 :            :         /* Store domain name after flags field */
     153                 :          8 :         r = dns_name_to_wire_format(fqdn, buffer + 1, sizeof(buffer) - 1,  false);
     154         [ -  + ]:          8 :         if (r <= 0)
     155                 :          0 :                 return r;
     156                 :            : 
     157                 :            :         /*
     158                 :            :          * According to RFC 4704, chapter 4.2 only add terminating zero-length
     159                 :            :          * label in case a FQDN is provided. Since dns_name_to_wire_format
     160                 :            :          * always adds terminating zero-length label remove if only a hostname
     161                 :            :          * is provided.
     162                 :            :          */
     163         [ -  + ]:          8 :         if (dns_name_is_single_label(fqdn))
     164                 :          0 :                 r--;
     165                 :            : 
     166                 :          8 :         r = dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_FQDN, 1 + r, buffer);
     167                 :            : 
     168                 :          8 :         return r;
     169                 :            : }
     170                 :            : 
     171                 :          0 : int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd) {
     172                 :          0 :         DHCP6Option *option = (DHCP6Option *)buf;
     173                 :          0 :         size_t i = sizeof(*option) + sizeof(pd->ia_pd);
     174                 :            :         DHCP6Address *prefix;
     175                 :            : 
     176   [ #  #  #  # ]:          0 :         assert_return(buf, -EINVAL);
     177   [ #  #  #  # ]:          0 :         assert_return(pd, -EINVAL);
     178   [ #  #  #  # ]:          0 :         assert_return(pd->type == SD_DHCP6_OPTION_IA_PD, -EINVAL);
     179                 :            : 
     180         [ #  # ]:          0 :         if (len < i)
     181                 :          0 :                 return -ENOBUFS;
     182                 :            : 
     183                 :          0 :         option->code = htobe16(SD_DHCP6_OPTION_IA_PD);
     184                 :            : 
     185                 :          0 :         memcpy(&option->data, &pd->ia_pd, sizeof(pd->ia_pd));
     186                 :            : 
     187         [ #  # ]:          0 :         LIST_FOREACH(addresses, prefix, pd->addresses) {
     188                 :            :                 DHCP6PDPrefixOption *prefix_opt;
     189                 :            : 
     190         [ #  # ]:          0 :                 if (len < i + sizeof(*prefix_opt))
     191                 :          0 :                         return -ENOBUFS;
     192                 :            : 
     193                 :          0 :                 prefix_opt = (DHCP6PDPrefixOption *)&buf[i];
     194                 :          0 :                 prefix_opt->option.code = htobe16(SD_DHCP6_OPTION_IA_PD_PREFIX);
     195                 :          0 :                 prefix_opt->option.len = htobe16(sizeof(prefix_opt->iapdprefix));
     196                 :            : 
     197                 :          0 :                 memcpy(&prefix_opt->iapdprefix, &prefix->iapdprefix,
     198                 :            :                        sizeof(struct iapdprefix));
     199                 :            : 
     200                 :          0 :                 i += sizeof(*prefix_opt);
     201                 :            :         }
     202                 :            : 
     203                 :          0 :         option->len = htobe16(i - sizeof(*option));
     204                 :            : 
     205                 :          0 :         return i;
     206                 :            : }
     207                 :            : 
     208                 :         12 : static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen) {
     209                 :         12 :         DHCP6Option *option = (DHCP6Option*) *buf;
     210                 :            :         uint16_t len;
     211                 :            : 
     212   [ -  +  -  + ]:         12 :         assert_return(buf, -EINVAL);
     213   [ -  +  -  + ]:         12 :         assert_return(optcode, -EINVAL);
     214   [ -  +  -  + ]:         12 :         assert_return(optlen, -EINVAL);
     215                 :            : 
     216         [ +  + ]:         12 :         if (*buflen < offsetof(DHCP6Option, data))
     217                 :          4 :                 return -ENOMSG;
     218                 :            : 
     219                 :          8 :         len = be16toh(option->len);
     220                 :            : 
     221         [ -  + ]:          8 :         if (len > *buflen)
     222                 :          0 :                 return -ENOMSG;
     223                 :            : 
     224                 :          8 :         *optcode = be16toh(option->code);
     225                 :          8 :         *optlen = len;
     226                 :            : 
     227                 :          8 :         *buf += 4;
     228                 :          8 :         *buflen -= 4;
     229                 :            : 
     230                 :          8 :         return 0;
     231                 :            : }
     232                 :            : 
     233                 :         12 : int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
     234                 :            :                        size_t *optlen, uint8_t **optvalue) {
     235                 :            :         int r;
     236                 :            : 
     237   [ +  -  +  -  :         12 :         assert_return(buf && buflen && optcode && optlen && optvalue, -EINVAL);
          +  -  +  -  +  
          -  +  -  +  -  
             -  +  -  + ]
     238                 :            : 
     239                 :         12 :         r = option_parse_hdr(buf, buflen, optcode, optlen);
     240         [ +  + ]:         12 :         if (r < 0)
     241                 :          4 :                 return r;
     242                 :            : 
     243         [ -  + ]:          8 :         if (*optlen > *buflen)
     244                 :          0 :                 return -ENOBUFS;
     245                 :            : 
     246                 :          8 :         *optvalue = *buf;
     247                 :          8 :         *buflen -= *optlen;
     248                 :          8 :         *buf += *optlen;
     249                 :            : 
     250                 :          8 :         return 0;
     251                 :            : }
     252                 :            : 
     253                 :         36 : int dhcp6_option_parse_status(DHCP6Option *option, size_t len) {
     254                 :         36 :         DHCP6StatusOption *statusopt = (DHCP6StatusOption *)option;
     255                 :            : 
     256         [ +  - ]:         36 :         if (len < sizeof(DHCP6StatusOption) ||
     257         [ -  + ]:         36 :             be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(DHCP6StatusOption))
     258                 :          0 :                 return -ENOBUFS;
     259                 :            : 
     260                 :         36 :         return be16toh(statusopt->status);
     261                 :            : }
     262                 :            : 
     263                 :         24 : static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia,
     264                 :            :                                       uint32_t *lifetime_valid) {
     265                 :         24 :         DHCP6AddressOption *addr_option = (DHCP6AddressOption *)option;
     266                 :            :         DHCP6Address *addr;
     267                 :            :         uint32_t lt_valid, lt_pref;
     268                 :            :         int r;
     269                 :            : 
     270         [ -  + ]:         24 :         if (be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(*addr_option))
     271                 :          0 :                 return -ENOBUFS;
     272                 :            : 
     273                 :         24 :         lt_valid = be32toh(addr_option->iaaddr.lifetime_valid);
     274                 :         24 :         lt_pref = be32toh(addr_option->iaaddr.lifetime_preferred);
     275                 :            : 
     276   [ +  -  -  + ]:         24 :         if (lt_valid == 0 || lt_pref > lt_valid) {
     277                 :          0 :                 log_dhcp6_client(client, "Valid lifetime of an IA address is zero or preferred lifetime %d > valid lifetime %d",
     278                 :            :                                  lt_pref, lt_valid);
     279                 :            : 
     280                 :          0 :                 return 0;
     281                 :            :         }
     282                 :            : 
     283         [ +  + ]:         24 :         if (be16toh(option->len) + offsetof(DHCP6Option, data) > sizeof(*addr_option)) {
     284                 :          8 :                 r = dhcp6_option_parse_status((DHCP6Option *)addr_option->options, be16toh(option->len) + offsetof(DHCP6Option, data) - sizeof(*addr_option));
     285         [ +  + ]:          8 :                 if (r != 0)
     286                 :          4 :                         return r < 0 ? r: 0;
     287                 :            :         }
     288                 :            : 
     289                 :         20 :         addr = new0(DHCP6Address, 1);
     290         [ -  + ]:         20 :         if (!addr)
     291                 :          0 :                 return -ENOMEM;
     292                 :            : 
     293         [ -  + ]:         20 :         LIST_INIT(addresses, addr);
     294                 :         20 :         memcpy(&addr->iaaddr, option->data, sizeof(addr->iaaddr));
     295                 :            : 
     296   [ -  +  -  + ]:         20 :         LIST_PREPEND(addresses, ia->addresses, addr);
     297                 :            : 
     298                 :         20 :         *lifetime_valid = be32toh(addr->iaaddr.lifetime_valid);
     299                 :            : 
     300                 :         20 :         return 0;
     301                 :            : }
     302                 :            : 
     303                 :         12 : static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia,
     304                 :            :                                        uint32_t *lifetime_valid) {
     305                 :         12 :         DHCP6PDPrefixOption *pdprefix_option = (DHCP6PDPrefixOption *)option;
     306                 :            :         DHCP6Address *prefix;
     307                 :            :         uint32_t lt_valid, lt_pref;
     308                 :            :         int r;
     309                 :            : 
     310         [ -  + ]:         12 :         if (be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(*pdprefix_option))
     311                 :          0 :                 return -ENOBUFS;
     312                 :            : 
     313                 :         12 :         lt_valid = be32toh(pdprefix_option->iapdprefix.lifetime_valid);
     314                 :         12 :         lt_pref = be32toh(pdprefix_option->iapdprefix.lifetime_preferred);
     315                 :            : 
     316   [ +  -  -  + ]:         12 :         if (lt_valid == 0 || lt_pref > lt_valid) {
     317                 :          0 :                 log_dhcp6_client(client, "Valid lifetieme of a PD prefix is zero or preferred lifetime %d > valid lifetime %d",
     318                 :            :                                  lt_pref, lt_valid);
     319                 :            : 
     320                 :          0 :                 return 0;
     321                 :            :         }
     322                 :            : 
     323         [ +  - ]:         12 :         if (be16toh(option->len) + offsetof(DHCP6Option, data) > sizeof(*pdprefix_option)) {
     324                 :         12 :                 r = dhcp6_option_parse_status((DHCP6Option *)pdprefix_option->options, be16toh(option->len) + offsetof(DHCP6Option, data) - sizeof(*pdprefix_option));
     325         [ -  + ]:         12 :                 if (r != 0)
     326                 :          0 :                         return r < 0 ? r: 0;
     327                 :            :         }
     328                 :            : 
     329                 :         12 :         prefix = new0(DHCP6Address, 1);
     330         [ -  + ]:         12 :         if (!prefix)
     331                 :          0 :                 return -ENOMEM;
     332                 :            : 
     333         [ -  + ]:         12 :         LIST_INIT(addresses, prefix);
     334                 :         12 :         memcpy(&prefix->iapdprefix, option->data, sizeof(prefix->iapdprefix));
     335                 :            : 
     336   [ -  +  +  + ]:         12 :         LIST_PREPEND(addresses, ia->addresses, prefix);
     337                 :            : 
     338                 :         12 :         *lifetime_valid = be32toh(prefix->iapdprefix.lifetime_valid);
     339                 :            : 
     340                 :         12 :         return 0;
     341                 :            : }
     342                 :            : 
     343                 :         44 : int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
     344                 :            :         uint16_t iatype, optlen;
     345                 :            :         size_t i, len;
     346                 :         44 :         int r = 0, status;
     347                 :            :         uint16_t opt;
     348                 :            :         size_t iaaddr_offset;
     349                 :         44 :         uint32_t lt_t1, lt_t2, lt_valid = 0, lt_min = UINT32_MAX;
     350                 :            : 
     351   [ -  +  -  + ]:         44 :         assert_return(ia, -EINVAL);
     352   [ -  +  -  + ]:         44 :         assert_return(!ia->addresses, -EINVAL);
     353                 :            : 
     354                 :         44 :         iatype = be16toh(iaoption->code);
     355                 :         44 :         len = be16toh(iaoption->len);
     356                 :            : 
     357   [ +  +  -  - ]:         44 :         switch (iatype) {
     358                 :         36 :         case SD_DHCP6_OPTION_IA_NA:
     359                 :            : 
     360         [ +  + ]:         36 :                 if (len < DHCP6_OPTION_IA_NA_LEN)
     361                 :          4 :                         return -ENOBUFS;
     362                 :            : 
     363                 :         32 :                 iaaddr_offset = DHCP6_OPTION_IA_NA_LEN;
     364                 :         32 :                 memcpy(&ia->ia_na, iaoption->data, sizeof(ia->ia_na));
     365                 :            : 
     366                 :         32 :                 lt_t1 = be32toh(ia->ia_na.lifetime_t1);
     367                 :         32 :                 lt_t2 = be32toh(ia->ia_na.lifetime_t2);
     368                 :            : 
     369   [ +  -  +  -  :         32 :                 if (lt_t1 && lt_t2 && lt_t1 > lt_t2) {
                   -  + ]
     370                 :          0 :                         log_dhcp6_client(client, "IA NA T1 %ds > T2 %ds",
     371                 :            :                                          lt_t1, lt_t2);
     372                 :          0 :                         return -EINVAL;
     373                 :            :                 }
     374                 :            : 
     375                 :         32 :                 break;
     376                 :            : 
     377                 :          8 :         case SD_DHCP6_OPTION_IA_PD:
     378                 :            : 
     379         [ -  + ]:          8 :                 if (len < sizeof(ia->ia_pd))
     380                 :          0 :                         return -ENOBUFS;
     381                 :            : 
     382                 :          8 :                 iaaddr_offset = sizeof(ia->ia_pd);
     383                 :          8 :                 memcpy(&ia->ia_pd, iaoption->data, sizeof(ia->ia_pd));
     384                 :            : 
     385                 :          8 :                 lt_t1 = be32toh(ia->ia_pd.lifetime_t1);
     386                 :          8 :                 lt_t2 = be32toh(ia->ia_pd.lifetime_t2);
     387                 :            : 
     388   [ +  -  +  -  :          8 :                 if (lt_t1 && lt_t2 && lt_t1 > lt_t2) {
                   -  + ]
     389                 :          0 :                         log_dhcp6_client(client, "IA PD T1 %ds > T2 %ds",
     390                 :            :                                          lt_t1, lt_t2);
     391                 :          0 :                         return -EINVAL;
     392                 :            :                 }
     393                 :            : 
     394                 :          8 :                 break;
     395                 :            : 
     396                 :          0 :         case SD_DHCP6_OPTION_IA_TA:
     397         [ #  # ]:          0 :                 if (len < DHCP6_OPTION_IA_TA_LEN)
     398                 :          0 :                         return -ENOBUFS;
     399                 :            : 
     400                 :          0 :                 iaaddr_offset = DHCP6_OPTION_IA_TA_LEN;
     401                 :          0 :                 memcpy(&ia->ia_ta.id, iaoption->data, sizeof(ia->ia_ta));
     402                 :            : 
     403                 :          0 :                 break;
     404                 :            : 
     405                 :          0 :         default:
     406                 :          0 :                 return -ENOMSG;
     407                 :            :         }
     408                 :            : 
     409                 :         40 :         ia->type = iatype;
     410                 :         40 :         i = iaaddr_offset;
     411                 :            : 
     412         [ +  + ]:         88 :         while (i < len) {
     413                 :         56 :                 DHCP6Option *option = (DHCP6Option *)&iaoption->data[i];
     414                 :            : 
     415   [ +  -  +  + ]:         56 :                 if (len < i + sizeof(*option) || len < i + sizeof(*option) + be16toh(option->len))
     416                 :          4 :                         return -ENOBUFS;
     417                 :            : 
     418                 :         52 :                 opt = be16toh(option->code);
     419                 :         52 :                 optlen = be16toh(option->len);
     420                 :            : 
     421   [ +  +  +  - ]:         52 :                 switch (opt) {
     422                 :         24 :                 case SD_DHCP6_OPTION_IAADDR:
     423                 :            : 
     424   [ +  -  -  + ]:         24 :                         if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) {
     425                 :          0 :                                 log_dhcp6_client(client, "IA Address option not in IA NA or TA option");
     426                 :          0 :                                 return -EINVAL;
     427                 :            :                         }
     428                 :            : 
     429                 :         24 :                         r = dhcp6_option_parse_address(option, ia, &lt_valid);
     430         [ -  + ]:         24 :                         if (r < 0)
     431                 :          0 :                                 return r;
     432                 :            : 
     433         [ +  - ]:         24 :                         if (lt_valid < lt_min)
     434                 :         24 :                                 lt_min = lt_valid;
     435                 :            : 
     436                 :         24 :                         break;
     437                 :            : 
     438                 :         12 :                 case SD_DHCP6_OPTION_IA_PD_PREFIX:
     439                 :            : 
     440   [ +  -  -  + ]:         12 :                         if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_PD)) {
     441                 :          0 :                                 log_dhcp6_client(client, "IA PD Prefix option not in IA PD option");
     442                 :          0 :                                 return -EINVAL;
     443                 :            :                         }
     444                 :            : 
     445                 :         12 :                         r = dhcp6_option_parse_pdprefix(option, ia, &lt_valid);
     446         [ -  + ]:         12 :                         if (r < 0)
     447                 :          0 :                                 return r;
     448                 :            : 
     449         [ +  + ]:         12 :                         if (lt_valid < lt_min)
     450                 :          8 :                                 lt_min = lt_valid;
     451                 :            : 
     452                 :         12 :                         break;
     453                 :            : 
     454                 :         16 :                 case SD_DHCP6_OPTION_STATUS_CODE:
     455                 :            : 
     456                 :         16 :                         status = dhcp6_option_parse_status(option, optlen + offsetof(DHCP6Option, data));
     457         [ -  + ]:         16 :                         if (status < 0)
     458                 :          0 :                                 return status;
     459         [ +  + ]:         16 :                         if (status > 0) {
     460                 :          4 :                                 log_dhcp6_client(client, "IA status %d",
     461                 :            :                                                  status);
     462                 :            : 
     463                 :          4 :                                 return -EINVAL;
     464                 :            :                         }
     465                 :            : 
     466                 :         12 :                         break;
     467                 :            : 
     468                 :          0 :                 default:
     469                 :          0 :                         log_dhcp6_client(client, "Unknown IA option %d", opt);
     470                 :          0 :                         break;
     471                 :            :                 }
     472                 :            : 
     473                 :         48 :                 i += sizeof(*option) + optlen;
     474                 :            :         }
     475                 :            : 
     476      [ +  +  - ]:         32 :         switch(iatype) {
     477                 :         24 :         case SD_DHCP6_OPTION_IA_NA:
     478   [ -  +  #  # ]:         24 :                 if (!ia->ia_na.lifetime_t1 && !ia->ia_na.lifetime_t2) {
     479                 :          0 :                         lt_t1 = lt_min / 2;
     480                 :          0 :                         lt_t2 = lt_min / 10 * 8;
     481                 :          0 :                         ia->ia_na.lifetime_t1 = htobe32(lt_t1);
     482                 :          0 :                         ia->ia_na.lifetime_t2 = htobe32(lt_t2);
     483                 :            : 
     484                 :          0 :                         log_dhcp6_client(client, "Computed IA NA T1 %ds and T2 %ds as both were zero",
     485                 :            :                                          lt_t1, lt_t2);
     486                 :            :                 }
     487                 :            : 
     488                 :         24 :                 break;
     489                 :            : 
     490                 :          8 :         case SD_DHCP6_OPTION_IA_PD:
     491   [ -  +  #  # ]:          8 :                 if (!ia->ia_pd.lifetime_t1 && !ia->ia_pd.lifetime_t2) {
     492                 :          0 :                         lt_t1 = lt_min / 2;
     493                 :          0 :                         lt_t2 = lt_min / 10 * 8;
     494                 :          0 :                         ia->ia_pd.lifetime_t1 = htobe32(lt_t1);
     495                 :          0 :                         ia->ia_pd.lifetime_t2 = htobe32(lt_t2);
     496                 :            : 
     497                 :          0 :                         log_dhcp6_client(client, "Computed IA PD T1 %ds and T2 %ds as both were zero",
     498                 :            :                                          lt_t1, lt_t2);
     499                 :            :                 }
     500                 :            : 
     501                 :          8 :                 break;
     502                 :            : 
     503                 :          0 :         default:
     504                 :          0 :                 break;
     505                 :            :         }
     506                 :            : 
     507                 :         32 :         return 0;
     508                 :            : }
     509                 :            : 
     510                 :         32 : int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
     511                 :            :                                 struct in6_addr **addrs, size_t count,
     512                 :            :                                 size_t *allocated) {
     513                 :            : 
     514   [ +  -  -  + ]:         32 :         if (optlen == 0 || optlen % sizeof(struct in6_addr) != 0)
     515                 :          0 :                 return -EINVAL;
     516                 :            : 
     517         [ -  + ]:         32 :         if (!GREEDY_REALLOC(*addrs, *allocated,
     518                 :            :                             count * sizeof(struct in6_addr) + optlen))
     519                 :          0 :                 return -ENOMEM;
     520                 :            : 
     521                 :         32 :         memcpy(*addrs + count, optval, optlen);
     522                 :            : 
     523                 :         32 :         count += optlen / sizeof(struct in6_addr);
     524                 :            : 
     525                 :         32 :         return count;
     526                 :            : }
     527                 :            : 
     528                 :         16 : int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char ***str_arr) {
     529                 :         16 :         size_t pos = 0, idx = 0;
     530                 :         16 :         _cleanup_strv_free_ char **names = NULL;
     531                 :            :         int r;
     532                 :            : 
     533   [ -  +  -  + ]:         16 :         assert_return(optlen > 1, -ENODATA);
     534   [ -  +  -  + ]:         16 :         assert_return(optval[optlen - 1] == '\0', -EINVAL);
     535                 :            : 
     536         [ +  + ]:         32 :         while (pos < optlen) {
     537      [ +  -  - ]:         16 :                 _cleanup_free_ char *ret = NULL;
     538                 :         16 :                 size_t n = 0, allocated = 0;
     539                 :         16 :                 bool first = true;
     540                 :            : 
     541                 :         32 :                 for (;;) {
     542                 :            :                         const char *label;
     543                 :            :                         uint8_t c;
     544                 :            : 
     545                 :         48 :                         c = optval[pos++];
     546                 :            : 
     547         [ +  + ]:         48 :                         if (c == 0)
     548                 :            :                                 /* End of name */
     549                 :         16 :                                 break;
     550         [ -  + ]:         32 :                         if (c > 63)
     551                 :          0 :                                 return -EBADMSG;
     552                 :            : 
     553                 :            :                         /* Literal label */
     554                 :         32 :                         label = (const char *)&optval[pos];
     555                 :         32 :                         pos += c;
     556         [ -  + ]:         32 :                         if (pos >= optlen)
     557                 :          0 :                                 return -EMSGSIZE;
     558                 :            : 
     559         [ -  + ]:         32 :                         if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
     560                 :          0 :                                 return -ENOMEM;
     561                 :            : 
     562         [ +  + ]:         32 :                         if (first)
     563                 :         16 :                                 first = false;
     564                 :            :                         else
     565                 :         16 :                                 ret[n++] = '.';
     566                 :            : 
     567                 :         32 :                         r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX);
     568         [ -  + ]:         32 :                         if (r < 0)
     569                 :          0 :                                 return r;
     570                 :            : 
     571                 :         32 :                         n += r;
     572                 :            :                 }
     573                 :            : 
     574         [ -  + ]:         16 :                 if (n == 0)
     575                 :          0 :                         continue;
     576                 :            : 
     577         [ -  + ]:         16 :                 if (!GREEDY_REALLOC(ret, allocated, n + 1))
     578                 :          0 :                         return -ENOMEM;
     579                 :            : 
     580                 :         16 :                 ret[n] = 0;
     581                 :            : 
     582                 :         16 :                 r = strv_extend(&names, ret);
     583         [ -  + ]:         16 :                 if (r < 0)
     584                 :          0 :                         return r;
     585                 :            : 
     586                 :         16 :                 idx++;
     587                 :            :         }
     588                 :            : 
     589                 :         16 :         *str_arr = TAKE_PTR(names);
     590                 :            : 
     591                 :         16 :         return idx;
     592                 :            : }

Generated by: LCOV version 1.14