LCOV - code coverage report
Current view: top level - nss/tests - nss-hardcoded.c (source / functions) Coverage Total Hit
Test: 2 coverage DB files Lines: 80.6 % 62 50
Test Date: 2025-09-15 13:55:46 Functions: 100.0 % 3 3
Branches: 71.9 % 32 23

             Branch data     Line data    Source code
       1                 :             : /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
       2                 :             :  *
       3                 :             :  * Copyright 2025 GNOME Foundation, Inc.
       4                 :             :  *
       5                 :             :  * SPDX-License-Identifier: GPL-2.0-or-later
       6                 :             :  *
       7                 :             :  * This program is free software; you can redistribute it and/or modify
       8                 :             :  * it under the terms of the GNU General Public License as published by
       9                 :             :  * the Free Software Foundation; either version 2 of the License, or
      10                 :             :  * (at your option) any later version.
      11                 :             :  *
      12                 :             :  * This program is distributed in the hope that it will be useful,
      13                 :             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14                 :             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15                 :             :  * GNU General Public License for more details.
      16                 :             :  *
      17                 :             :  * You should have received a copy of the GNU General Public License
      18                 :             :  * along with this program; if not, see <http://www.gnu.org/licenses/>.
      19                 :             :  *
      20                 :             :  * Authors:
      21                 :             :  *  - Philip Withnall <pwithnall@gnome.org>
      22                 :             :  */
      23                 :             : 
      24                 :             : #include <arpa/inet.h>
      25                 :             : #include <assert.h>
      26                 :             : #include <ctype.h>
      27                 :             : #include <errno.h>
      28                 :             : #include <err.h>
      29                 :             : #include <fcntl.h>
      30                 :             : #include <limits.h>
      31                 :             : #include <nss.h>
      32                 :             : #include <netdb.h>
      33                 :             : #include <pwd.h>
      34                 :             : #include <stdbool.h>
      35                 :             : #include <stddef.h>
      36                 :             : #include <stdio.h>
      37                 :             : #include <stdlib.h>
      38                 :             : #include <string.h>
      39                 :             : #include <sys/types.h>
      40                 :             : #include <unistd.h>
      41                 :             : 
      42                 :             : 
      43                 :             : /**
      44                 :             :  * NSS hardcoded test module
      45                 :             :  *
      46                 :             :  * This is an NSS module with some hardcoded `gethostbyname()` results, which we
      47                 :             :  * can use as a fallback behind the `malcontent` NSS module to test whether it
      48                 :             :  * works (and whether it’s recursing lookups and passing through lookups
      49                 :             :  * correctly).
      50                 :             :  *
      51                 :             :  * It is *not* meant to be used in production.
      52                 :             :  *
      53                 :             :  * NSS documentation:
      54                 :             :  *  - https://www.gnu.org/software/libc/manual/html_node/NSS-Modules-Interface.html
      55                 :             :  *  - https://www.gnu.org/software/libc/manual/html_node/NSS-Module-Function-Internals.html
      56                 :             :  *  - https://elixir.bootlin.com/glibc/glibc-2.41/source/nss/getaddrinfo.c
      57                 :             :  */
      58                 :             : 
      59                 :             : /* Exported module API: */
      60                 :             : enum nss_status _nss_hardcoded_gethostbyname3_r (const char      *name,
      61                 :             :                                                  int              af,
      62                 :             :                                                  struct hostent  *result,
      63                 :             :                                                  char            *buffer,
      64                 :             :                                                  size_t           buffer_len,
      65                 :             :                                                  int             *errnop,
      66                 :             :                                                  int             *h_errnop,
      67                 :             :                                                  int32_t         *ttlp,
      68                 :             :                                                  char           **canonp);
      69                 :             : enum nss_status _nss_hardcoded_gethostbyname2_r (const char     *name,
      70                 :             :                                                  int             af,
      71                 :             :                                                  struct hostent *result,
      72                 :             :                                                  char           *buffer,
      73                 :             :                                                  size_t          buffer_len,
      74                 :             :                                                  int            *errnop,
      75                 :             :                                                  int            *h_errnop);
      76                 :             : 
      77                 :             : static inline size_t
      78                 :          16 : align_as_pointer (size_t in)
      79                 :             : {
      80                 :          16 :   const size_t ptr_alignment = __alignof__ (void *);
      81                 :          16 :   return (in + (ptr_alignment - 1)) & ~(ptr_alignment - 1);
      82                 :             : }
      83                 :             : 
      84                 :             : /* As per https://elixir.bootlin.com/glibc/glibc-2.41/source/nss/getaddrinfo.c,
      85                 :             :  * glibc only calls gethostbyname4_r and gethostbyname3_r conditionally. If we
      86                 :             :  * want to support the most possible queries (and versions of glibc), provide
      87                 :             :  * gethostbyname2_r. glibc will handle the fallbacks for other API versions.
      88                 :             :  *
      89                 :             :  * We do need to provide a gethostbyname3_r() function, though, as that’s
      90                 :             :  * explicitly called when AI_CANONNAME is set in the request flags. */
      91                 :             : enum nss_status
      92                 :          44 : _nss_hardcoded_gethostbyname3_r (const char      *name,
      93                 :             :                                  int              af,
      94                 :             :                                  struct hostent  *result,
      95                 :             :                                  char            *buffer,
      96                 :             :                                  size_t           buffer_len,
      97                 :             :                                  int             *errnop,
      98                 :             :                                  int             *h_errnop,
      99                 :             :                                  int32_t         *ttlp,
     100                 :             :                                  char           **canonp)
     101                 :             : {
     102                 :          44 :   const char *result_name = NULL;
     103                 :          44 :   const char *result_addr_str = NULL;
     104                 :          44 :   const char *result_addr6_str = NULL;
     105                 :          44 :   struct in_addr result_addr = { .s_addr = 0 };
     106                 :          44 :   struct in6_addr result_addr6 = { .s6_addr = { 0, } };
     107                 :             : 
     108                 :             :   /* Is the app querying for a protocol which we support? */
     109   [ +  +  -  + ]:          44 :   if (af != AF_INET && af != AF_INET6)
     110                 :             :     {
     111                 :           0 :       *errnop = EAFNOSUPPORT;
     112                 :           0 :       *h_errnop = HOST_NOT_FOUND;
     113                 :           0 :       return NSS_STATUS_UNAVAIL;
     114                 :             :     }
     115                 :             : 
     116                 :             :   /* Define certain well-known domains with well-known resolutions, which we can
     117                 :             :    * then use in the unit tests to check results. The actual IP addresses are
     118                 :             :    * arbitrary and meaningless, they just need to be distinct between domains.
     119                 :             :    */
     120         [ +  + ]:          44 :   if (strcmp (name, "always-resolves.com") == 0)
     121                 :             :     {
     122                 :           2 :       result_name = name;
     123                 :           2 :       result_addr_str = "1.2.3.4";
     124                 :           2 :       result_addr6_str = "2001:0DB8:AC10:FE01::";
     125                 :             :     }
     126         [ +  + ]:          42 :   else if (strcmp (name, "never-resolves.com") == 0)
     127                 :             :     {
     128                 :           6 :       result_name = name;
     129                 :           6 :       result_addr_str = NULL;
     130                 :           6 :       result_addr6_str = NULL;
     131                 :             :     }
     132         [ -  + ]:          36 :   else if (strcmp (name, "redirects.com") == 0)
     133                 :             :     {
     134                 :           0 :       result_name = "safe.redirects.com";
     135                 :           0 :       result_addr_str = "1.2.3.5";
     136                 :           0 :       result_addr6_str = "2001:0DB8:AC10:FE02::";
     137                 :             :     }
     138                 :             : 
     139   [ +  +  +  +  :          44 :   if (result_name != NULL && result_addr_str != NULL && result_addr6_str != NULL)
                   +  - ]
     140                 :             :     {
     141                 :           2 :       size_t buffer_offset = 0;
     142         [ +  + ]:           2 :       size_t h_length = (af == AF_INET6) ? sizeof (struct in6_addr) : sizeof (struct in_addr);
     143                 :             : 
     144                 :             :       /* Check the buffer size first. */
     145         [ -  + ]:           2 :       if (buffer_len < align_as_pointer (strlen (result_name) + 1) + align_as_pointer (sizeof (void *)) + align_as_pointer (sizeof (void *) * 2) + align_as_pointer (h_length))
     146                 :             :         {
     147                 :           0 :           *errnop = ERANGE;
     148                 :           0 :           *h_errnop = NO_RECOVERY;
     149                 :           0 :           return NSS_STATUS_TRYAGAIN;
     150                 :             :         }
     151                 :             : 
     152                 :             :       /* Build the result. Even though we never set any h_aliases, tools like
     153                 :             :        * `getent` expect a non-NULL (though potentially empty) array. */
     154   [ +  -  -  + ]:           4 :       if (inet_pton (AF_INET, result_addr_str, &result_addr) != 1 ||
     155                 :           2 :           inet_pton (AF_INET6, result_addr6_str, &result_addr6) != 1)
     156                 :           0 :         assert (0);
     157                 :             : 
     158                 :           2 :       strcpy (buffer, result_name);
     159                 :           2 :       result->h_name = buffer;
     160                 :           2 :       buffer_offset = align_as_pointer (strlen (result_name) + 1);
     161                 :             : 
     162                 :           2 :       result->h_aliases = (char **) (buffer + buffer_offset);
     163                 :           2 :       buffer_offset += align_as_pointer (sizeof (void *));
     164                 :           2 :       result->h_aliases[0] = NULL;
     165                 :             : 
     166                 :           2 :       result->h_addrtype = af;
     167                 :           2 :       result->h_length = h_length;
     168                 :             : 
     169                 :           2 :       result->h_addr_list = (char **) (buffer + buffer_offset);
     170                 :           2 :       buffer_offset += align_as_pointer (sizeof (void *) * 2);
     171         [ +  + ]:           2 :       memcpy (buffer + buffer_offset, (af == AF_INET6) ? (char *) &result_addr6 : (char *) &result_addr, result->h_length);
     172                 :           2 :       result->h_addr_list[0] = buffer + buffer_offset;
     173                 :           2 :       buffer_offset += align_as_pointer (result->h_length);
     174                 :           2 :       result->h_addr_list[1] = NULL;
     175                 :             : 
     176         [ -  + ]:           2 :       assert (buffer_offset <= buffer_len);
     177                 :             : 
     178         [ -  + ]:           2 :       if (ttlp != NULL)
     179                 :           0 :         *ttlp = 0;
     180         [ -  + ]:           2 :       if (canonp != NULL)
     181                 :           0 :         *canonp = result->h_name;
     182                 :             : 
     183                 :           2 :       *errnop = 0;
     184                 :           2 :       *h_errnop = 0;
     185                 :           2 :       return NSS_STATUS_SUCCESS;
     186                 :             :     }
     187                 :             :   else
     188                 :             :     {
     189                 :             :       /* Not found in the filter list, so let another module actually resolve it. */
     190                 :          42 :       *errnop = EINVAL;
     191                 :          42 :       *h_errnop = NO_ADDRESS;
     192                 :          42 :       return NSS_STATUS_NOTFOUND;
     193                 :             :     }
     194                 :             : }
     195                 :             : 
     196                 :             : enum nss_status
     197                 :          44 : _nss_hardcoded_gethostbyname2_r (const char     *name,
     198                 :             :                                  int             af,
     199                 :             :                                  struct hostent *result,
     200                 :             :                                  char           *buffer,
     201                 :             :                                  size_t          buffer_len,
     202                 :             :                                  int            *errnop,
     203                 :             :                                  int            *h_errnop)
     204                 :             : {
     205                 :          44 :   return _nss_hardcoded_gethostbyname3_r (name, af, result, buffer, buffer_len,
     206                 :             :                                           errnop, h_errnop, NULL, NULL);
     207                 :             : }
        

Generated by: LCOV version 2.0-1