1/* Convert struct addrinfo values to a string.
2 Copyright (C) 2016-2017 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 <support/support.h>
25#include <support/xmemstream.h>
26
27static size_t
28socket_address_length (int family)
29{
30 switch (family)
31 {
32 case AF_INET:
33 return sizeof (struct sockaddr_in);
34 case AF_INET6:
35 return sizeof (struct sockaddr_in6);
36 default:
37 return -1;
38 }
39}
40
41static void
42format_ai_flags_1 (FILE *out, struct addrinfo *ai, int flag, const char *name,
43 int * flags_printed)
44{
45 if ((ai->ai_flags & flag) != 0)
46 fprintf (out, " %s", name);
47 *flags_printed |= flag;
48}
49
50static void
51format_ai_flags (FILE *out, struct addrinfo *ai)
52{
53 if (ai == NULL)
54 return;
55
56 if (ai->ai_flags != 0)
57 {
58 fprintf (out, "flags:");
59 int flags_printed = 0;
60#define FLAG(flag) format_ai_flags_1 (out, ai, flag, #flag, &flags_printed)
61 FLAG (AI_PASSIVE);
62 FLAG (AI_CANONNAME);
63 FLAG (AI_NUMERICHOST);
64 FLAG (AI_V4MAPPED);
65 FLAG (AI_ALL);
66 FLAG (AI_ADDRCONFIG);
67 FLAG (AI_IDN);
68 FLAG (AI_CANONIDN);
69 FLAG (AI_IDN_ALLOW_UNASSIGNED);
70 FLAG (AI_IDN_USE_STD3_ASCII_RULES);
71 FLAG (AI_NUMERICSERV);
72#undef FLAG
73 int remaining = ai->ai_flags & ~flags_printed;
74 if (remaining != 0)
75 fprintf (out, " %08x", remaining);
76 fprintf (out, "\n");
77 }
78
79 /* Report flag mismatches within the list. */
80 int flags = ai->ai_flags;
81 int index = 1;
82 ai = ai->ai_next;
83 while (ai != NULL)
84 {
85 if (ai->ai_flags != flags)
86 fprintf (out, "error: flags at %d: 0x%x expected, 0x%x actual\n",
87 index, flags, ai->ai_flags);
88 ai = ai->ai_next;
89 ++index;
90 }
91}
92
93static void
94format_ai_canonname (FILE *out, struct addrinfo *ai)
95{
96 if (ai == NULL)
97 return;
98 if (ai->ai_canonname != NULL)
99 fprintf (out, "canonname: %s\n", ai->ai_canonname);
100
101 /* Report incorrectly set ai_canonname fields on subsequent list
102 entries. */
103 int index = 1;
104 ai = ai->ai_next;
105 while (ai != NULL)
106 {
107 if (ai->ai_canonname != NULL)
108 fprintf (out, "error: canonname set at %d: %s\n",
109 index, ai->ai_canonname);
110 ai = ai->ai_next;
111 ++index;
112 }
113}
114
115static void
116format_ai_one (FILE *out, struct addrinfo *ai)
117{
118 {
119 char type_buf[32];
120 const char *type_str;
121 char proto_buf[32];
122 const char *proto_str;
123
124 /* ai_socktype */
125 switch (ai->ai_socktype)
126 {
127 case SOCK_RAW:
128 type_str = "RAW";
129 break;
130 case SOCK_DGRAM:
131 type_str = "DGRAM";
132 break;
133 case SOCK_STREAM:
134 type_str = "STREAM";
135 break;
136 default:
137 snprintf (type_buf, sizeof (type_buf), "%d", ai->ai_socktype);
138 type_str = type_buf;
139 }
140
141 /* ai_protocol */
142 switch (ai->ai_protocol)
143 {
144 case IPPROTO_IP:
145 proto_str = "IP";
146 break;
147 case IPPROTO_UDP:
148 proto_str = "UDP";
149 break;
150 case IPPROTO_TCP:
151 proto_str = "TCP";
152 break;
153 default:
154 snprintf (proto_buf, sizeof (proto_buf), "%d", ai->ai_protocol);
155 proto_str = proto_buf;
156 }
157 fprintf (out, "address: %s/%s", type_str, proto_str);
158 }
159
160 /* ai_addrlen */
161 if (ai->ai_addrlen != socket_address_length (ai->ai_family))
162 {
163 char *family = support_format_address_family (ai->ai_family);
164 fprintf (out, "error: invalid address length %d for %s\n",
165 ai->ai_addrlen, family);
166 free (family);
167 }
168
169 /* ai_addr */
170 {
171 char buf[128];
172 uint16_t port;
173 const char *ret;
174 switch (ai->ai_family)
175 {
176 case AF_INET:
177 {
178 struct sockaddr_in *sin = (struct sockaddr_in *) ai->ai_addr;
179 ret = inet_ntop (AF_INET, &sin->sin_addr, buf, sizeof (buf));
180 port = sin->sin_port;
181 }
182 break;
183 case AF_INET6:
184 {
185 struct sockaddr_in6 *sin = (struct sockaddr_in6 *) ai->ai_addr;
186 ret = inet_ntop (AF_INET6, &sin->sin6_addr, buf, sizeof (buf));
187 port = sin->sin6_port;
188 }
189 break;
190 default:
191 errno = EAFNOSUPPORT;
192 ret = NULL;
193 }
194 if (ret == NULL)
195 fprintf (out, "error: inet_top failed: %m\n");
196 else
197 fprintf (out, " %s %u\n", buf, ntohs (port));
198 }
199}
200
201/* Format all the addresses in one address family. */
202static void
203format_ai_family (FILE *out, struct addrinfo *ai, int family)
204{
205 while (ai)
206 {
207 if (ai->ai_family == family)
208 format_ai_one (out, ai);
209 ai = ai->ai_next;
210 }
211}
212
213char *
214support_format_addrinfo (struct addrinfo *ai, int ret)
215{
216 int errno_copy = errno;
217
218 struct xmemstream mem;
219 xopen_memstream (&mem);
220 if (ret != 0)
221 {
222 fprintf (mem.out, "error: %s\n", gai_strerror (ret));
223 if (ret == EAI_SYSTEM)
224 {
225 errno = errno_copy;
226 fprintf (mem.out, "error: %m\n");
227 }
228 }
229 else
230 {
231 format_ai_flags (mem.out, ai);
232 format_ai_canonname (mem.out, ai);
233 format_ai_family (mem.out, ai, AF_INET);
234 format_ai_family (mem.out, ai, AF_INET6);
235 }
236
237 xfclose_memstream (&mem);
238 return mem.buffer;
239}
240