]> granicus.if.org Git - postgresql/blob - src/common/ip.c
Phase 2 of pgindent updates.
[postgresql] / src / common / ip.c
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 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 = -1;
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 == -1 || ret > nodelen)
247                         return EAI_MEMORY;
248         }
249
250         if (service)
251         {
252                 ret = snprintf(service, servicelen, "%s", sa->sun_path);
253                 if (ret == -1 || ret > servicelen)
254                         return EAI_MEMORY;
255         }
256
257         return 0;
258 }
259 #endif                                                  /* HAVE_UNIX_SOCKETS */