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