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 (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_one (FILE *out, struct addrinfo *ai, int *flags)
52{
53 /* ai_flags */
54 if (ai->ai_flags != *flags)
55 {
56 fprintf (out, "flags:");
57 int flags_printed = 0;
58#define FLAG(flag) format_ai_flags (out, ai, flag, #flag, &flags_printed)
59 FLAG (AI_PASSIVE);
60 FLAG (AI_CANONNAME);
61 FLAG (AI_NUMERICHOST);
62 FLAG (AI_V4MAPPED);
63 FLAG (AI_ALL);
64 FLAG (AI_ADDRCONFIG);
65 FLAG (AI_IDN);
66 FLAG (AI_CANONIDN);
67 FLAG (AI_IDN_ALLOW_UNASSIGNED);
68 FLAG (AI_IDN_USE_STD3_ASCII_RULES);
69 FLAG (AI_NUMERICSERV);
70#undef FLAG
71 int remaining = ai->ai_flags & ~flags_printed;
72 if (remaining != 0)
73 fprintf (out, " %08x", remaining);
74 fprintf (out, "\n");
75 *flags = ai->ai_flags;
76 }
77
78 {
79 char type_buf[32];
80 const char *type_str;
81 char proto_buf[32];
82 const char *proto_str;
83
84 /* ai_socktype */
85 switch (ai->ai_socktype)
86 {
87 case SOCK_RAW:
88 type_str = "RAW";
89 break;
90 case SOCK_DGRAM:
91 type_str = "DGRAM";
92 break;
93 case SOCK_STREAM:
94 type_str = "STREAM";
95 break;
96 default:
97 snprintf (type_buf, sizeof (type_buf), "%d", ai->ai_socktype);
98 type_str = type_buf;
99 }
100
101 /* ai_protocol */
102 switch (ai->ai_protocol)
103 {
104 case IPPROTO_IP:
105 proto_str = "IP";
106 break;
107 case IPPROTO_UDP:
108 proto_str = "UDP";
109 break;
110 case IPPROTO_TCP:
111 proto_str = "TCP";
112 break;
113 default:
114 snprintf (proto_buf, sizeof (proto_buf), "%d", ai->ai_protocol);
115 proto_str = proto_buf;
116 }
117 fprintf (out, "address: %s/%s", type_str, proto_str);
118 }
119
120 /* ai_addrlen */
121 if (ai->ai_addrlen != socket_address_length (ai->ai_family))
122 {
123 char *family = support_format_address_family (ai->ai_family);
124 fprintf (out, "error: invalid address length %d for %s\n",
125 ai->ai_addrlen, family);
126 free (family);
127 }
128
129 /* ai_addr */
130 {
131 char buf[128];
132 uint16_t port;
133 const char *ret;
134 switch (ai->ai_family)
135 {
136 case AF_INET:
137 {
138 struct sockaddr_in *sin = (struct sockaddr_in *) ai->ai_addr;
139 ret = inet_ntop (AF_INET, &sin->sin_addr, buf, sizeof (buf));
140 port = sin->sin_port;
141 }
142 break;
143 case AF_INET6:
144 {
145 struct sockaddr_in6 *sin = (struct sockaddr_in6 *) ai->ai_addr;
146 ret = inet_ntop (AF_INET6, &sin->sin6_addr, buf, sizeof (buf));
147 port = sin->sin6_port;
148 }
149 break;
150 default:
151 errno = EAFNOSUPPORT;
152 ret = NULL;
153 }
154 if (ret == NULL)
155 fprintf (out, "error: inet_top failed: %m\n");
156 else
157 fprintf (out, " %s %u\n", buf, ntohs (port));
158 }
159
160 /* ai_canonname */
161 if (ai->ai_canonname != NULL)
162 fprintf (out, "canonname: %s\n", ai->ai_canonname);
163}
164
165/* Format all the addresses in one address family. */
166static void
167format_ai_family (FILE *out, struct addrinfo *ai, int family, int *flags)
168{
169 while (ai)
170 {
171 if (ai->ai_family == family)
172 format_ai_one (out, ai, flags);
173 ai = ai->ai_next;
174 }
175}
176
177char *
178support_format_addrinfo (struct addrinfo *ai, int ret)
179{
180 int errno_copy = errno;
181
182 struct xmemstream mem;
183 xopen_memstream (&mem);
184 if (ret != 0)
185 {
186 fprintf (mem.out, "error: %s\n", gai_strerror (ret));
187 if (ret == EAI_SYSTEM)
188 {
189 errno = errno_copy;
190 fprintf (mem.out, "error: %m\n");
191 }
192 }
193 else
194 {
195 int flags = 0;
196 format_ai_family (mem.out, ai, AF_INET, &flags);
197 format_ai_family (mem.out, ai, AF_INET6, &flags);
198 }
199
200 xfclose_memstream (&mem);
201 return mem.buffer;
202}
203