1 /*-------------------------------------------------------------------------
2 *
3 * ip.c
4 * IPv6-aware network access.
5 *
6 * Portions Copyright (c) 1996-2020, 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
42 static int getaddrinfo_unix(const char *path,
43 const struct addrinfo *hintsp,
44 struct addrinfo **result);
45
46 static 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 */
56 int
pg_getaddrinfo_all(const char * hostname,const char * servname,const struct addrinfo * hintp,struct addrinfo ** result)57 pg_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 */
87 void
pg_freeaddrinfo_all(int hint_ai_family,struct addrinfo * ai)88 pg_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 */
121 int
pg_getnameinfo_all(const struct sockaddr_storage * addr,int salen,char * node,int nodelen,char * service,int servicelen,int flags)122 pg_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 */
164 static int
getaddrinfo_unix(const char * path,const struct addrinfo * hintsp,struct addrinfo ** result)165 getaddrinfo_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 */
230 static int
getnameinfo_unix(const struct sockaddr_un * sa,int salen,char * node,int nodelen,char * service,int servicelen,int flags)231 getnameinfo_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