File: bufip.hpp

package info (click to toggle)
openvpn3-client 25%2Bdfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 19,276 kB
  • sloc: cpp: 190,085; python: 7,218; ansic: 1,866; sh: 1,361; java: 402; lisp: 81; makefile: 17
file content (142 lines) | stat: -rw-r--r-- 3,869 bytes parent folder | download
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