PostgreSQL Source Code  git master
ip.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * ip.c
4  * IPv6-aware network access.
5  *
6  * Portions Copyright (c) 1996-2024, 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 #include <netinet/tcp.h>
32 #include <arpa/inet.h>
33 #include <sys/file.h>
34 
35 #include "common/ip.h"
36 
37 
38 
39 static int getaddrinfo_unix(const char *path,
40  const struct addrinfo *hintsp,
41  struct addrinfo **result);
42 
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);
47 
48 
49 /*
50  * pg_getaddrinfo_all - get address info for Unix, IPv4 and IPv6 sockets
51  */
52 int
53 pg_getaddrinfo_all(const char *hostname, const char *servname,
54  const struct addrinfo *hintp, struct addrinfo **result)
55 {
56  int rc;
57 
58  /* not all versions of getaddrinfo() zero *result on failure */
59  *result = NULL;
60 
61  if (hintp->ai_family == AF_UNIX)
62  return getaddrinfo_unix(servname, hintp, result);
63 
64  /* NULL has special meaning to getaddrinfo(). */
65  rc = getaddrinfo((!hostname || hostname[0] == '\0') ? NULL : hostname,
66  servname, hintp, result);
67 
68  return rc;
69 }
70 
71 
72 /*
73  * pg_freeaddrinfo_all - free addrinfo structures for IPv4, IPv6, or Unix
74  *
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.
80  */
81 void
82 pg_freeaddrinfo_all(int hint_ai_family, struct addrinfo *ai)
83 {
84  if (hint_ai_family == AF_UNIX)
85  {
86  /* struct was built by getaddrinfo_unix (see pg_getaddrinfo_all) */
87  while (ai != NULL)
88  {
89  struct addrinfo *p = ai;
90 
91  ai = ai->ai_next;
92  free(p->ai_addr);
93  free(p);
94  }
95  }
96  else
97  {
98  /* struct was built by getaddrinfo() */
99  if (ai != NULL)
100  freeaddrinfo(ai);
101  }
102 }
103 
104 
105 /*
106  * pg_getnameinfo_all - get name info for Unix, IPv4 and IPv6 sockets
107  *
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.
112  */
113 int
114 pg_getnameinfo_all(const struct sockaddr_storage *addr, int salen,
115  char *node, int nodelen,
116  char *service, int servicelen,
117  int flags)
118 {
119  int rc;
120 
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);
131 
132  if (rc != 0)
133  {
134  if (node)
135  strlcpy(node, "???", nodelen);
136  if (service)
137  strlcpy(service, "???", servicelen);
138  }
139 
140  return rc;
141 }
142 
143 
144 /* -------
145  * getaddrinfo_unix - get unix socket info using IPv6-compatible API
146  *
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  * -------
151  */
152 static int
153 getaddrinfo_unix(const char *path, const struct addrinfo *hintsp,
154  struct addrinfo **result)
155 {
156  struct addrinfo hints = {0};
157  struct addrinfo *aip;
158  struct sockaddr_un *unp;
159 
160  *result = NULL;
161 
162  if (strlen(path) >= sizeof(unp->sun_path))
163  return EAI_FAIL;
164 
165  if (hintsp == NULL)
166  {
167  hints.ai_family = AF_UNIX;
168  hints.ai_socktype = SOCK_STREAM;
169  }
170  else
171  memcpy(&hints, hintsp, sizeof(hints));
172 
173  if (hints.ai_socktype == 0)
174  hints.ai_socktype = SOCK_STREAM;
175 
176  if (hints.ai_family != AF_UNIX)
177  {
178  /* shouldn't have been called */
179  return EAI_FAIL;
180  }
181 
182  aip = calloc(1, sizeof(struct addrinfo));
183  if (aip == NULL)
184  return EAI_MEMORY;
185 
186  unp = calloc(1, sizeof(struct sockaddr_un));
187  if (unp == NULL)
188  {
189  free(aip);
190  return EAI_MEMORY;
191  }
192 
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;
199 
200  unp->sun_family = AF_UNIX;
201  aip->ai_addr = (struct sockaddr *) unp;
202  aip->ai_addrlen = sizeof(struct sockaddr_un);
203 
204  strcpy(unp->sun_path, path);
205 
206  /*
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.
214  */
215  if (path[0] == '@')
216  {
217  unp->sun_path[0] = '\0';
218  aip->ai_addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(path);
219  }
220 
221  return 0;
222 }
223 
224 /*
225  * Convert an address to a hostname.
226  */
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)
232 {
233  int ret;
234 
235  /* Invalid arguments. */
236  if (sa == NULL || sa->sun_family != AF_UNIX ||
237  (node == NULL && service == NULL))
238  return EAI_FAIL;
239 
240  if (node)
241  {
242  ret = snprintf(node, nodelen, "%s", "[local]");
243  if (ret < 0 || ret >= nodelen)
244  return EAI_MEMORY;
245  }
246 
247  if (service)
248  {
249  /*
250  * Check whether it looks like an abstract socket, but it could also
251  * just be an empty string.
252  */
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;
259  }
260 
261  return 0;
262 }
#define calloc(a, b)
Definition: header.h:55
#define free(a)
Definition: header.h:65
void pg_freeaddrinfo_all(int hint_ai_family, struct addrinfo *ai)
Definition: ip.c:82
int pg_getnameinfo_all(const struct sockaddr_storage *addr, int salen, char *node, int nodelen, char *service, int servicelen, int flags)
Definition: ip.c:114
static int getnameinfo_unix(const struct sockaddr_un *sa, int salen, char *node, int nodelen, char *service, int servicelen, int flags)
Definition: ip.c:228
static int getaddrinfo_unix(const char *path, const struct addrinfo *hintsp, struct addrinfo **result)
Definition: ip.c:153
int pg_getaddrinfo_all(const char *hostname, const char *servname, const struct addrinfo *hintp, struct addrinfo **result)
Definition: ip.c:53
static char * hostname
Definition: pg_regress.c:115
#define snprintf
Definition: port.h:238
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
Definition: un.h:12
unsigned short sun_family
Definition: un.h:13
char sun_path[108]
Definition: un.h:14