S2N_RESULT s2n_inet_ntop()

in utils/s2n_rfc5952.c [28:130]


S2N_RESULT s2n_inet_ntop(int af, const void *addr, struct s2n_blob *dst)
{
    const uint8_t *bytes = addr;
    uint8_t *cursor = dst->data;

    if (af == AF_INET) {
        RESULT_ENSURE(dst->size >= sizeof("111.222.333.444"), S2N_ERR_SIZE_MISMATCH);

        for (int i = 0; i < 4; i++) {
            if (bytes[i] / 100) {
                *cursor++ = dec[bytes[i] / 100];
            }
            if (bytes[i] >= 10) {
                *cursor++ = dec[(bytes[i] % 100) / 10];
            }
            *cursor++ = dec[(bytes[i] % 10)];
            *cursor++ = '.';
        }

        *--cursor = '\0';

        return S2N_RESULT_OK;
    }

    if (af == AF_INET6) {
        RESULT_ENSURE(dst->size >= sizeof("1111:2222:3333:4444:5555:6666:7777:8888"), S2N_ERR_SIZE_MISMATCH);

        /* See Section 4 of RFC5952 for the rules we are going to follow here
         *
         * Here's the general algorithm:
         *
         *   1/ Treat the bytes as 8 16-bit fields
         *   2/ Find the longest run of 16-bit fields.
         *   3/ or if there are two or more equal length longest runs, go with the left-most run
         *   4/ Make that run ::
         *   5/ Print the remaining 16-bit fields in lowercase hex, no leading zeroes
         */

        uint16_t octets[8] = { 0 };

        int longest_run_start = 0;
        int longest_run_length = 0;
        int current_run_length = 0;

        /* 2001:db8::1:0:0:1 */

        /* Find the longest run of zeroes */
        for (int i = 0; i < 8; i++) {
            octets[i] = (bytes[i * 2] << 8) + bytes[(i * 2) + 1];

            if (octets[i]) {
                current_run_length = 0;
            } else {
                current_run_length++;
            }

            if (current_run_length > longest_run_length) {
                longest_run_length = current_run_length;
                longest_run_start = (i - current_run_length) + 1;
            }
        }

        for (int i = 0; i < 8; i++) {
            if (i == longest_run_start && longest_run_length > 1) {
                if (i == 0) {
                    *cursor++ = ':';
                }

                if (longest_run_length == 8) {
                    *cursor++ = ':';
                }

                i += longest_run_length - 1;

            } else {
                uint8_t nibbles[4] = { (octets[i] & 0xF000) >> 12,
                    (octets[i] & 0x0F00) >> 8,
                    (octets[i] & 0x00F0) >> 4,
                    (octets[i] & 0x000F) };

                /* Skip up to three leading zeroes */
                int j = 0;
                for (j = 0; j < 3; j++) {
                    if (nibbles[j]) {
                        break;
                    }
                }

                for (; j < 4; j++) {
                    *cursor++ = hex[nibbles[j]];
                }
            }

            *cursor++ = ':';
        }

        *--cursor = '\0';

        return S2N_RESULT_OK;
    }

    RESULT_BAIL(S2N_ERR_INVALID_ARGUMENT);
}