LCOV - code coverage report
Current view: top level - libsystemd-network - dhcp-option.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 98 151 64.9 %
Date: 2019-08-23 13:36:53 Functions: 4 4 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 58 118 49.2 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : /***
       3                 :            :   Copyright © 2013 Intel Corporation. All rights reserved.
       4                 :            : ***/
       5                 :            : 
       6                 :            : #include <errno.h>
       7                 :            : #include <stdint.h>
       8                 :            : #include <stdio.h>
       9                 :            : #include <string.h>
      10                 :            : 
      11                 :            : #include "alloc-util.h"
      12                 :            : #include "dhcp-internal.h"
      13                 :            : #include "memory-util.h"
      14                 :            : #include "strv.h"
      15                 :            : #include "utf8.h"
      16                 :            : 
      17                 :         72 : static int option_append(uint8_t options[], size_t size, size_t *offset,
      18                 :            :                          uint8_t code, size_t optlen, const void *optval) {
      19         [ -  + ]:         72 :         assert(options);
      20         [ -  + ]:         72 :         assert(offset);
      21                 :            : 
      22         [ +  + ]:         72 :         if (code != SD_DHCP_OPTION_END)
      23                 :            :                 /* always make sure there is space for an END option */
      24                 :         60 :                 size--;
      25                 :            : 
      26      [ +  -  + ]:         72 :         switch (code) {
      27                 :            : 
      28                 :         24 :         case SD_DHCP_OPTION_PAD:
      29                 :            :         case SD_DHCP_OPTION_END:
      30         [ +  + ]:         24 :                 if (*offset + 1 > size)
      31                 :          4 :                         return -ENOBUFS;
      32                 :            : 
      33                 :         20 :                 options[*offset] = code;
      34                 :         20 :                 *offset += 1;
      35                 :         20 :                 break;
      36                 :            : 
      37                 :          0 :         case SD_DHCP_OPTION_USER_CLASS: {
      38                 :          0 :                 size_t total = 0;
      39                 :            :                 char **s;
      40                 :            : 
      41   [ #  #  #  # ]:          0 :                 STRV_FOREACH(s, (char **) optval) {
      42                 :          0 :                         size_t len = strlen(*s);
      43                 :            : 
      44         [ #  # ]:          0 :                         if (len > 255)
      45                 :          0 :                                 return -ENAMETOOLONG;
      46                 :            : 
      47                 :          0 :                         total += 1 + len;
      48                 :            :                 }
      49                 :            : 
      50         [ #  # ]:          0 :                 if (*offset + 2 + total > size)
      51                 :          0 :                         return -ENOBUFS;
      52                 :            : 
      53                 :          0 :                 options[*offset] = code;
      54                 :          0 :                 options[*offset + 1] =  total;
      55                 :          0 :                 *offset += 2;
      56                 :            : 
      57   [ #  #  #  # ]:          0 :                 STRV_FOREACH(s, (char **) optval) {
      58                 :          0 :                         size_t len = strlen(*s);
      59                 :            : 
      60                 :          0 :                         options[*offset] = len;
      61                 :            : 
      62                 :          0 :                         memcpy(&options[*offset + 1], *s, len);
      63                 :          0 :                         *offset += 1 + len;
      64                 :            :                 }
      65                 :            : 
      66                 :          0 :                 break;
      67                 :            :         }
      68                 :         48 :         default:
      69         [ +  + ]:         48 :                 if (*offset + 2 + optlen > size)
      70                 :          4 :                         return -ENOBUFS;
      71                 :            : 
      72                 :         44 :                 options[*offset] = code;
      73                 :         44 :                 options[*offset + 1] = optlen;
      74                 :            : 
      75                 :         44 :                 memcpy_safe(&options[*offset + 2], optval, optlen);
      76                 :         44 :                 *offset += 2 + optlen;
      77                 :            : 
      78                 :         44 :                 break;
      79                 :            :         }
      80                 :            : 
      81                 :         64 :         return 0;
      82                 :            : }
      83                 :            : 
      84                 :         68 : int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset,
      85                 :            :                        uint8_t overload,
      86                 :            :                        uint8_t code, size_t optlen, const void *optval) {
      87                 :         68 :         const bool use_file = overload & DHCP_OVERLOAD_FILE;
      88                 :         68 :         const bool use_sname = overload & DHCP_OVERLOAD_SNAME;
      89                 :            :         int r;
      90                 :            : 
      91         [ -  + ]:         68 :         assert(message);
      92         [ -  + ]:         68 :         assert(offset);
      93                 :            : 
      94                 :            :         /* If *offset is in range [0, size), we are writing to ->options,
      95                 :            :          * if *offset is in range [size, size + sizeof(message->file)) and use_file, we are writing to ->file,
      96                 :            :          * if *offset is in range [size + use_file*sizeof(message->file), size + use_file*sizeof(message->file) + sizeof(message->sname))
      97                 :            :          * and use_sname, we are writing to ->sname.
      98                 :            :          */
      99                 :            : 
     100         [ +  + ]:         68 :         if (*offset < size) {
     101                 :            :                 /* still space in the options array */
     102                 :         64 :                 r = option_append(message->options, size, offset, code, optlen, optval);
     103         [ +  + ]:         64 :                 if (r >= 0)
     104                 :         56 :                         return 0;
     105   [ +  -  +  -  :          8 :                 else if (r == -ENOBUFS && (use_file || use_sname)) {
                   +  + ]
     106                 :            :                         /* did not fit, but we have more buffers to try
     107                 :            :                            close the options array and move the offset to its end */
     108                 :          4 :                         r = option_append(message->options, size, offset, SD_DHCP_OPTION_END, 0, NULL);
     109         [ -  + ]:          4 :                         if (r < 0)
     110                 :          0 :                                 return r;
     111                 :            : 
     112                 :          4 :                         *offset = size;
     113                 :            :                 } else
     114                 :          4 :                         return r;
     115                 :            :         }
     116                 :            : 
     117         [ -  + ]:          8 :         if (use_file) {
     118                 :          0 :                 size_t file_offset = *offset - size;
     119                 :            : 
     120         [ #  # ]:          0 :                 if (file_offset < sizeof(message->file)) {
     121                 :            :                         /* still space in the 'file' array */
     122                 :          0 :                         r = option_append(message->file, sizeof(message->file), &file_offset, code, optlen, optval);
     123         [ #  # ]:          0 :                         if (r >= 0) {
     124                 :          0 :                                 *offset = size + file_offset;
     125                 :          0 :                                 return 0;
     126   [ #  #  #  # ]:          0 :                         } else if (r == -ENOBUFS && use_sname) {
     127                 :            :                                 /* did not fit, but we have more buffers to try
     128                 :            :                                    close the file array and move the offset to its end */
     129                 :          0 :                                 r = option_append(message->options, size, offset, SD_DHCP_OPTION_END, 0, NULL);
     130         [ #  # ]:          0 :                                 if (r < 0)
     131                 :          0 :                                         return r;
     132                 :            : 
     133                 :          0 :                                 *offset = size + sizeof(message->file);
     134                 :            :                         } else
     135                 :          0 :                                 return r;
     136                 :            :                 }
     137                 :            :         }
     138                 :            : 
     139         [ +  + ]:          8 :         if (use_sname) {
     140                 :          4 :                 size_t sname_offset = *offset - size - use_file*sizeof(message->file);
     141                 :            : 
     142         [ +  - ]:          4 :                 if (sname_offset < sizeof(message->sname)) {
     143                 :            :                         /* still space in the 'sname' array */
     144                 :          4 :                         r = option_append(message->sname, sizeof(message->sname), &sname_offset, code, optlen, optval);
     145         [ +  - ]:          4 :                         if (r >= 0) {
     146                 :          4 :                                 *offset = size + use_file*sizeof(message->file) + sname_offset;
     147                 :          4 :                                 return 0;
     148                 :            :                         } else
     149                 :            :                                 /* no space, or other error, give up */
     150                 :          0 :                                 return r;
     151                 :            :                 }
     152                 :            :         }
     153                 :            : 
     154                 :          4 :         return -ENOBUFS;
     155                 :            : }
     156                 :            : 
     157                 :         64 : static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overload,
     158                 :            :                          uint8_t *message_type, char **error_message, dhcp_option_callback_t cb,
     159                 :            :                          void *userdata) {
     160                 :            :         uint8_t code, len;
     161                 :            :         const uint8_t *option;
     162                 :         64 :         size_t offset = 0;
     163                 :            : 
     164         [ +  + ]:       1436 :         while (offset < buflen) {
     165                 :       1392 :                 code = options[offset ++];
     166                 :            : 
     167      [ +  +  + ]:       1392 :                 switch (code) {
     168                 :       1232 :                 case SD_DHCP_OPTION_PAD:
     169                 :       1232 :                         continue;
     170                 :            : 
     171                 :         12 :                 case SD_DHCP_OPTION_END:
     172                 :         12 :                         return 0;
     173                 :            :                 }
     174                 :            : 
     175         [ +  + ]:        148 :                 if (buflen < offset + 1)
     176                 :          4 :                         return -ENOBUFS;
     177                 :            : 
     178                 :        144 :                 len = options[offset ++];
     179                 :            : 
     180         [ +  + ]:        144 :                 if (buflen < offset + len)
     181                 :          4 :                         return -EINVAL;
     182                 :            : 
     183                 :        140 :                 option = &options[offset];
     184                 :            : 
     185   [ +  -  +  + ]:        140 :                 switch (code) {
     186                 :         36 :                 case SD_DHCP_OPTION_MESSAGE_TYPE:
     187         [ -  + ]:         36 :                         if (len != 1)
     188                 :          0 :                                 return -EINVAL;
     189                 :            : 
     190         [ +  - ]:         36 :                         if (message_type)
     191                 :         36 :                                 *message_type = *option;
     192                 :            : 
     193                 :         36 :                         break;
     194                 :            : 
     195                 :          0 :                 case SD_DHCP_OPTION_ERROR_MESSAGE:
     196         [ #  # ]:          0 :                         if (len == 0)
     197                 :          0 :                                 return -EINVAL;
     198                 :            : 
     199         [ #  # ]:          0 :                         if (error_message) {
     200         [ #  # ]:          0 :                                 _cleanup_free_ char *string = NULL;
     201                 :            : 
     202                 :            :                                 /* Accept a trailing NUL byte */
     203         [ #  # ]:          0 :                                 if (memchr(option, 0, len - 1))
     204                 :          0 :                                         return -EINVAL;
     205                 :            : 
     206                 :          0 :                                 string = memdup_suffix0((const char *) option, len);
     207         [ #  # ]:          0 :                                 if (!string)
     208                 :          0 :                                         return -ENOMEM;
     209                 :            : 
     210         [ #  # ]:          0 :                                 if (!ascii_is_valid(string))
     211                 :          0 :                                         return -EINVAL;
     212                 :            : 
     213                 :          0 :                                 free_and_replace(*error_message, string);
     214                 :            :                         }
     215                 :            : 
     216                 :          0 :                         break;
     217                 :          8 :                 case SD_DHCP_OPTION_OVERLOAD:
     218         [ -  + ]:          8 :                         if (len != 1)
     219                 :          0 :                                 return -EINVAL;
     220                 :            : 
     221         [ +  - ]:          8 :                         if (overload)
     222                 :          8 :                                 *overload = *option;
     223                 :            : 
     224                 :          8 :                         break;
     225                 :            : 
     226                 :         96 :                 default:
     227         [ +  - ]:         96 :                         if (cb)
     228                 :         96 :                                 cb(code, len, option, userdata);
     229                 :            : 
     230                 :         96 :                         break;
     231                 :            :                 }
     232                 :            : 
     233                 :        140 :                 offset += len;
     234                 :            :         }
     235                 :            : 
     236         [ -  + ]:         44 :         if (offset < buflen)
     237                 :          0 :                 return -EINVAL;
     238                 :            : 
     239                 :         44 :         return 0;
     240                 :            : }
     241                 :            : 
     242                 :         60 : int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_t cb, void *userdata, char **_error_message) {
     243                 :         60 :         _cleanup_free_ char *error_message = NULL;
     244                 :         60 :         uint8_t overload = 0;
     245                 :         60 :         uint8_t message_type = 0;
     246                 :            :         int r;
     247                 :            : 
     248         [ -  + ]:         60 :         if (!message)
     249                 :          0 :                 return -EINVAL;
     250                 :            : 
     251         [ +  + ]:         60 :         if (len < sizeof(DHCPMessage))
     252                 :          8 :                 return -EINVAL;
     253                 :            : 
     254                 :         52 :         len -= sizeof(DHCPMessage);
     255                 :            : 
     256                 :         52 :         r = parse_options(message->options, len, &overload, &message_type, &error_message, cb, userdata);
     257         [ +  + ]:         52 :         if (r < 0)
     258                 :          8 :                 return r;
     259                 :            : 
     260         [ +  + ]:         44 :         if (overload & DHCP_OVERLOAD_FILE) {
     261                 :          8 :                 r = parse_options(message->file, sizeof(message->file), NULL, &message_type, &error_message, cb, userdata);
     262         [ -  + ]:          8 :                 if (r < 0)
     263                 :          0 :                         return r;
     264                 :            :         }
     265                 :            : 
     266         [ +  + ]:         44 :         if (overload & DHCP_OVERLOAD_SNAME) {
     267                 :          4 :                 r = parse_options(message->sname, sizeof(message->sname), NULL, &message_type, &error_message, cb, userdata);
     268         [ -  + ]:          4 :                 if (r < 0)
     269                 :          0 :                         return r;
     270                 :            :         }
     271                 :            : 
     272         [ +  + ]:         44 :         if (message_type == 0)
     273                 :          8 :                 return -ENOMSG;
     274                 :            : 
     275   [ -  +  #  #  :         36 :         if (_error_message && IN_SET(message_type, DHCP_NAK, DHCP_DECLINE))
                   #  # ]
     276                 :          0 :                 *_error_message = TAKE_PTR(error_message);
     277                 :            : 
     278                 :         36 :         return message_type;
     279                 :            : }

Generated by: LCOV version 1.14