1/* Convert struct addrinfo values to a string.
2 Copyright (C) 2016-2018 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 <http://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
28static size_t
29socket_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
42static void
43format_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
51static void
52format_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
92static void
93format_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
114static void
115format_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. */
201static void
202format_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
212char *
213support_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