Coverage Report

Created: 2026-08-14 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/common/ip.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * ip.c
4
 *    IPv6-aware network access.
5
 *
6
 * Portions Copyright (c) 1996-2026, 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
 * The API of this routine differs from the standard getaddrinfo() definition
53
 * in that it requires a valid hintp, a null pointer is not allowed.
54
 */
55
int
56
pg_getaddrinfo_all(const char *hostname, const char *servname,
57
           const struct addrinfo *hintp, struct addrinfo **result)
58
0
{
59
0
  int     rc;
60
61
  /* not all versions of getaddrinfo() zero *result on failure */
62
0
  *result = NULL;
63
64
0
  if (hintp->ai_family == AF_UNIX)
65
0
    return getaddrinfo_unix(servname, hintp, result);
66
67
  /* NULL has special meaning to getaddrinfo(). */
68
0
  rc = getaddrinfo((!hostname || hostname[0] == '\0') ? NULL : hostname,
69
0
           servname, hintp, result);
70
71
0
  return rc;
72
0
}
73
74
75
/*
76
 *  pg_freeaddrinfo_all - free addrinfo structures for IPv4, IPv6, or Unix
77
 *
78
 * Note: the ai_family field of the original hint structure must be passed
79
 * so that we can tell whether the addrinfo struct was built by the system's
80
 * getaddrinfo() routine or our own getaddrinfo_unix() routine.  Some versions
81
 * of getaddrinfo() might be willing to return AF_UNIX addresses, so it's
82
 * not safe to look at ai_family in the addrinfo itself.
83
 */
84
void
85
pg_freeaddrinfo_all(int hint_ai_family, struct addrinfo *ai)
86
0
{
87
0
  if (hint_ai_family == AF_UNIX)
88
0
  {
89
    /* struct was built by getaddrinfo_unix (see pg_getaddrinfo_all) */
90
0
    while (ai != NULL)
91
0
    {
92
0
      struct addrinfo *p = ai;
93
94
0
      ai = ai->ai_next;
95
0
      free(p->ai_addr);
96
0
      free(p);
97
0
    }
98
0
  }
99
0
  else
100
0
  {
101
    /* struct was built by getaddrinfo() */
102
0
    if (ai != NULL)
103
0
      freeaddrinfo(ai);
104
0
  }
105
0
}
106
107
108
/*
109
 *  pg_getnameinfo_all - get name info for Unix, IPv4 and IPv6 sockets
110
 *
111
 * The API of this routine differs from the standard getnameinfo() definition
112
 * in two ways: first, the addr parameter is declared as sockaddr_storage
113
 * rather than struct sockaddr, and second, the node and service fields are
114
 * guaranteed to be filled with something even on failure return.
115
 */
116
int
117
pg_getnameinfo_all(const struct sockaddr_storage *addr, int salen,
118
           char *node, int nodelen,
119
           char *service, int servicelen,
120
           int flags)
121
0
{
122
0
  int     rc;
123
124
0
  if (addr && addr->ss_family == AF_UNIX)
125
0
    rc = getnameinfo_unix((const struct sockaddr_un *) addr, salen,
126
0
                node, nodelen,
127
0
                service, servicelen,
128
0
                flags);
129
0
  else
130
0
    rc = getnameinfo((const struct sockaddr *) addr, salen,
131
0
             node, nodelen,
132
0
             service, servicelen,
133
0
             flags);
134
135
0
  if (rc != 0)
136
0
  {
137
0
    if (node)
138
0
      strlcpy(node, "???", nodelen);
139
0
    if (service)
140
0
      strlcpy(service, "???", servicelen);
141
0
  }
142
143
0
  return rc;
144
0
}
145
146
147
/* -------
148
 *  getaddrinfo_unix - get unix socket info using IPv6-compatible API
149
 *
150
 *  Bugs: only one addrinfo is set even though hintsp is NULL or
151
 *      ai_socktype is 0
152
 *      AI_CANONNAME is not supported.
153
 * -------
154
 */
