1/*-------------------------------------------------------------------------
2 *
3 * ip.c
4 * IPv6-aware network access.
5 *
6 * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 *
10 * IDENTIFICATION
11 * src/common/ip.c
12 *
13 * This file and the IPV6 implementation were initially provided by
14 * Nigel Kukard <nkukard@lbsd.net>, Linux Based Systems Design
15 * http://www.lbsd.net.
16 *
17 *-------------------------------------------------------------------------
18 */
19
20#ifndef FRONTEND
21#include "postgres.h"
22#else
23#include "postgres_fe.h"
24#endif
25
26#include <unistd.h>
27#include <sys/stat.h>
28#include <sys/socket.h>
29#include <netdb.h>
30#include <netinet/in.h>
31#ifdef HAVE_NETINET_TCP_H
32#include <netinet/tcp.h>
33#endif
34#include <arpa/inet.h>
35#include <sys/file.h>
36
37#include "common/ip.h"
38
39
40
41#ifdef HAVE_UNIX_SOCKETS
42static int getaddrinfo_unix(const char *path,
43 const struct addrinfo *hintsp,
44 struct addrinfo **result);
45
46static int getnameinfo_unix(const struct sockaddr_un *sa, int salen,
47 char *node, int nodelen,
48 char *service, int servicelen,
49 int flags);
50#endif
51
52
53/*
54 * pg_getaddrinfo_all - get address info for Unix, IPv4 and IPv6 sockets
55 */
56int
57pg_getaddrinfo_all(const char *hostname, const char *servname,
58 const struct addrinfo *hintp, struct addrinfo **result)
59{
60 int rc;
61
62 /* not all versions of getaddrinfo() zero *result on failure */
63 *result = NULL;
64
65#ifdef HAVE_UNIX_SOCKETS
66 if (hintp->ai_family == AF_UNIX)
67 return getaddrinfo_unix(servname, hintp, result);
68#endif
69
70 /* NULL has special meaning to getaddrinfo(). */
71 rc = getaddrinfo((!hostname || hostname[0] == '\0') ? NULL : hostname,
72 servname, hintp, result);
73
74 return rc;
75}
76
77
78/*
79 * pg_freeaddrinfo_all - free addrinfo structures for IPv4, IPv6, or Unix
80 *
81 * Note: the ai_family field of the original hint structure must be passed
82 * so that we can tell whether the addrinfo struct was built by the system's
83 * getaddrinfo() routine or our own getaddrinfo_unix() routine. Some versions
84 * of getaddrinfo() might be willing to return AF_UNIX addresses, so it's
85 * not safe to look at ai_family in the addrinfo itself.
86 */
87void
88pg_freeaddrinfo_all(int hint_ai_family, struct addrinfo *ai)
89{
90#ifdef HAVE_UNIX_SOCKETS
91 if (hint_ai_family == AF_UNIX)
92 {
93 /* struct was built by getaddrinfo_unix (see pg_getaddrinfo_all) */
94 while (ai != NULL)
95 {
96 struct addrinfo *p = ai;
97
98 ai = ai->ai_next;
99 free(p->ai_addr);
100 free(p);
101 }
102 }
103 else
104#endif /* HAVE_UNIX_SOCKETS */
105 {
106 /* struct was built by getaddrinfo() */
107 if (ai != NULL)
108 freeaddrinfo(ai);
109 }
110}
111
112
113/*
114 * pg_getnameinfo_all - get name info for Unix, IPv4 and IPv6 sockets
115 *
116 * The API of this routine differs from the standard getnameinfo() definition
117 * in two ways: first, the addr parameter is declared as sockaddr_storage
118 * rather than struct sockaddr, and second, the node and service fields are
119 * guaranteed to be filled with something even on failure return.
120 */
121int
122pg_getnameinfo_all(const struct sockaddr_storage *addr, int salen,
123 char *node, int nodelen,
124 char *service, int servicelen,
125 int flags)
126{
127 int rc;
128
129#ifdef HAVE_UNIX_SOCKETS
130 if (addr && addr->ss_family == AF_UNIX)
131 rc = getnameinfo_unix((const struct sockaddr_un *) addr, salen,
132 node, nodelen,
133 service, servicelen,
134 flags);
135 else
136#endif
137 rc = getnameinfo((const struct sockaddr *) addr, salen,
138 node, nodelen,
139 service, servicelen,
140 flags);
141
142 if (rc != 0)
143 {
144 if (node)
145 strlcpy(node, "???", nodelen);
146 if (service)
147 strlcpy(service, "???", servicelen);
148 }
149
150 return rc;
151}
152
153
154#if defined(HAVE_UNIX_SOCKETS)
155
156/* -------
157 * getaddrinfo_unix - get unix socket info using IPv6-compatible API
158 *
159 * Bugs: only one addrinfo is set even though hintsp is NULL or
160 * ai_socktype is 0
161 * AI_CANONNAME is not supported.
162 * -------
163 */
164static int
165getaddrinfo_unix(const char *path, const struct addrinfo *hintsp,
166 struct addrinfo **result)
167{
168 struct addrinfo hints;
169 struct addrinfo *aip;
170 struct sockaddr_un *unp;
171
172 *result = NULL;
173
174 MemSet(&hints, 0, sizeof(hints));
175
176 if (strlen(path) >= sizeof(unp->sun_path))
177 return EAI_FAIL;
178
179 if (hintsp == NULL)
180 {
181 hints.ai_family = AF_UNIX;
182 hints.ai_socktype = SOCK_STREAM;
183 }
184 else
185 memcpy(&hints, hintsp, sizeof(hints));
186
187 if (hints.ai_socktype == 0)
188 hints.ai_socktype = SOCK_STREAM;
189
190 if (hints.ai_family != AF_UNIX)
191 {
192 /* shouldn't have been called */
193 return EAI_FAIL;
194 }
195
196 aip = calloc(1, sizeof(struct addrinfo));
197 if (aip == NULL)
198 return EAI_MEMORY;
199
200 unp = calloc(1, sizeof(struct sockaddr_un));
201 if (unp == NULL)
202 {
203 free(aip);
204 return EAI_MEMORY;
205 }
206
207 aip->ai_family = AF_UNIX;
208 aip->ai_socktype = hints.ai_socktype;
209 aip->ai_protocol = hints.ai_protocol;
210 aip->ai_next = NULL;
211 aip->ai_canonname = NULL;
212 *result = aip;
213
214 unp->sun_family = AF_UNIX;
215 aip->ai_addr = (struct sockaddr *) unp;
216 aip->ai_addrlen = sizeof(struct sockaddr_un);
217
218 strcpy(unp->sun_path, path);
219
220#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
221 unp->sun_len = sizeof(struct sockaddr_un);
222#endif
223
224 return 0;
225}
226
227/*
228 * Convert an address to a hostname.
229 */
230static int
231getnameinfo_unix(const struct sockaddr_un *sa, int salen,
232 char *node, int nodelen,
233 char *service, int servicelen,
234 int flags)
235{
236 int ret;
237
238 /* Invalid arguments. */
239 if (sa == NULL || sa->sun_family != AF_UNIX ||
240 (node == NULL && service == NULL))
241 return EAI_FAIL;
242
243 if (node)
244 {
245 ret = snprintf(node, nodelen, "%s", "[local]");
246 if (ret < 0 || ret >= nodelen)
247 return EAI_MEMORY;
248 }
249
250 if (service)
251 {
252 ret = snprintf(service, servicelen, "%s", sa->sun_path);
253 if (ret < 0 || ret >= servicelen)
254 return EAI_MEMORY;
255 }
256
257 return 0;
258}
259#endif /* HAVE_UNIX_SOCKETS */
260