PostgreSQL Source Code git master
ip.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * ip.c
4 * IPv6-aware network access.
5 *
6 * Portions Copyright (c) 1996-2025, 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#include <netinet/tcp.h>
32#include <arpa/inet.h>
33#include <sys/file.h>
34
35#include "common/ip.h"
36
37
38
39static int getaddrinfo_unix(const char *path,
40 const struct addrinfo *hintsp,
41 struct addrinfo **result);
42
43static int getnameinfo_unix(const struct sockaddr_un *sa, int salen,
44 char *node, int nodelen,
45 char *service, int servicelen,
46 int flags);
47
48
49/*
50 * pg_getaddrinfo_all - get address info for Unix, IPv4 and IPv6 sockets
51 *
52 * The API of this routine differs from the standard getaddrinfo() definition
53 * in that it requires a valid hintp, a null pointer is not allowed.
54 */
55int
56pg_getaddrinfo_all(const char *hostname, const char *servname,
57 const struct addrinfo *hintp, struct addrinfo **result)
58{
59 int rc;
60
61 /* not all versions of getaddrinfo() zero *result on failure */
62 *result = NULL;
63
64 if (hintp->ai_family == AF_UNIX)
65 return getaddrinfo_unix(servname, hintp, result);
66
67 /* NULL has special meaning to getaddrinfo(). */
68 rc = getaddrinfo((!hostname || hostname[0] == '\0') ? NULL : hostname,
69 servname, hintp, result);
70
71 return rc;
72}
73
74
75/*
76 * pg_freeaddrinfo_all - free addrinfo structures for IPv4, IPv6, or Unix
77 *
78 * Note: the ai_family field of the original hint structure must be passed
79 * so that we can tell whether the addrinfo struct was built by the system's
80 * getaddrinfo() routine or our own getaddrinfo_unix() routine. Some versions
81 * of getaddrinfo() might be willing to return AF_UNIX addresses, so it's
82 * not safe to look at ai_family in the addrinfo itself.
83 */
84void
85pg_freeaddrinfo_all(int hint_ai_family, struct addrinfo *ai)
86{
87 if (hint_ai_family == AF_UNIX)
88 {
89 /* struct was built by getaddrinfo_unix (see pg_getaddrinfo_all) */
90 while (ai != NULL)
91 {
92 struct addrinfo *p = ai;
93
94 ai = ai->ai_next;
95 free(p->ai_addr);
96 free(p);
97 }
98 }
99 else
100 {
101 /* struct was built by getaddrinfo() */
102 if (ai != NULL)
103 freeaddrinfo(ai);
104 }
105}
106
107
108/*
109 * pg_getnameinfo_all - get name info for Unix, IPv4 and IPv6 sockets
110 *
111 * The API of this routine differs from the standard getnameinfo() definition
112 * in two ways: first, the addr parameter is declared as sockaddr_storage
113 * rather than struct sockaddr, and second, the node and service fields are
114 * guaranteed to be filled with something even on failure return.
115 */
116int
117pg_getnameinfo_all(const struct sockaddr_storage *addr, int salen,
118 char *node, int nodelen,
119 char *service, int servicelen,
120 int flags)
121{
122 int rc;
123
124 if (addr && addr->ss_family == AF_UNIX)
125 rc = getnameinfo_unix((const struct sockaddr_un *) addr, salen,
126 node, nodelen,
127 service, servicelen,
128 flags);
129 else
130 rc = getnameinfo((const struct sockaddr *) addr, salen,
131 node, nodelen,
132 service, servicelen,
133 flags);
134
135 if (rc != 0)
136 {
137 if (node)
138 strlcpy(node, "???", nodelen);
139 if (service)
140 strlcpy(service, "???", servicelen);
141 }
142
143 return rc;
144}
145
146
147/* -------
148 * getaddrinfo_unix - get unix socket info using IPv6-compatible API
149 *
150 * Bugs: only one addrinfo is set even though hintsp is NULL or
151 * ai_socktype is 0
152 * AI_CANONNAME is not supported.
153 * -------
154 */
155static int
156getaddrinfo_unix(const char *path, const struct addrinfo *hintsp,
157 struct addrinfo **result)
158{
159 struct addrinfo hints = {0};
160 struct addrinfo *aip;
161 struct sockaddr_un *unp;
162
163 *result = NULL;
164
165 if (strlen(path) >= sizeof(unp->sun_path))
166 return EAI_FAIL;
167
168 if (hintsp == NULL)
169 {
170 hints.ai_family = AF_UNIX;
171 hints.ai_socktype = SOCK_STREAM;
172 }
173 else
174 memcpy(&hints, hintsp, sizeof(hints));
175
176 if (hints.ai_socktype == 0)
177 hints.ai_socktype = SOCK_STREAM;
178
179 if (hints.ai_family != AF_UNIX)
180 {
181 /* shouldn't have been called */
182 return EAI_FAIL;
183 }
184
185 aip = calloc(1, sizeof(struct addrinfo));
186 if (aip == NULL)
187 return EAI_MEMORY;
188
189 unp = calloc(1, sizeof(struct sockaddr_un));
190 if (unp == NULL)
191 {
192 free(aip);
193 return EAI_MEMORY;
194 }
195
196 aip->ai_family = AF_UNIX;
197 aip->ai_socktype = hints.ai_socktype;
198 aip->ai_protocol = hints.ai_protocol;
199 aip->ai_next = NULL;
200 aip->ai_canonname = NULL;
201 *result = aip;
202
203 unp->sun_family = AF_UNIX;
204 aip->ai_addr = (struct sockaddr *) unp;
205 aip->ai_addrlen = sizeof(struct sockaddr_un);
206
207 strcpy(unp->sun_path, path);
208
209 /*
210 * If the supplied path starts with @, replace that with a zero byte for
211 * the internal representation. In that mode, the entire sun_path is the
212 * address, including trailing zero bytes. But we set the address length
213 * to only include the length of the original string. That way the
214 * trailing zero bytes won't show up in any network or socket lists of the
215 * operating system. This is just a convention, also followed by other
216 * packages.
217 */
218 if (path[0] == '@')
219 {
220 unp->sun_path[0] = '\0';
221 aip->ai_addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(path);
222 }
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 /*
253 * Check whether it looks like an abstract socket, but it could also
254 * just be an empty string.
255 */
256 if (sa->sun_path[0] == '\0' && sa->sun_path[1] != '\0')
257 ret = snprintf(service, servicelen, "@%s", sa->sun_path + 1);
258 else
259 ret = snprintf(service, servicelen, "%s", sa->sun_path);
260 if (ret < 0 || ret >= servicelen)
261 return EAI_MEMORY;
262 }
263
264 return 0;
265}
#define calloc(a, b)
Definition: header.h:55
#define free(a)
Definition: header.h:65
void pg_freeaddrinfo_all(int hint_ai_family, struct addrinfo *ai)
Definition: ip.c:85
int pg_getnameinfo_all(const struct sockaddr_storage *addr, int salen, char *node, int nodelen, char *service, int servicelen, int flags)
Definition: ip.c:117
static int getnameinfo_unix(const struct sockaddr_un *sa, int salen, char *node, int nodelen, char *service, int servicelen, int flags)
Definition: ip.c:231
static int getaddrinfo_unix(const char *path, const struct addrinfo *hintsp, struct addrinfo **result)
Definition: ip.c:156
int pg_getaddrinfo_all(const char *hostname, const char *servname, const struct addrinfo *hintp, struct addrinfo **result)
Definition: ip.c:56
static char * hostname
Definition: pg_regress.c:114
#define snprintf
Definition: port.h:239
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
Definition: un.h:12
unsigned short sun_family
Definition: un.h:13
char sun_path[108]
Definition: un.h:14