Simplify and merge unwanted-module drop logic in AdjustUpgrade.pm.
[pgsql.git] / src / common / ip.c
blobb5ba02347aa71faed532f69acec2388870a16024
1 /*-------------------------------------------------------------------------
3 * ip.c
4 * IPv6-aware network access.
6 * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
10 * IDENTIFICATION
11 * src/common/ip.c
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.
17 *-------------------------------------------------------------------------
20 #ifndef FRONTEND
21 #include "postgres.h"
22 #else
23 #include "postgres_fe.h"
24 #endif
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>
35 #include "common/ip.h"
39 static int getaddrinfo_unix(const char *path,
40 const struct addrinfo *hintsp,
41 struct addrinfo **result);
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);
50 * pg_getaddrinfo_all - get address info for Unix, IPv4 and IPv6 sockets
52 int
53 pg_getaddrinfo_all(const char *hostname, const char *servname,
54 const struct addrinfo *hintp, struct addrinfo **result)
56 int rc;
58 /* not all versions of getaddrinfo() zero *result on failure */
59 *result = NULL;
61 if (hintp->ai_family == AF_UNIX)
62 return getaddrinfo_unix(servname, hintp, result);
64 /* NULL has special meaning to getaddrinfo(). */
65 rc = getaddrinfo((!hostname || hostname[0] == '\0') ? NULL : hostname,
66 servname, hintp, result);
68 return rc;
73 * pg_freeaddrinfo_all - free addrinfo structures for IPv4, IPv6, or Unix
75 * Note: the ai_family field of the original hint structure must be passed
76 * so that we can tell whether the addrinfo struct was built by the system's
77 * getaddrinfo() routine or our own getaddrinfo_unix() routine. Some versions
78 * of getaddrinfo() might be willing to return AF_UNIX addresses, so it's
79 * not safe to look at ai_family in the addrinfo itself.
81 void
82 pg_freeaddrinfo_all(int hint_ai_family, struct addrinfo *ai)
84 if (hint_ai_family == AF_UNIX)
86 /* struct was built by getaddrinfo_unix (see pg_getaddrinfo_all) */
87 while (ai != NULL)
89 struct addrinfo *p = ai;
91 ai = ai->ai_next;
92 free(p->ai_addr);
93 free(p);
96 else
98 /* struct was built by getaddrinfo() */
99 if (ai != NULL)
100 freeaddrinfo(ai);
106 * pg_getnameinfo_all - get name info for Unix, IPv4 and IPv6 sockets
108 * The API of this routine differs from the standard getnameinfo() definition
109 * in two ways: first, the addr parameter is declared as sockaddr_storage
110 * rather than struct sockaddr, and second, the node and service fields are
111 * guaranteed to be filled with something even on failure return.
114 pg_getnameinfo_all(const struct sockaddr_storage *addr, int salen,
115 char *node, int nodelen,
116 char *service, int servicelen,
117 int flags)
119 int rc;
121 if (addr && addr->ss_family == AF_UNIX)
122 rc = getnameinfo_unix((const struct sockaddr_un *) addr, salen,
123 node, nodelen,
124 service, servicelen,
125 flags);
126 else
127 rc = getnameinfo((const struct sockaddr *) addr, salen,
128 node, nodelen,
129 service, servicelen,
130 flags);
132 if (rc != 0)
134 if (node)
135 strlcpy(node, "???", nodelen);
136 if (service)
137 strlcpy(service, "???", servicelen);
140 return rc;
144 /* -------
145 * getaddrinfo_unix - get unix socket info using IPv6-compatible API
147 * Bugs: only one addrinfo is set even though hintsp is NULL or
148 * ai_socktype is 0
149 * AI_CANONNAME is not supported.
150 * -------
152 static int
153 getaddrinfo_unix(const char *path, const struct addrinfo *hintsp,
154 struct addrinfo **result)
156 struct addrinfo hints = {0};
157 struct addrinfo *aip;
158 struct sockaddr_un *unp;
160 *result = NULL;
162 if (strlen(path) >= sizeof(unp->sun_path))
163 return EAI_FAIL;
165 if (hintsp == NULL)
167 hints.ai_family = AF_UNIX;
168 hints.ai_socktype = SOCK_STREAM;
170 else
171 memcpy(&hints, hintsp, sizeof(hints));
173 if (hints.ai_socktype == 0)
174 hints.ai_socktype = SOCK_STREAM;
176 if (hints.ai_family != AF_UNIX)
178 /* shouldn't have been called */
179 return EAI_FAIL;
182 aip = calloc(1, sizeof(struct addrinfo));
183 if (aip == NULL)
184 return EAI_MEMORY;
186 unp = calloc(1, sizeof(struct sockaddr_un));
187 if (unp == NULL)
189 free(aip);
190 return EAI_MEMORY;
193 aip->ai_family = AF_UNIX;
194 aip->ai_socktype = hints.ai_socktype;
195 aip->ai_protocol = hints.ai_protocol;
196 aip->ai_next = NULL;
197 aip->ai_canonname = NULL;
198 *result = aip;
200 unp->sun_family = AF_UNIX;
201 aip->ai_addr = (struct sockaddr *) unp;
202 aip->ai_addrlen = sizeof(struct sockaddr_un);
204 strcpy(unp->sun_path, path);
207 * If the supplied path starts with @, replace that with a zero byte for
208 * the internal representation. In that mode, the entire sun_path is the
209 * address, including trailing zero bytes. But we set the address length
210 * to only include the length of the original string. That way the
211 * trailing zero bytes won't show up in any network or socket lists of the
212 * operating system. This is just a convention, also followed by other
213 * packages.
215 if (path[0] == '@')
217 unp->sun_path[0] = '\0';
218 aip->ai_addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(path);
221 return 0;
225 * Convert an address to a hostname.
227 static int
228 getnameinfo_unix(const struct sockaddr_un *sa, int salen,
229 char *node, int nodelen,
230 char *service, int servicelen,
231 int flags)
233 int ret;
235 /* Invalid arguments. */
236 if (sa == NULL || sa->sun_family != AF_UNIX ||
237 (node == NULL && service == NULL))
238 return EAI_FAIL;
240 if (node)
242 ret = snprintf(node, nodelen, "%s", "[local]");
243 if (ret < 0 || ret >= nodelen)
244 return EAI_MEMORY;
247 if (service)
250 * Check whether it looks like an abstract socket, but it could also
251 * just be an empty string.
253 if (sa->sun_path[0] == '\0' && sa->sun_path[1] != '\0')
254 ret = snprintf(service, servicelen, "@%s", sa->sun_path + 1);
255 else
256 ret = snprintf(service, servicelen, "%s", sa->sun_path);
257 if (ret < 0 || ret >= servicelen)
258 return EAI_MEMORY;
261 return 0;