inet_pton函数源码分析(ipv4地址转换成数字)

本文详细介绍了Linux C运行库glibc中inet_pton函数的实现,该函数用于将IPv4和IPv6的字符串形式转换为网络字节序的二进制数据。文章通过源代码分析了函数如何处理IPv4和IPv6地址,包括合法性的检查和转换过程,并提供了实例演示其使用。
摘要由CSDN通过智能技术生成

简介

inet_pton的作用是将可读的IP地址(ipv4和ipv6均支持)字符串转换成网络序的函数,此函数的实现不在编译器中、也不在操作系统源码中,在C运行库的代码中实现。本文介绍的是Linux的C运行库:glibc对inet_pton函数的实现。

怎么找到glibc的inet_pton

fh@Feihu-3 glibc-2.34 % grep -nwr 'inet_pton' ./ | grep -nw 'weak_alias'
7:.//resolv/inet_pton.c:72:weak_alias (__inet_pton, inet_pton)
fh@Feihu-3 glibc-2.34 %

inet_pton函数源码分析(ipv4地址转换成数字)

fh@Feihu-3 glibc-2.34 % vim include/libc-symbols.h

inet_pton函数源码分析(ipv4地址转换成数字)

里面涉及到强引用和弱引用,请自行查阅相关资料。

接口源码实现

#include <string.h>
#include <stdio.h>
#include <nameser.h>
#include <sys/socket.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

// ipv4是4个字节,xxx.yyy.mmm.nnn
// 注意:每个八位组第一个不能为0
// 每个八位组值不能大于255
// 只能有4个八位组
// 整个ip字符串中只能有数字和'.'符号
// dst的结构体不需要理会,只需要知道dst为接收内存且长度只能是4字节,其他随意
static int inet_pton4(const char *src, const char *end, unsigned char *dst)
{
    int saw_digit; // 每个八位组是否遇到数字标记,新的八位组将会清除标记
    int octets;     // 八位组的个数,ip地址合法,则为4个
    int ch;         // 遍历字符串,每次获得的字符
    // NS_INADDRSZ长度是4个字节, 宏定义在nameser.h中
    // 注意tmp不是字符串,仅仅是用来存放4个字节的数据,无'\0'
    unsigned char tmp[NS_INADDRSZ];
    unsigned char *tp;      // tp指的是八位组
    
    saw_digit = 0;
    octets = 0;
    *(tp = tmp) = 0; // *tp = 0;初始化必须为0,计算数值的时候会有依赖
    
    // 本函数实际上是转换成网络序的,所以方向是src -> end
    // 如果仅仅转换成二进制,方向是end -> src
    while (src < end)
    {
        ch = *src++; // 得到获得的ASCII值,src指向下一个字符
        if (ch >= '0' && ch <= '9')
        {
            // 八位组迭代,比如192.168.8.217
            // 以192为例:
            // 0 -> 0 * 10 + 1 = 1 -> 1 * 10 + 9 = 19 -> 19 * 10 + 2 = 192
            // 在将192赋值到tmp的第一个字节上(第一个八位组)
            unsigned int new = *tp * 10 + (ch - '0');
            
            // 八位组中不能以0开头,比如192.168.08.217是错误的
            if (saw_digit && *tp == 0)
                return 0;
            // 某一个八位组值不能超过255
            if (new > 255)
                return 0;
            
            // 八位组赋值
            *tp = new;
            
            // 一般是在遇到'.'的时候,(! saw_digit)为0
            // 而在'.'之后的第一个数字置为1
            // 统计八位组的数目,由于在运行中,所以值不得超过4
            if (! saw_digit)
            {
                if (++octets > 4)
                    return 0;
                saw_digit = 1;
            }
        }
        else if (ch == '.' && saw_digit)
        {
            if (octets == 4)
                return 0;
            
            // 下一个八位组赋值,必须为0,方面迭代
            // saw_digit标记为未遇到数值
            *++tp = 0;
            saw_digit = 0;
        }
        else
            return 0; // 其他字符,直接返回错误
    }
    if (octets < 4)
        return 0;
    memcpy (dst, tmp, NS_INADDRSZ);
    return 1;
}

static int hex_digit_value(char ch)
{
    if ('0' <= ch && ch <= '9')
        return ch - '0';
    if ('a' <= ch && ch <= 'f')
        return ch - 'a' + 10;
    if ('A' <= ch && ch <= 'F')
        return ch - 'A' + 10;
    return -1;
}

