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