1 /* Convert struct addrinfo values to a string.
2    Copyright (C) 2016-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 #include <support/format_nss.h>
20 
21 #include <arpa/inet.h>
22 #include <errno.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <support/support.h>
26 #include <support/xmemstream.h>
27 
28 static size_t
socket_address_length(int family)29 socket_address_length (int family)
30 {
31   switch (family)
32     {
33     case AF_INET:
34       return sizeof (struct sockaddr_in);
35     case AF_INET6:
36       return sizeof (struct sockaddr_in6);
37     default:
38       return -1;
39     }
40 }
41 
42 static void
format_ai_flags_1(FILE * out,struct addrinfo * ai,int flag,const char * name,int * flags_printed)43 format_ai_flags_1 (FILE *out, struct addrinfo *ai, int flag, const char *name,
44                    int * flags_printed)
45 {
46   if ((ai->ai_flags & flag) != 0)
47     fprintf (out, " %s", name);
48   *flags_printed |= flag;
49 }
50 
51 static void
format_ai_flags(FILE * out,struct addrinfo * ai)52 format_ai_flags (FILE *out, struct addrinfo *ai)
53 {
54   if (ai == NULL)
55     return;
56 
57   if (ai->ai_flags != 0)
58     {
59       fprintf (out, "flags:");
60       int flags_printed = 0;
61 #define FLAG(flag) format_ai_flags_1 (out, ai, flag, #flag, &flags_printed)
62       FLAG (AI_PASSIVE);
63       FLAG (AI_CANONNAME);
64       FLAG (AI_NUMERICHOST);
65       FLAG (AI_V4MAPPED);
66       FLAG (AI_ALL);
67       FLAG (AI_ADDRCONFIG);
68       FLAG (AI_IDN);
69       FLAG (AI_CANONIDN);
70       FLAG (AI_NUMERICSERV);
71 #undef FLAG
72       int remaining = ai->ai_flags & ~flags_printed;
73       if (remaining != 0)
74         fprintf (out, " %08x", remaining);
75       fprintf (out, "\n");
76     }
77 
78   /* Report flag mismatches within the list.  */
79   int flags = ai->ai_flags;
80   int index = 1;
81   ai = ai->ai_next;
82   while (ai != NULL)
83     {
84       if (ai->ai_flags != flags)
85         fprintf (out, "error: flags at %d: 0x%x expected, 0x%x actual\n",
86                  index, flags, ai->ai_flags);
87       ai = ai->ai_next;
88       ++index;
89     }
90 }
91 
92 static void
format_ai_canonname(FILE * out,struct addrinfo * ai)93 format_ai_canonname (FILE *out, struct addrinfo *ai)
94 {
95   if (ai == NULL)
96     return;
97   if (ai->ai_canonname != NULL)
98     fprintf (out, "canonname: %s\n", ai->ai_canonname);
99 
100   /* Report incorrectly set ai_canonname fields on subsequent list
101      entries.  */
102   int index = 1;
103   ai = ai->ai_next;
104   while (ai != NULL)
105     {
106       if (ai->ai_canonname != NULL)
107         fprintf (out, "error: canonname set at %d: %s\n",
108                  index, ai->ai_canonname);
109       ai = ai->ai_next;
110       ++index;
111     }
112 }
113 
114 static void
format_ai_one(FILE * out,struct addrinfo * ai)115 format_ai_one (FILE *out, struct addrinfo *ai)
116 {
117   {
118     char type_buf[32];
119     const char *type_str;
120     char proto_buf[32];
121     const char *proto_str;
122 
123     /* ai_socktype */
124     switch (ai->ai_socktype)
125       {
126       case SOCK_RAW:
127         type_str = "RAW";
128         break;
129       case SOCK_DGRAM:
130         type_str = "DGRAM";
131         break;
132       case SOCK_STREAM:
133         type_str = "STREAM";
134         break;
135       default:
136         snprintf (type_buf, sizeof (type_buf), "%d", ai->ai_socktype);
137         type_str = type_buf;
138       }
139 
140     /* ai_protocol */
141     switch (ai->ai_protocol)
142       {
143       case IPPROTO_IP:
144         proto_str = "IP";
145         break;
146       case IPPROTO_UDP:
147         proto_str = "UDP";
148         break;
149       case IPPROTO_TCP:
150         proto_str = "TCP";
151         break;
152       default:
153         snprintf (proto_buf, sizeof (proto_buf), "%d", ai->ai_protocol);
154         proto_str = proto_buf;
155       }
156     fprintf (out, "address: %s/%s", type_str, proto_str);
157   }
158 
159   /* ai_addrlen */
160   if (ai->ai_addrlen != socket_address_length (ai->ai_family))
161     {
162       char *family = support_format_address_family (ai->ai_family);
163       fprintf (out, "error: invalid address length %d for %s\n",
164                ai->ai_addrlen, family);
165       free (family);
166     }
167 
168   /* ai_addr */
169   {
170     char buf[128];
171     uint16_t port;
172     const char *ret;
173     switch (ai->ai_family)
174       {
175       case AF_INET:
176         {
177           struct sockaddr_in *sin = (struct sockaddr_in *) ai->ai_addr;
178           ret = inet_ntop (AF_INET, &sin->sin_addr, buf, sizeof (buf));
179           port = sin->sin_port;
180         }
181         break;
182       case AF_INET6:
183         {
184           struct sockaddr_in6 *sin = (struct sockaddr_in6 *) ai->ai_addr;
185           ret = inet_ntop (AF_INET6, &sin->sin6_addr, buf, sizeof (buf));
186           port = sin->sin6_port;
187         }
188         break;
189       default:
190         errno = EAFNOSUPPORT;
191         ret = NULL;
192       }
193     if (ret == NULL)
194         fprintf (out, "error: inet_top failed: %m\n");
195     else
196       fprintf (out, " %s %u\n", buf, ntohs (port));
197   }
198 }
199 
200 /* Format all the addresses in one address family.  */
201 static void
format_ai_family(FILE * out,struct addrinfo * ai,int family)202 format_ai_family (FILE *out, struct addrinfo *ai, int family)
203 {
204   while (ai)
205     {
206       if (ai->ai_family == family)
207         format_ai_one (out, ai);
208       ai = ai->ai_next;
209     }
210 }
211 
212 char *
support_format_addrinfo(struct addrinfo * ai,int ret)213 support_format_addrinfo (struct addrinfo *ai, int ret)
214 {
215   int errno_copy = errno;
216 
217   struct xmemstream mem;
218   xopen_memstream (&mem);
219   if (ret != 0)
220     {
221       const char *errmsg = gai_strerror (ret);
222       if (strcmp (errmsg, "Unknown error") == 0)
223         fprintf (mem.out, "error: Unknown error %d\n", ret);
224       else
225         fprintf (mem.out, "error: %s\n", errmsg);
226       if (ret == EAI_SYSTEM)
227         {
228           errno = errno_copy;
229           fprintf (mem.out, "error: %m\n");
230         }
231     }
232   else
233     {
234       format_ai_flags (mem.out, ai);
235       format_ai_canonname (mem.out, ai);
236       format_ai_family (mem.out, ai, AF_INET);
237       format_ai_family (mem.out, ai, AF_INET6);
238     }
239 
240   xfclose_memstream (&mem);
241   return mem.buffer;
242 }
243