postgresql/src/backend/utils/adt/inet_cidr_ntop.c
Tom Lane 927f34ce8a Avoid conflicts with library versions of inet_net_ntop() and friends.
Prefix inet_net_ntop and sibling routines with "pg_" to ensure that
they aren't mistaken for C-library functions.  This fixes warnings
from cpluspluscheck on some platforms, and should help reduce reader
confusion everywhere, since our functions aren't exactly interchangeable
with the library versions (they may have different ideas about address
family codes).

This shouldn't be fixing any actual bugs, unless somebody's linker
is misbehaving, so no need to back-patch.

Discussion: https://postgr.es/m/20518.1559494394@sss.pgh.pa.us
2019-08-18 19:27:23 -04:00

295 lines
6.3 KiB
C

/*
* Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1996,1999 by Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* src/backend/utils/adt/inet_cidr_ntop.c
*/
#if defined(LIBC_SCCS) && !defined(lint)
static const char rcsid[] = "Id: inet_net_ntop.c,v 1.1.2.2 2004/03/09 09:17:27 marka Exp $";
#endif
#include "postgres.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "utils/builtins.h"
#include "utils/inet.h"
#ifdef SPRINTF_CHAR
#define SPRINTF(x) strlen(sprintf/**/x)
#else
#define SPRINTF(x) ((size_t)sprintf x)
#endif
static char *inet_cidr_ntop_ipv4(const u_char *src, int bits,
char *dst, size_t size);
static char *inet_cidr_ntop_ipv6(const u_char *src, int bits,
char *dst, size_t size);
/*
* char *
* pg_inet_cidr_ntop(af, src, bits, dst, size)
* convert network number from network to presentation format.
* generates CIDR style result always.
* return:
* pointer to dst, or NULL if an error occurred (check errno).
* author:
* Paul Vixie (ISC), July 1996
*/
char *
pg_inet_cidr_ntop(int af, const void *src, int bits, char *dst, size_t size)
{
switch (af)
{
case PGSQL_AF_INET:
return inet_cidr_ntop_ipv4(src, bits, dst, size);
case PGSQL_AF_INET6:
return inet_cidr_ntop_ipv6(src, bits, dst, size);
default:
errno = EAFNOSUPPORT;
return NULL;
}
}
/*
* static char *
* inet_cidr_ntop_ipv4(src, bits, dst, size)
* convert IPv4 network number from network to presentation format.
* generates CIDR style result always.
* return:
* pointer to dst, or NULL if an error occurred (check errno).
* note:
* network byte order assumed. this means 192.5.5.240/28 has
* 0b11110000 in its fourth octet.
* author:
* Paul Vixie (ISC), July 1996
*/
static char *
inet_cidr_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size)
{
char *odst = dst;
char *t;
u_int m;
int b;
if (bits < 0 || bits > 32)
{
errno = EINVAL;
return NULL;
}
if (bits == 0)
{
if (size < sizeof "0")
goto emsgsize;
*dst++ = '0';
size--;
*dst = '\0';
}
/* Format whole octets. */
for (b = bits / 8; b > 0; b--)
{
if (size <= sizeof "255.")
goto emsgsize;
t = dst;
dst += SPRINTF((dst, "%u", *src++));
if (b > 1)
{
*dst++ = '.';
*dst = '\0';
}
size -= (size_t) (dst - t);
}
/* Format partial octet. */
b = bits % 8;
if (b > 0)
{
if (size <= sizeof ".255")
goto emsgsize;
t = dst;
if (dst != odst)
*dst++ = '.';
m = ((1 << b) - 1) << (8 - b);
dst += SPRINTF((dst, "%u", *src & m));
size -= (size_t) (dst - t);
}
/* Format CIDR /width. */
if (size <= sizeof "/32")
goto emsgsize;
dst += SPRINTF((dst, "/%u", bits));
return odst;
emsgsize:
errno = EMSGSIZE;
return NULL;
}
/*
* static char *
* inet_cidr_ntop_ipv6(src, bits, dst, size)
* convert IPv6 network number from network to presentation format.
* generates CIDR style result always. Picks the shortest representation
* unless the IP is really IPv4.
* always prints specified number of bits (bits).
* return:
* pointer to dst, or NULL if an error occurred (check errno).
* note:
* network byte order assumed. this means 192.5.5.240/28 has
* 0x11110000 in its fourth octet.
* author:
* Vadim Kogan (UCB), June 2001
* Original version (IPv4) by Paul Vixie (ISC), July 1996
*/
static char *
inet_cidr_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size)
{
u_int m;
int b;
int p;
int zero_s,
zero_l,
tmp_zero_s,
tmp_zero_l;
int i;
int is_ipv4 = 0;
unsigned char inbuf[16];
char outbuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
char *cp;
int words;
u_char *s;
if (bits < 0 || bits > 128)
{
errno = EINVAL;
return NULL;
}
cp = outbuf;
if (bits == 0)
{
*cp++ = ':';
*cp++ = ':';
*cp = '\0';
}
else
{
/* Copy src to private buffer. Zero host part. */
p = (bits + 7) / 8;
memcpy(inbuf, src, p);
memset(inbuf + p, 0, 16 - p);
b = bits % 8;
if (b != 0)
{
m = ((u_int) ~0) << (8 - b);
inbuf[p - 1] &= m;
}
s = inbuf;
/* how many words need to be displayed in output */
words = (bits + 15) / 16;
if (words == 1)
words = 2;
/* Find the longest substring of zero's */
zero_s = zero_l = tmp_zero_s = tmp_zero_l = 0;
for (i = 0; i < (words * 2); i += 2)
{
if ((s[i] | s[i + 1]) == 0)
{
if (tmp_zero_l == 0)
tmp_zero_s = i / 2;
tmp_zero_l++;
}
else
{
if (tmp_zero_l && zero_l < tmp_zero_l)
{
zero_s = tmp_zero_s;
zero_l = tmp_zero_l;
tmp_zero_l = 0;
}
}
}
if (tmp_zero_l && zero_l < tmp_zero_l)
{
zero_s = tmp_zero_s;
zero_l = tmp_zero_l;
}
if (zero_l != words && zero_s == 0 && ((zero_l == 6) ||
((zero_l == 5 && s[10] == 0xff && s[11] == 0xff) ||
((zero_l == 7 && s[14] != 0 && s[15] != 1)))))
is_ipv4 = 1;
/* Format whole words. */
for (p = 0; p < words; p++)
{
if (zero_l != 0 && p >= zero_s && p < zero_s + zero_l)
{
/* Time to skip some zeros */
if (p == zero_s)
*cp++ = ':';
if (p == words - 1)
*cp++ = ':';
s++;
s++;
continue;
}
if (is_ipv4 && p > 5)
{
*cp++ = (p == 6) ? ':' : '.';
cp += SPRINTF((cp, "%u", *s++));
/* we can potentially drop the last octet */
if (p != 7 || bits > 120)
{
*cp++ = '.';
cp += SPRINTF((cp, "%u", *s++));
}
}
else
{
if (cp != outbuf)
*cp++ = ':';
cp += SPRINTF((cp, "%x", *s * 256 + s[1]));
s += 2;
}
}
}
/* Format CIDR /width. */
(void) SPRINTF((cp, "/%u", bits));
if (strlen(outbuf) + 1 > size)
goto emsgsize;
strcpy(dst, outbuf);
return dst;
emsgsize:
errno = EMSGSIZE;
return NULL;
}