1 /*-------------------------------------------------------------------------
2  *
3  * ip.c
4  *	  IPv6-aware network access.
5  *
6  * Portions Copyright (c) 1996-2019, 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 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
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
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
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
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