1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "nacl_io/ossocket.h"
6#if defined(PROVIDES_SOCKET_API) && !defined(__GLIBC__) && !defined(__BIONIC__)
7
8#include <errno.h>
9#include <string.h>
10
11#include <iostream>
12#include <sstream>
13#include <string>
14
15#include "sdk_util/macros.h"
16
17EXTERN_C_BEGIN
18
19const char* inet_ntop(int af, const void* src, char* dst, socklen_t size) {
20  if (AF_INET == af) {
21    if (size < INET_ADDRSTRLEN) {
22      errno = ENOSPC;
23      return NULL;
24    }
25    struct in_addr in;
26    memcpy(&in, src, sizeof(in));
27    char* result = inet_ntoa(in);
28    memcpy(dst, result, strlen(result) + 1);
29    return dst;
30  }
31
32  if (AF_INET6 == af) {
33    if (size < INET6_ADDRSTRLEN) {
34      errno = ENOSPC;
35      return NULL;
36    }
37
38    // Convert to an array of 8 host order shorts
39    const uint16_t* tuples = static_cast<const uint16_t*>(src);
40    uint16_t host_tuples[8];
41    int zero_run_start = -1;
42    int zero_run_end = -1;
43    for (int i = 0; i < 8; i++) {
44      host_tuples[i] = ntohs(tuples[i]);
45      if (host_tuples[i] == 0) {
46        if (zero_run_start == -1)
47          zero_run_start = i;
48      } else if (zero_run_start != -1 && zero_run_end == -1) {
49        zero_run_end = i;
50      }
51    }
52
53    if (zero_run_start != -1) {
54      if (zero_run_end == -1)
55        zero_run_end = 8;
56      if (zero_run_end - zero_run_start < 2) {
57        zero_run_start = -1;
58        zero_run_end = -1;
59      }
60    }
61
62    // Mimick glibc's behaviour here and allow ipv4 address to be specified
63    // as either ::A.B.C.D or ::ffff:A.B.C.D.
64    if (zero_run_start == 0 &&
65        (zero_run_end == 6 ||
66         (zero_run_end == 5 && host_tuples[zero_run_end] == 0xffff))) {
67      if (zero_run_end == 5) {
68        strcpy(dst, "::ffff:");
69      } else {
70        strcpy(dst, "::");
71      }
72      inet_ntop(AF_INET, host_tuples + 6, dst + strlen(dst), INET_ADDRSTRLEN);
73    } else {
74      std::stringstream output;
75      for (int i = 0; i < 8; i++) {
76        if (i == zero_run_start) {
77          output << "::";
78          continue;
79        }
80        if (i > zero_run_start && i < zero_run_end)
81          continue;
82        output << std::hex << host_tuples[i];
83        if (i < 7 && i + 1 != zero_run_start)
84          output << ":";
85      }
86      memcpy(dst, output.str().c_str(), output.str().size() + 1);
87    }
88    return dst;
89  }
90
91  errno = EAFNOSUPPORT;
92  return NULL;
93}
94
95EXTERN_C_END
96
97#endif  // defined(PROVIDES_SOCKET_API) && !defined(__GLIBC__) ...
98