1 /*-------------------------------------------------------------------------
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
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 *-------------------------------------------------------------------------
23 #include "postgres_fe.h"
28 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <netinet/tcp.h>
32 #include <arpa/inet.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
,
50 * pg_getaddrinfo_all - get address info for Unix, IPv4 and IPv6 sockets
53 pg_getaddrinfo_all(const char *hostname
, const char *servname
,
54 const struct addrinfo
*hintp
, struct addrinfo
**result
)
58 /* not all versions of getaddrinfo() zero *result on failure */
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
);
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.
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) */
89 struct addrinfo
*p
= ai
;
98 /* struct was built by getaddrinfo() */
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
,
121 if (addr
&& addr
->ss_family
== AF_UNIX
)
122 rc
= getnameinfo_unix((const struct sockaddr_un
*) addr
, salen
,
127 rc
= getnameinfo((const struct sockaddr
*) addr
, salen
,
135 strlcpy(node
, "???", nodelen
);
137 strlcpy(service
, "???", servicelen
);
145 * getaddrinfo_unix - get unix socket info using IPv6-compatible API
147 * Bugs: only one addrinfo is set even though hintsp is NULL or
149 * AI_CANONNAME is not supported.
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
;
162 if (strlen(path
) >= sizeof(unp
->sun_path
))
167 hints
.ai_family
= AF_UNIX
;
168 hints
.ai_socktype
= SOCK_STREAM
;
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 */
182 aip
= calloc(1, sizeof(struct addrinfo
));
186 unp
= calloc(1, sizeof(struct sockaddr_un
));
193 aip
->ai_family
= AF_UNIX
;
194 aip
->ai_socktype
= hints
.ai_socktype
;
195 aip
->ai_protocol
= hints
.ai_protocol
;
197 aip
->ai_canonname
= NULL
;
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
217 unp
->sun_path
[0] = '\0';
218 aip
->ai_addrlen
= offsetof(struct sockaddr_un
, sun_path
) + strlen(path
);
225 * Convert an address to a hostname.
228 getnameinfo_unix(const struct sockaddr_un
*sa
, int salen
,
229 char *node
, int nodelen
,
230 char *service
, int servicelen
,
235 /* Invalid arguments. */
236 if (sa
== NULL
|| sa
->sun_family
!= AF_UNIX
||
237 (node
== NULL
&& service
== NULL
))
242 ret
= snprintf(node
, nodelen
, "%s", "[local]");
243 if (ret
< 0 || ret
>= nodelen
)
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);
256 ret
= snprintf(service
, servicelen
, "%s", sa
->sun_path
);
257 if (ret
< 0 || ret
>= servicelen
)