Header And Logo

PostgreSQL
| The world's most advanced open source database.

inet_cidr_ntop.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
00003  * Copyright (c) 1996,1999 by Internet Software Consortium.
00004  *
00005  * Permission to use, copy, modify, and distribute this software for any
00006  * purpose with or without fee is hereby granted, provided that the above
00007  * copyright notice and this permission notice appear in all copies.
00008  *
00009  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
00010  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
00011  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
00012  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00013  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
00014  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
00015  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00016  *
00017  *    src/backend/utils/adt/inet_net_ntop.c
00018  */
00019 
00020 #if defined(LIBC_SCCS) && !defined(lint)
00021 static const char rcsid[] = "Id: inet_net_ntop.c,v 1.1.2.2 2004/03/09 09:17:27 marka Exp $";
00022 #endif
00023 
00024 #include "postgres.h"
00025 
00026 #include <sys/types.h>
00027 #include <sys/socket.h>
00028 #include <netinet/in.h>
00029 #include <arpa/inet.h>
00030 
00031 #include "utils/builtins.h"
00032 #include "utils/inet.h"
00033 
00034 
00035 #ifdef SPRINTF_CHAR
00036 #define SPRINTF(x) strlen(sprintfx)
00037 #else
00038 #define SPRINTF(x) ((size_t)sprintf x)
00039 #endif
00040 
00041 static char *inet_cidr_ntop_ipv4(const u_char *src, int bits,
00042                     char *dst, size_t size);
00043 static char *inet_cidr_ntop_ipv6(const u_char *src, int bits,
00044                     char *dst, size_t size);
00045 
00046 /*
00047  * char *
00048  * inet_cidr_ntop(af, src, bits, dst, size)
00049  *  convert network number from network to presentation format.
00050  *  generates CIDR style result always.
00051  * return:
00052  *  pointer to dst, or NULL if an error occurred (check errno).
00053  * author:
00054  *  Paul Vixie (ISC), July 1996
00055  */
00056 char *
00057 inet_cidr_ntop(int af, const void *src, int bits, char *dst, size_t size)
00058 {
00059     switch (af)
00060     {
00061         case PGSQL_AF_INET:
00062             return (inet_cidr_ntop_ipv4(src, bits, dst, size));
00063         case PGSQL_AF_INET6:
00064             return (inet_cidr_ntop_ipv6(src, bits, dst, size));
00065         default:
00066             errno = EAFNOSUPPORT;
00067             return (NULL);
00068     }
00069 }
00070 
00071 
00072 /*
00073  * static char *
00074  * inet_cidr_ntop_ipv4(src, bits, dst, size)
00075  *  convert IPv4 network number from network to presentation format.
00076  *  generates CIDR style result always.
00077  * return:
00078  *  pointer to dst, or NULL if an error occurred (check errno).
00079  * note:
00080  *  network byte order assumed.  this means 192.5.5.240/28 has
00081  *  0b11110000 in its fourth octet.
00082  * author:
00083  *  Paul Vixie (ISC), July 1996
00084  */
00085 static char *
00086 inet_cidr_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size)
00087 {
00088     char       *odst = dst;
00089     char       *t;
00090     u_int       m;
00091     int         b;
00092 
00093     if (bits < 0 || bits > 32)
00094     {
00095         errno = EINVAL;
00096         return (NULL);
00097     }
00098 
00099     if (bits == 0)
00100     {
00101         if (size < sizeof "0")
00102             goto emsgsize;
00103         *dst++ = '0';
00104         size--;
00105         *dst = '\0';
00106     }
00107 
00108     /* Format whole octets. */
00109     for (b = bits / 8; b > 0; b--)
00110     {
00111         if (size <= sizeof "255.")
00112             goto emsgsize;
00113         t = dst;
00114         dst += SPRINTF((dst, "%u", *src++));
00115         if (b > 1)
00116         {
00117             *dst++ = '.';
00118             *dst = '\0';
00119         }
00120         size -= (size_t) (dst - t);
00121     }
00122 
00123     /* Format partial octet. */
00124     b = bits % 8;
00125     if (b > 0)
00126     {
00127         if (size <= sizeof ".255")
00128             goto emsgsize;
00129         t = dst;
00130         if (dst != odst)
00131             *dst++ = '.';
00132         m = ((1 << b) - 1) << (8 - b);
00133         dst += SPRINTF((dst, "%u", *src & m));
00134         size -= (size_t) (dst - t);
00135     }
00136 
00137     /* Format CIDR /width. */
00138     if (size <= sizeof "/32")
00139         goto emsgsize;
00140     dst += SPRINTF((dst, "/%u", bits));
00141     return (odst);
00142 
00143 emsgsize:
00144     errno = EMSGSIZE;
00145     return (NULL);
00146 }
00147 
00148 /*
00149  * static char *
00150  * inet_cidr_ntop_ipv6(src, bits, fakebits, dst, size)
00151  *  convert IPv6 network number from network to presentation format.
00152  *  generates CIDR style result always. Picks the shortest representation
00153  *  unless the IP is really IPv4.
00154  *  always prints specified number of bits (bits).
00155  * return:
00156  *  pointer to dst, or NULL if an error occurred (check errno).
00157  * note:
00158  *  network byte order assumed.  this means 192.5.5.240/28 has
00159  *  0x11110000 in its fourth octet.
00160  * author:
00161  *  Vadim Kogan (UCB), June 2001
00162  *  Original version (IPv4) by Paul Vixie (ISC), July 1996
00163  */
00164 
00165 static char *
00166 inet_cidr_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size)
00167 {
00168     u_int       m;
00169     int         b;
00170     int         p;
00171     int         zero_s,
00172                 zero_l,
00173                 tmp_zero_s,
00174                 tmp_zero_l;
00175     int         i;
00176     int         is_ipv4 = 0;
00177     unsigned char inbuf[16];
00178     char        outbuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
00179     char       *cp;
00180     int         words;
00181     u_char     *s;
00182 
00183     if (bits < 0 || bits > 128)
00184     {
00185         errno = EINVAL;
00186         return (NULL);
00187     }
00188 
00189     cp = outbuf;
00190 
00191     if (bits == 0)
00192     {
00193         *cp++ = ':';
00194         *cp++ = ':';
00195         *cp = '\0';
00196     }
00197     else
00198     {
00199         /* Copy src to private buffer.  Zero host part. */
00200         p = (bits + 7) / 8;
00201         memcpy(inbuf, src, p);
00202         memset(inbuf + p, 0, 16 - p);
00203         b = bits % 8;
00204         if (b != 0)
00205         {
00206             m = ~0 << (8 - b);
00207             inbuf[p - 1] &= m;
00208         }
00209 
00210         s = inbuf;
00211 
00212         /* how many words need to be displayed in output */
00213         words = (bits + 15) / 16;
00214         if (words == 1)
00215             words = 2;
00216 
00217         /* Find the longest substring of zero's */
00218         zero_s = zero_l = tmp_zero_s = tmp_zero_l = 0;
00219         for (i = 0; i < (words * 2); i += 2)
00220         {
00221             if ((s[i] | s[i + 1]) == 0)
00222             {
00223                 if (tmp_zero_l == 0)
00224                     tmp_zero_s = i / 2;
00225                 tmp_zero_l++;
00226             }
00227             else
00228             {
00229                 if (tmp_zero_l && zero_l < tmp_zero_l)
00230                 {
00231                     zero_s = tmp_zero_s;
00232                     zero_l = tmp_zero_l;
00233                     tmp_zero_l = 0;
00234                 }
00235             }
00236         }
00237 
00238         if (tmp_zero_l && zero_l < tmp_zero_l)
00239         {
00240             zero_s = tmp_zero_s;
00241             zero_l = tmp_zero_l;
00242         }
00243 
00244         if (zero_l != words && zero_s == 0 && ((zero_l == 6) ||
00245                           ((zero_l == 5 && s[10] == 0xff && s[11] == 0xff) ||
00246                            ((zero_l == 7 && s[14] != 0 && s[15] != 1)))))
00247             is_ipv4 = 1;
00248 
00249         /* Format whole words. */
00250         for (p = 0; p < words; p++)
00251         {
00252             if (zero_l != 0 && p >= zero_s && p < zero_s + zero_l)
00253             {
00254                 /* Time to skip some zeros */
00255                 if (p == zero_s)
00256                     *cp++ = ':';
00257                 if (p == words - 1)
00258                     *cp++ = ':';
00259                 s++;
00260                 s++;
00261                 continue;
00262             }
00263 
00264             if (is_ipv4 && p > 5)
00265             {
00266                 *cp++ = (p == 6) ? ':' : '.';
00267                 cp += SPRINTF((cp, "%u", *s++));
00268                 /* we can potentially drop the last octet */
00269                 if (p != 7 || bits > 120)
00270                 {
00271                     *cp++ = '.';
00272                     cp += SPRINTF((cp, "%u", *s++));
00273                 }
00274             }
00275             else
00276             {
00277                 if (cp != outbuf)
00278                     *cp++ = ':';
00279                 cp += SPRINTF((cp, "%x", *s * 256 + s[1]));
00280                 s += 2;
00281             }
00282         }
00283     }
00284     /* Format CIDR /width. */
00285     (void) SPRINTF((cp, "/%u", bits));
00286     if (strlen(outbuf) + 1 > size)
00287         goto emsgsize;
00288     strcpy(dst, outbuf);
00289 
00290     return (dst);
00291 
00292 emsgsize:
00293     errno = EMSGSIZE;
00294     return (NULL);
00295 }