[RFC,2/4,BZ,17083] NSS: Parse zone identifier of IPv6 addresses in /etc/hosts

Message ID 20170905160530.19525-3-stlman@poczta.fm
State New, archived
Headers

Commit Message

Łukasz Stelmach Sept. 5, 2017, 4:05 p.m. UTC
  Parse link-local IPv6 addresses in /etc/hosts, which contain zone
identifiers as described in RFC 4007.

Signed-off-by: Łukasz Stelmach <stlman@poczta.fm>
---
 nss/nss_files/files-hosts.c | 54 ++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 46 insertions(+), 8 deletions(-)
  

Comments

Florian Weimer Sept. 12, 2017, 10 a.m. UTC | #1
On 09/05/2017 06:05 PM, Łukasz Stelmach wrote:
> +	   ret = getaddrinfo(addr, NULL, &hints, &res);

That makes me rather nervous due to the potential for recursion.  Can we
factor out the AI_NUMERICHOST code into a separate function instead?

Thanks,
Florian
  
Łukasz Stelmach Sept. 12, 2017, 10:40 a.m. UTC | #2
Dnia 12 września 2017 12:00:28 CEST, Florian Weimer <fweimer@redhat.com> napisał(a):
>On 09/05/2017 06:05 PM, Łukasz Stelmach wrote:
>> +	   ret = getaddrinfo(addr, NULL, &hints, &res);
>
>That makes me rather nervous due to the potential for recursion.  Can
>we
>factor out the AI_NUMERICHOST code into a separate function instead?

It appears there is little code to be factored-out in gaih_inet(),
which is the workhorse. There several attempts to parse
a hostname as numeric and the flag prevents from going
further.

Yet another issue I can see, is that the factored out code
would have to be public (?) to be available to NSS module.

Now I can see there is __inet6_scopeid__pton(). I'll try it but I am
afraid  probably it won't be visible in the NSS module.
  
Florian Weimer Sept. 12, 2017, 10:51 a.m. UTC | #3
On 09/12/2017 12:40 PM, Łukasz Stelmach wrote:
> Yet another issue I can see, is that the factored out code
> would have to be public (?) to be available to NSS module.

We have GLIBC_PRIVATE for that.

> Now I can see there is __inet6_scopeid__pton(). I'll try it but I am
> afraid  probably it won't be visible in the NSS module.

It's exported because getaddrinfo is also compiled into nscd (outside
libc) and it has to reference __inet6_scopeid__pton somehow.

Thanks,
Florian
  
Łukasz Stelmach Sept. 13, 2017, 8:19 a.m. UTC | #4
Dnia 12 września 2017 12:51:19 CEST, Florian Weimer <fweimer@redhat.com> napisał(a):
>On 09/12/2017 12:40 PM, Łukasz Stelmach wrote:
>> Now I can see there is __inet6_scopeid__pton(). I'll try it but I am
>> afraid  probably it won't be visible in the NSS module.
>
>It's exported because getaddrinfo is also compiled into nscd (outside
>libc) and it has to reference __inet6_scopeid__pton somehow.

Yes it is visible.

I have identified the code that does what I need. This is more or less the else-if
in getaddrinfo.c:531 (commit 620ff9eea6).

Practical question: what would be the preferred way of (re)using thiscode in files-hosts.c?
  

Patch

diff --git a/nss/nss_files/files-hosts.c b/nss/nss_files/files-hosts.c
index bccb6a5780..28eb73fb2d 100644
--- a/nss/nss_files/files-hosts.c
+++ b/nss/nss_files/files-hosts.c
@@ -33,8 +33,8 @@ 
 #define DATABASE	"hosts"
 #define NEED_H_ERRNO
 
-#define EXTRA_ARGS	 , af, flags
-#define EXTRA_ARGS_DECL	 , int af, int flags
+#define EXTRA_ARGS	 , af, flags, in6_zone_id
+#define EXTRA_ARGS_DECL	 , int af, int flags, uint32_t *in6_zone_id
 
 #define ENTDATA hostent_data
 struct hostent_data
@@ -53,6 +53,9 @@  LINE_PARSER
 
    STRING_FIELD (addr, isspace, 1);
 
+   if (in6_zone_id != NULL)
+     *in6_zone_id = 0;
+
    /* Parse address.  */
    if (inet_pton (af == AF_UNSPEC ? AF_INET : af, addr, entdata->host_addr)
        > 0)