155
static int
156
getaddrinfo_unix(const char *path, const struct addrinfo *hintsp,
157
         struct addrinfo **result)
158
0
{
159
0
  struct addrinfo hints = {0};
160
0
  struct addrinfo *aip;
161
0
  struct sockaddr_un *unp;
162
163
0
  *result = NULL;
164
165
0
  if (strlen(path) >= sizeof(unp->sun_path))
166
0
    return EAI_FAIL;
167
168
0
  if (hintsp == NULL)
169
0
  {
170
0
    hints.ai_family = AF_UNIX;
171
0
    hints.ai_socktype = SOCK_STREAM;
172
0
  }
173
0
  else
174
0
    memcpy(&hints, hintsp, sizeof(hints));
175
176
0
  if (hints.ai_socktype == 0)
177
0
    hints.ai_socktype = SOCK_STREAM;
178
179
0
  if (hints.ai_family != AF_UNIX)
180
0
  {
181
    /* shouldn't have been called */
182
0
    return EAI_FAIL;
183
0
  }
184
185
0
  aip = calloc(1, sizeof(struct addrinfo));
186
0
  if (aip == NULL)
187
0
    return EAI_MEMORY;
188
189
0
  unp = calloc(1, sizeof(struct sockaddr_un));
190
0
  if (unp == NULL)
191
0
  {
192
0
    free(aip);
193
0
    return EAI_MEMORY;
194
0
  }
195
196
0
  aip->ai_family = AF_UNIX;
197
0
  aip->ai_socktype = hints.ai_socktype;
198
0
  aip->ai_protocol = hints.ai_protocol;
199
0
  aip->ai_next = NULL;
200
0
  aip->ai_canonname = NULL;
201
0
  *result = aip;
202
203
0
  unp->sun_family = AF_UNIX;
204
0
  aip->ai_addr = (struct sockaddr *) unp;
205
0
  aip->ai_addrlen = sizeof(struct sockaddr_un);
206
207
0
  strcpy(unp->sun_path, path);
208
209
  /*
210
   * If the supplied path starts with @, replace that with a zero byte for
211
   * the internal representation.  In that mode, the entire sun_path is the
212
   * address, including trailing zero bytes.  But we set the address length
213
   * to only include the length of the original string.  That way the
214
   * trailing zero bytes won't show up in any network or socket lists of the
215
   * operating system.  This is just a convention, also followed by other
216
   * packages.
217
   */
218
0
  if (path[0] == '@')
219
0
  {
220
0
    unp->sun_path[0] = '\0';
221
0
    aip->ai_addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(path);
222
0
  }
223
224
0
  return 0;
225
0
}
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
0
{
236
0
  int     ret;
237
238
  /* Invalid arguments. */
239
0
  if (sa == NULL || sa->sun_family != AF_UNIX ||
240
0
    (node == NULL && service == NULL))
241
0
    return EAI_FAIL;
242
243
0
  if (node)
244
0
  {
245
0
    ret = snprintf(node, nodelen, "%s", "[local]");
246
0
    if (ret < 0 || ret >= nodelen)
247
0
      return EAI_MEMORY;
248
0
  }
249
250
0
  if (service)
251
0
  {
252
    /*
253
     * Check whether it looks like an abstract socket, but it could also
254
     * just be an empty string.
255
     */
256
0
    if (sa->sun_path[0] == '\0' && sa->sun_path[1] != '\0')
257
0
      ret = snprintf(service, servicelen, "@%s", sa->sun_path + 1);
258
0
    else
259
0
      ret = snprintf(service, servicelen, "%s", sa->sun_path);
260
0
    if (ret < 0 || ret >= servicelen)
261
0
      return EAI_MEMORY;
262
0
  }
263
264
0
  return 0;
265
0
}