1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
|
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012- OpenVPN Inc.
//
// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
//
// Fast formatting of IP addresses to a Buffer object.
// Formatting should be indistinguishable from inet_ntop().
#pragma once
#include <openvpn/buffer/bufstr.hpp>
#include <openvpn/buffer/buffmt.hpp>
#include <openvpn/common/socktypes.hpp> // for ntohs
namespace openvpn::BufferFormat {
static inline void ipv4(Buffer &buf, const std::uint32_t addr) // addr is big-endian
{
typedef BufferFormat::UnsignedDecimal<std::uint32_t> Decimal;
Decimal::write(buf, addr & 0xff);
buf.push_back('.');
Decimal::write(buf, (addr >> 8) & 0xff);
buf.push_back('.');
Decimal::write(buf, (addr >> 16) & 0xff);
buf.push_back('.');
Decimal::write(buf, addr >> 24);
}
static inline void ipv6(Buffer &buf, const void *addr)
{
typedef BufferFormat::Hex<std::uint16_t> Hex;
// address the IPv6 address as an array of 8 hextets
const std::uint16_t *a = static_cast<const std::uint16_t *>(addr);
// first pass -- look for any extended zero hextet series
size_t zero_start = 0;
size_t zero_extent = 0;
{
bool zero = false;
size_t start = 0;
for (size_t i = 0; i < 8; ++i)
{
if (zero)
{
if (a[i])
{
const size_t extent = i - start;
if (extent > zero_extent)
{
zero_start = start;
zero_extent = extent;
}
zero = false;
}
}
else
{
if (!a[i])
{
start = i;
zero = true;
}
}
}
// zero residual state?
if (zero)
{
const size_t extent = 8 - start;
if (extent > zero_extent)
{
zero_start = start;
zero_extent = extent;
}
}
}
// special case for IPv4
if (zero_start == 0)
{
if (zero_extent == 5 && a[5] == 0xffff)
{
buf_append_string(buf, "::ffff:");
ipv4(buf, *reinterpret_cast<const std::uint32_t *>(a + 6));
return;
}
else if (zero_extent == 6)
{
buf_append_string(buf, "::");
ipv4(buf, *reinterpret_cast<const std::uint32_t *>(a + 6));
return;
}
}
// second pass -- now write the hextets
{
enum State
{
INITIAL,
ZERO,
NORMAL,
};
State state = INITIAL;
for (size_t i = 0; i < 8; ++i)
{
const std::uint16_t hextet = ntohs(a[i]);
if (i == zero_start && zero_extent >= 2)
state = ZERO;
switch (state)
{
case INITIAL:
Hex::write(buf, hextet);
state = NORMAL;
break;
case ZERO:
if (!hextet)
break;
buf.push_back(':');
state = NORMAL;
// fallthrough
case NORMAL:
buf.push_back(':');
Hex::write(buf, hextet);
break;
}
}
// process residual state
if (state == ZERO)
buf_append_string(buf, "::");
}
}
} // namespace openvpn::BufferFormat
|