ipv6_address_utils.h 4.55 KB
//
//  ipv6_address_utils.h
//
//  Created by cairui01 on 18/11/9.
//  Copyright (c) 2018年 baidu. All rights reserved.
//
#ifndef comm_in6_v4mapped_macros_h
#define comm_in6_v4mapped_macros_h

#if !(defined(_WIN32) || defined(__WIN32__) || defined(WIN32))

#include <netinet/in.h>
#include <unistd.h>
#include <string>
#include <net/if.h>
#include "unix_socket.h"

// Google DNS address used for IPv6 probes.
const uint8_t kIPv6ProbeAddress[] =
{ 0x20, 0x01, 0x48, 0x60, 0x48, 0x60, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88 };
// Baidu DND address used for IPv4 probes.
const uint8_t kIPv4ProbeAddress[] = {0xB4, 0x4C, 0x4C, 0x4C};
// By definition, socklen_t is large enough to hold both sizes.
const socklen_t kSockaddrIn6Size = sizeof(struct sockaddr_in6);
const socklen_t kSockaddrIn4Size = sizeof(struct sockaddr_in);

#ifndef __APPLE__
#define IN6ADDR_V4MAPPED_INIT {{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00}}}
#endif

#ifdef __APPLE__
#define	s6_addr16   __u6_addr.__u6_addr16
#define	s6_addr32   __u6_addr.__u6_addr32
#endif

#define	IN6ADDR_NAT64_INIT {{{ 0x00, 0x64, 0xff, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}}

const in6_addr in6addr_v4mapped_init  = IN6ADDR_V4MAPPED_INIT;
const in6_addr in6addr_nat64_init		= IN6ADDR_NAT64_INIT;

inline void IN6_SET_ADDR_V4MAPPED(in6_addr* a6, const in_addr* a4) {
    *a6 = in6addr_v4mapped_init;
    a6->s6_addr32[3] = a4->s_addr;
}

inline void IN6_SET_ADDR_NAT64(in6_addr* a6, const in_addr* a4) {
    *a6 = in6addr_nat64_init;
    a6->s6_addr32[3] = a4->s_addr;
}

inline bool IN6_IS_ADDR_NAT64(const in6_addr* a6) {
    return a6->s6_addr32[0] == htonl(0x0064ff9b);
}

#else
#include <mstcpip.h>
//TODO impl
#define	IN6ADDR_NAT64_INIT {{{ 0x00, 0x64, 0xff, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}}
const in6_addr in6addr_nat64_init = IN6ADDR_NAT64_INIT;

#define s6_addr8    u.Byte
#define	s6_addr16   u.Word

inline void IN6_SET_ADDR_NAT64(in6_addr* a6, const in_addr* a4) {
	*a6 = in6addr_nat64_init;
	a6->s6_bytes[12] = ((CONST UCHAR *) a4)[0];
	a6->s6_bytes[13] = ((CONST UCHAR *) a4)[1];
	a6->s6_bytes[14] = ((CONST UCHAR *) a4)[2];
	a6->s6_bytes[15] = ((CONST UCHAR *) a4)[3];
}

inline bool IN6_IS_ADDR_NAT64(in6_addr* a6) {
	return a6->s6_words[0] == htons(0x0064) && a6->s6_words[1] == htons(0x0064);
}
#endif

#endif
// Attempts to connect a UDP socket to |dest|:53 with specify interface.
inline bool isGloballyReachable(bool isIPv4, const std::string& interface_name = std::string()) {
    int socketfd = INVALID_SOCKET;
    int connectResult;
    struct sockaddr* addr;
    int interface_index = 0;
    
    //  Bind to Interface
    if (!interface_name.empty()) {
        interface_index = if_nametoindex(interface_name.c_str());
        if (0 == interface_index) {
            return false;
        }
    }
    
    if (isIPv4) {
        socketfd = socket(AF_INET, SOCK_DGRAM, 0);
        //  Bind to Interface
        if (INVALID_SOCKET != socketfd && !interface_name.empty()) {
            if (0 != setsockopt(socketfd, IPPROTO_IP, IP_BOUND_IF, &interface_index, sizeof(interface_index))) {
                return false;
            }
        }
        struct sockaddr_in addr4;
        memset(&addr4, 0, kSockaddrIn4Size);
        addr4.sin_family = AF_INET;
        uint16_t port = 53;
        addr4.sin_port =  port;
        memcpy(&addr4.sin_addr, kIPv4ProbeAddress, 4);
        addr = (sockaddr*)&addr4;
        connectResult = connect(socketfd, addr, kSockaddrIn4Size);
    } else {
        socketfd = socket(AF_INET6, SOCK_DGRAM, 0);
        //  Bind to Interface
        if (INVALID_SOCKET != socketfd && !interface_name.empty()) {
            if (0 != setsockopt(socketfd, IPPROTO_IPV6, IPV6_BOUND_IF, &interface_index, sizeof(interface_index))) {
                return false;
            }
        }
        struct sockaddr_in6 addr6;
        memset(&addr6, 0, kSockaddrIn6Size);
        addr6.sin6_family = AF_INET6;
        uint16_t port = 53;
        addr6.sin6_port =  port;
        memcpy(&addr6.sin6_addr, kIPv6ProbeAddress, 16);
        addr = (sockaddr*)&addr6;
        connectResult = connect(socketfd, addr, kSockaddrIn6Size);
    }
    
    if (connectResult != 0) {
        close(socketfd);
        return false;
    }
    struct sockaddr localAddr;
    socklen_t localAddrLen = sizeof(localAddr);
    int socketNameResult = getsockname(socketfd, &localAddr, &localAddrLen);
    if (socketNameResult != 0) {
        close(socketfd);
        return false;
    }
    close(socketfd);
    return true;
}