@@ -80,6 +83,40 @@  LINE_PARSER
        else if (af == AF_UNSPEC
 		&& inet_pton (AF_INET6, addr, entdata->host_addr) > 0)
 	 af = AF_INET6;
+       else if (strchr(addr, SCOPE_DELIMITER) != NULL)
+	 {
+	   /* Parse a zone identifier RFC 4007 11. */
+	   struct addrinfo hints;
+	   struct addrinfo *res;
+	   struct sockaddr_in6 *sa;
+	   int ret;
+
+	   /* Only IPv6 link-local addresses can have zone identifiers. */
+	   af = AF_INET6;
+
+	   memset(&hints, 0, sizeof(struct addrinfo));
+	   /* Don't perform any lookups. */
+	   hints.ai_flags = AI_NUMERICHOST;
+	   hints.ai_family = AF_INET6;
+
+	   ret = getaddrinfo(addr, NULL, &hints, &res);
+	   if (ret != 0)
+	     return 0;
+
+	   sa = (struct sockaddr_in6*)res->ai_addr;
+
+	   /* Zone identifiers makes sense only for link-local addresses. */
+	   if (!IN6_IS_ADDR_LINKLOCAL(sa->sin6_addr.s6_addr)) {
+	     freeaddrinfo(res);
+	     return 0;
+	   }
+
+	   if (in6_zone_id != NULL)
+	     *in6_zone_id = sa->sin6_scope_id;
+
+	   memcpy(entdata->host_addr, &sa->sin6_addr, IN6ADDRSZ);
+	   freeaddrinfo(res);
+	 }
        else
 	 /* Illegal address: ignore line.  */
 	 return 0;
@@ -99,14 +136,14 @@  LINE_PARSER
 
 #define EXTRA_ARGS_VALUE \
   , (res_use_inet6 () ? AF_INET6 : AF_INET),		      \
-  (res_use_inet6 () ? AI_V4MAPPED : 0)
+  (res_use_inet6 () ? AI_V4MAPPED : 0), NULL
 #include "files-XXX.c"
 #undef EXTRA_ARGS_VALUE
 
 /* We only need to consider IPv4 mapped addresses if the input to the
    gethostbyaddr() function is an IPv6 address.  */
 #define EXTRA_ARGS_VALUE \
-  , af, (len == IN6ADDRSZ ? AI_V4MAPPED : 0)
+  , af, (len == IN6ADDRSZ ? AI_V4MAPPED : 0), NULL
 DB_LOOKUP (hostbyaddr, ,,,
 	   {
 	     if (result->h_length == (int) len
@@ -135,7 +172,7 @@  _nss_files_gethostbyname3_r (const char *name, int af, struct hostent *result,
       int flags = (res_use_inet6 () ? AI_V4MAPPED : 0);
 
       while ((status = internal_getent (stream, result, buffer, buflen, errnop,
-					herrnop, af, flags))
+					herrnop, af, flags, NULL))
 	     == NSS_STATUS_SUCCESS)
 	{
 	  LOOKUP_NAME_CASE (h_name, h_aliases)
@@ -163,7 +200,7 @@  _nss_files_gethostbyname3_r (const char *name, int af, struct hostent *result,
 	again:
 	  while ((status = internal_getent (stream, &tmp_result_buf, tmp_buffer,
 					    tmp_buflen, errnop, herrnop, af,
-					    flags))
+					    flags, NULL))
 		 == NSS_STATUS_SUCCESS)
 	    {
 	      int matches = 1;
@@ -385,12 +422,13 @@  _nss_files_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
 	  /* Align the buffer for the next record.  */
 	  uintptr_t pad = (-(uintptr_t) buffer
 			   % __alignof__ (struct hostent_data));
+	  uint32_t in6_zone_id = 0;
 	  buffer += pad;
 	  buflen = buflen > pad ? buflen - pad : 0;
 
 	  struct hostent result;
 	  status = internal_getent (stream, &result, buffer, buflen, errnop,
-				    herrnop, AF_UNSPEC, 0);
+				    herrnop, AF_UNSPEC, 0, &in6_zone_id);
 	  if (status != NSS_STATUS_SUCCESS)
 	    break;
 
@@ -448,7 +486,7 @@  _nss_files_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
 	  got_canon = true;
 	  (*pat)->family = result.h_addrtype;
 	  memcpy ((*pat)->addr, result.h_addr_list[0], result.h_length);
-	  (*pat)->scopeid = 0;
+	  (*pat)->scopeid = in6_zone_id;
 
 	  pat = &((*pat)->next);