/src/postgres/src/common/ip.c
Line | Count | Source |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * ip.c |
4 | | * IPv6-aware network access. |
5 | | * |
6 | | * Portions Copyright (c) 1996-2026, 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 | | |
39 | | static int getaddrinfo_unix(const char *path, |
40 | | const struct addrinfo *hintsp, |
41 | | struct addrinfo **result); |
42 | | |
43 | | static 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 | | */ |
55 | | int |
56 | | pg_getaddrinfo_all(const char *hostname, const char *servname, |
57 | | const struct addrinfo *hintp, struct addrinfo **result) |
58 | 0 | { |
59 | 0 | int rc; |
60 | | |
61 | | /* not all versions of getaddrinfo() zero *result on failure */ |
62 | 0 | *result = NULL; |
63 | |
|
64 | 0 | if (hintp->ai_family == AF_UNIX) |
65 | 0 | return getaddrinfo_unix(servname, hintp, result); |
66 | | |
67 | | /* NULL has special meaning to getaddrinfo(). */ |
68 | 0 | rc = getaddrinfo((!hostname || hostname[0] == '\0') ? NULL : hostname, |
69 | 0 | servname, hintp, result); |
70 | |
|
71 | 0 | return rc; |
72 | 0 | } |
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 | | */ |
84 | | void |
85 | | pg_freeaddrinfo_all(int hint_ai_family, struct addrinfo *ai) |
86 | 0 | { |
87 | 0 | if (hint_ai_family == AF_UNIX) |
88 | 0 | { |
89 | | /* struct was built by getaddrinfo_unix (see pg_getaddrinfo_all) */ |
90 | 0 | while (ai != NULL) |
91 | 0 | { |
92 | 0 | struct addrinfo *p = ai; |
93 | |
|
94 | 0 | ai = ai->ai_next; |
95 | 0 | free(p->ai_addr); |
96 | 0 | free(p); |
97 | 0 | } |
98 | 0 | } |
99 | 0 | else |
100 | 0 | { |
101 | | /* struct was built by getaddrinfo() */ |
102 | 0 | if (ai != NULL) |
103 | 0 | freeaddrinfo(ai); |
104 | 0 | } |
105 | 0 | } |
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 | | */ |
116 | | int |
117 | | pg_getnameinfo_all(const struct sockaddr_storage *addr, int salen, |
118 | | char *node, int nodelen, |
119 | | char *service, int servicelen, |
120 | | int flags) |
121 | 0 | { |
122 | 0 | int rc; |
123 | |
|
124 | 0 | if (addr && addr->ss_family == AF_UNIX) |
125 | 0 | rc = getnameinfo_unix((const struct sockaddr_un *) addr, salen, |
126 | 0 | node, nodelen, |
127 | 0 | service, servicelen, |
128 | 0 | flags); |
129 | 0 | else |
130 | 0 | rc = getnameinfo((const struct sockaddr *) addr, salen, |
131 | 0 | node, nodelen, |
132 | 0 | service, servicelen, |
133 | 0 | flags); |
134 | |
|
135 | 0 | if (rc != 0) |
136 | 0 | { |
137 | 0 | if (node) |
138 | 0 | strlcpy(node, "???", nodelen); |
139 | 0 | if (service) |
140 | 0 | strlcpy(service, "???", servicelen); |
141 | 0 | } |
142 | |
|
143 | 0 | return rc; |
144 | 0 | } |
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 | | */ |
155 | | static int |
156 | | getaddrinfo_unix(const char *path, const struct addrinfo *hintsp, |
157 | | struct addrinfo **result) |
158 | 0 | { |
159 | 0 | struct addrinfo hints = {0}; |
160 | 0 | struct addrinfo *aip; |
161 | 0 | struct sockaddr_un *unp; |
162 | |
|
163 | 0 | *result = NULL; |
164 | |
|
165 | 0 | if (strlen(path) >= sizeof(unp->sun_path)) |
166 | 0 | return EAI_FAIL; |
167 | | |
168 | 0 | if (hintsp == NULL) |
169 | 0 | { |
170 | 0 | hints.ai_family = AF_UNIX; |
171 | 0 | hints.ai_socktype = SOCK_STREAM; |
172 | 0 | } |
173 | 0 | else |
174 | 0 | memcpy(&hints, hintsp, sizeof(hints)); |
175 | |
|
176 | 0 | if (hints.ai_socktype == 0) |
177 | 0 | hints.ai_socktype = SOCK_STREAM; |
178 | |
|
179 | 0 | if (hints.ai_family != AF_UNIX) |
180 | 0 | { |
181 | | /* shouldn't have been called */ |
182 | 0 | return EAI_FAIL; |
183 | 0 | } |
184 | | |
185 | 0 | aip = calloc(1, sizeof(struct addrinfo)); |
186 | 0 | if (aip == NULL) |
187 | 0 | return EAI_MEMORY; |
188 | | |
189 | 0 | unp = calloc(1, sizeof(struct sockaddr_un)); |
190 | 0 | if (unp == NULL) |
191 | 0 | { |
192 | 0 | free(aip); |
193 | 0 | return EAI_MEMORY; |
194 | 0 | } |
195 | | |
196 | 0 | aip->ai_family = AF_UNIX; |
197 | 0 | aip->ai_socktype = hints.ai_socktype; |
198 | 0 | aip->ai_protocol = hints.ai_protocol; |
199 | 0 | aip->ai_next = NULL; |
200 | 0 | aip->ai_canonname = NULL; |
201 | 0 | *result = aip; |
202 | |
|
203 | 0 | unp->sun_family = AF_UNIX; |
204 | 0 | aip->ai_addr = (struct sockaddr *) unp; |
205 | 0 | aip->ai_addrlen = sizeof(struct sockaddr_un); |
206 | |
|
207 | 0 | 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 | 0 | if (path[0] == '@') |
219 | 0 | { |
220 | 0 | unp->sun_path[0] = '\0'; |
221 | 0 | aip->ai_addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(path); |
222 | 0 | } |
223 | |
|
224 | 0 | return 0; |
225 | 0 | } |
226 | | |
227 | | /* |
228 | | * Convert an address to a hostname. |
229 | | */ |
230 | | static int |
231 | | getnameinfo_unix(const struct sockaddr_un *sa, int salen, |
232 | | char *node, int nodelen, |
233 | | char *service, int servicelen, |
234 | | int flags) |
235 | 0 | { |
236 | 0 | int ret; |
237 | | |
238 | | /* Invalid arguments. */ |
239 | 0 | if (sa == NULL || sa->sun_family != AF_UNIX || |
240 | 0 | (node == NULL && service == NULL)) |
241 | 0 | return EAI_FAIL; |
242 | | |
243 | 0 | if (node) |
244 | 0 | { |
245 | 0 | ret = snprintf(node, nodelen, "%s", "[local]"); |
246 | 0 | if (ret < 0 || ret >= nodelen) |
247 | 0 | return EAI_MEMORY; |
248 | 0 | } |
249 | | |
250 | 0 | if (service) |
251 | 0 | { |
252 | | /* |
253 | | * Check whether it looks like an abstract socket, but it could also |
254 | | * just be an empty string. |
255 | | */ |
256 | 0 | if (sa->sun_path[0] == '\0' && sa->sun_path[1] != '\0') |
257 | 0 | ret = snprintf(service, servicelen, "@%s", sa->sun_path + 1); |
258 | 0 | else |
259 | 0 | ret = snprintf(service, servicelen, "%s", sa->sun_path); |
260 | 0 | if (ret < 0 || ret >= servicelen) |
261 | 0 | return EAI_MEMORY; |
262 | 0 | } |
263 | | |
264 | 0 | return 0; |
265 | 0 | } |