static int inet_pton6(const char *src, const char *src_endp, unsigned char *dst)
{
    unsigned char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
    const char *curtok;
    int ch;
    size_t xdigits_seen;    /* Number of hex digits since colon.  */
    unsigned int val;
    
    tp = memset (tmp, '\0', NS_IN6ADDRSZ);
    endp = tp + NS_IN6ADDRSZ;
    colonp = NULL;
    
    /* Leading :: requires some special handling.  */
    if (src == src_endp)
        return 0;
    if (*src == ':')
    {
        ++src;
        if (src == src_endp || *src != ':')
            return 0;
    }
    
    curtok = src;
    xdigits_seen = 0;
    val = 0;
    while (src < src_endp)
    {
        ch = *src++;
        int digit = hex_digit_value (ch);
        if (digit >= 0)
        {
            if (xdigits_seen == 4)
                return 0;
            val <<= 4;
            val |= digit;
            if (val > 0xffff)
                return 0;
            ++xdigits_seen;
            continue;
        }
        if (ch == ':')
        {
            curtok = src;
            if (xdigits_seen == 0)
            {
                if (colonp)
                    return 0;
                colonp = tp;
                continue;
            }
            else if (src == src_endp)
                return 0;
            if (tp + NS_INT16SZ > endp)
                return 0;
            *tp++ = (unsigned char) (val >> 8) & 0xff;
            *tp++ = (unsigned char) val & 0xff;
            xdigits_seen = 0;
            val = 0;
            continue;
        }
        if (ch == '.' && ((tp + NS_INADDRSZ) <= endp)
            && inet_pton4 (curtok, src_endp, tp) > 0)
        {
            tp += NS_INADDRSZ;
            xdigits_seen = 0;
            break;  /* '\0' was seen by inet_pton4.  */
        }
        return 0;
    }
    if (xdigits_seen > 0)
    {
        if (tp + NS_INT16SZ > endp)
            return 0;
        *tp++ = (unsigned char) (val >> 8) & 0xff;
        *tp++ = (unsigned char) val & 0xff;
    }
    if (colonp != NULL)
    {
        /* Replace :: with zeros.  */
        if (tp == endp)
        /* :: would expand to a zero-width field.  */
            return 0;
        size_t n = tp - colonp;
        memmove (endp - n, colonp, n);
        memset (colonp, 0, endp - n - colonp);
        tp = endp;
    }
    if (tp != endp)
        return 0;
    memcpy (dst, tmp, NS_IN6ADDRSZ);
    return 1;
}

static int __inet_pton_length(int af, const char *src, size_t srclen, void *dst)
{
    switch (af)
    {
        case AF_INET:
            return inet_pton4(src, src + srclen, dst);
        case AF_INET6:
            return inet_pton6(src, src + srclen, dst);
        default:
            printf("invalid AF, af: %d.\n", af);
            return -1;
    }
    return -1;
}

static int pton(int af, const char *src, void *dst)
{
    return __inet_pton_length(af, src, strlen(src), dst);
}


void AddrConvertTest(void)
{
    char szIpv4[INET_ADDRSTRLEN] = "192.168.8.217";
    //char szIpv4[INET_ADDRSTRLEN] = "192.168.10.1";
    unsigned int ulIpv4 = 3232237785;
    printf("ip: %s, dec: %u, little-endian-hex: %#x, big-endian-hex: %#x\n", szIpv4, ulIpv4, ulIpv4, htonl(ulIpv4));
    
    struct in_addr stIpv4Addr = {0};
    int lRet = 0;
    lRet = pton(AF_INET, szIpv4, &stIpv4Addr);
    printf("pton ret: %d, ip: %s, s_addr: %#x.\n", lRet, szIpv4, stIpv4Addr.s_addr);
    return;
}

void AddrConvertLibcTest(void)
{
    char szStrIp[] = "192.168.8.217";
    unsigned int ulIp = 3232237785;
    printf("ip: %s, dec: %u, little-endian-hex: %#x, big-endian-hex: %#x\n", szStrIp, ulIp, ulIp, htonl(ulIp));
    char *pcTmp = NULL;
    int lRet = 0;
    struct in_addr stInAddr = {0};
    stInAddr.s_addr = inet_addr("192.168.8.217");
    pcTmp = inet_ntoa(stInAddr);
    printf("inet_ntoa, ret: %s, s_addr: %#x.\n", pcTmp, stInAddr.s_addr);
    
    /** inet_addr 处理255.255.255.255以及错误的ip返回的结果为0xffffffff
     */
    stInAddr.s_addr = inet_addr("259.255.255.255");
    printf("inet_addr s_addr: %#x.\n", stInAddr.s_addr);
    
    char szIp[INET_ADDRSTRLEN] = "192.168.8.217";
    struct in_addr stOutAddr = {0};
    lRet = inet_aton(szIp, &stOutAddr);
    printf("inet_aton ret: %d, ip: %s, s_addr: %#x.\n", lRet, szIp, stOutAddr.s_addr);
    
    struct in_addr stAddr2 = {0};
    char szIp2[INET_ADDRSTRLEN] = "192.168.8.217";
    // 文本字符串格式转换成网络字节序的二进制地址
    lRet = inet_pton(AF_INET, szIp2, &stAddr2);
    int ipv4StructSize = sizeof(struct in_addr);
    printf("inet_pton ret: %d, ip: %s, s_addr: %#x.\n", lRet, szIp2, stAddr2.s_addr);
    
    const char *pcTmp2 = NULL;
    memset(szIp2, 0, sizeof(szIp2));
    // 网络字节序的二进制地址转换成文本字符串格式
    pcTmp2 = inet_ntop(AF_INET, &stAddr2, szIp2, INET_ADDRSTRLEN);
    printf("inet_ntop ret: %s, ip: %s.\n", pcTmp2, szIp2);
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值