Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * ip.c
4 : * IPv6-aware network access.
5 : *
6 : * Portions Copyright (c) 1996-2017, 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
57 217 : 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 217 : *result = NULL;
64 :
65 : #ifdef HAVE_UNIX_SOCKETS
66 217 : if (hintp->ai_family == AF_UNIX)
67 217 : return getaddrinfo_unix(servname, hintp, result);
68 : #endif
69 :
70 : /* NULL has special meaning to getaddrinfo(). */
71 0 : rc = getaddrinfo((!hostname || hostname[0] == '\0') ? NULL : hostname,
72 : servname, hintp, result);
73 :
74 0 : 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
88 432 : pg_freeaddrinfo_all(int hint_ai_family, struct addrinfo *ai)
89 : {
90 : #ifdef HAVE_UNIX_SOCKETS
91 432 : if (hint_ai_family == AF_UNIX)
92 : {
93 : /* struct was built by getaddrinfo_unix (see pg_getaddrinfo_all) */
94 1081 : while (ai != NULL)
95 : {
96 217 : struct addrinfo *p = ai;
97 :
98 217 : ai = ai->ai_next;
99 217 : free(p->ai_addr);
100 217 : free(p);
101 : }
102 : }
103 : else
104 : #endif /* HAVE_UNIX_SOCKETS */
105 : {
106 : /* struct was built by getaddrinfo() */
107 0 : if (ai != NULL)
108 0 : freeaddrinfo(ai);
109 : }
110 432 : }
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
122 2 : 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 2 : if (addr && addr->ss_family == AF_UNIX)
131 2 : rc = getnameinfo_unix((const struct sockaddr_un *) addr, salen,
132 : node, nodelen,
133 : service, servicelen,
134 : flags);
135 : else
136 : #endif
137 0 : rc = getnameinfo((const struct sockaddr *) addr, salen,
138 : node, nodelen,
139 : service, servicelen,
140 : flags);
141 :
142 2 : if (rc != 0)
143 : {
144 0 : if (node)
145 0 : strlcpy(node, "???", nodelen);
146 0 : if (service)
147 0 : strlcpy(service, "???", servicelen);
148 : }
149 :
150 2 : 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
165 217 : 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 217 : *result = NULL;
173 :
174 217 : MemSet(&hints, 0, sizeof(hints));
175 :
176 217 : if (strlen(path) >= sizeof(unp->sun_path))
177 0 : return EAI_FAIL;
178 :
179 217 : if (hintsp == NULL)
180 : {
181 0 : hints.ai_family = AF_UNIX;
182 0 : hints.ai_socktype = SOCK_STREAM;
183 : }
184 : else
185 217 : memcpy(&hints, hintsp, sizeof(hints));
186 :
187 217 : if (hints.ai_socktype == 0)
188 0 : hints.ai_socktype = SOCK_STREAM;
189 :
190 217 : if (hints.ai_family != AF_UNIX)
191 : {
192 : /* shouldn't have been called */
193 0 : return EAI_FAIL;
194 : }
195 :
196 217 : aip = calloc(1, sizeof(struct addrinfo));
197 217 : if (aip == NULL)
198 0 : return EAI_MEMORY;
199 :
200 217 : unp = calloc(1, sizeof(struct sockaddr_un));
201 217 : if (unp == NULL)
202 : {
203 0 : free(aip);
204 0 : return EAI_MEMORY;
205 : }
206 :
207 217 : aip->ai_family = AF_UNIX;
208 217 : aip->ai_socktype = hints.ai_socktype;
209 217 : aip->ai_protocol = hints.ai_protocol;
210 217 : aip->ai_next = NULL;
211 217 : aip->ai_canonname = NULL;
212 217 : *result = aip;
213 :
214 217 : unp->sun_family = AF_UNIX;
215 217 : aip->ai_addr = (struct sockaddr *) unp;
216 217 : aip->ai_addrlen = sizeof(struct sockaddr_un);
217 :
218 217 : 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 217 : return 0;
225 : }
226 :
227 : /*
228 : * Convert an address to a hostname.
229 : */
230 : static int
231 2 : getnameinfo_unix(const struct sockaddr_un *sa, int salen,
232 : char *node, int nodelen,
233 : char *service, int servicelen,
234 : int flags)
235 : {
236 2 : int ret = -1;
237 :
238 : /* Invalid arguments. */
239 2 : if (sa == NULL || sa->sun_family != AF_UNIX ||
240 2 : (node == NULL && service == NULL))
241 0 : return EAI_FAIL;
242 :
243 2 : if (node)
244 : {
245 0 : ret = snprintf(node, nodelen, "%s", "[local]");
246 0 : if (ret == -1 || ret > nodelen)
247 0 : return EAI_MEMORY;
248 : }
249 :
250 2 : if (service)
251 : {
252 2 : ret = snprintf(service, servicelen, "%s", sa->sun_path);
253 2 : if (ret == -1 || ret > servicelen)
254 0 : return EAI_MEMORY;
255 : }
256 :
257 2 : return 0;
258 : }
259 : #endif /* HAVE_UNIX_SOCKETS */
|