【问题标题】:How to implement inet_pton() in C?如何在 C 中实现 inet_pton()?
【发布时间】:2021-08-18 19:39:42
【问题描述】:

我正在解析 IP 标头并更改源 IP 地址...在我使用 NIPQUAD 宏但无法正常工作之前,我发现 inet_pton() 将字符串 IP 地址更改为网络字节顺序。没看懂:

  • 如何使用 uint_32(无符号整数)来保存 IP 地址?
  • 这个函数是如何从字符串 IP 转换为网络字节序的?
  • 它是如何用于显示 IP 地址的?

NIPQUAD宏:

// use printf("%d.%d.%d.%d",NIPQUAD(iphdr->saddr));
#define NIPQUAD(addr) \
    ((unsigned char *)&addr)[0], \
    ((unsigned char *)&addr)[1], \
    ((unsigned char *)&addr)[2], \
    ((unsigned char *)&addr)[3]

【问题讨论】:

  • IPv4 的长度为 32 位。 1 个字节代表 IP 地址的 1 部分。所以对于 4 个部分,4 个字节。
  • 更改字节顺序意味着反转字节。将字节 1 与 4、2 与 3 交换。
  • 例如IP 地址 192.168.20.42 可以表示为 32 位(十六进制)的单个整数 0xc0a8142a。在十六进制中,c0 是 192,a8 是 168,14 是 20,2a 是 42。
  • 对于上面的例子,颠倒字节顺序会导致:0x2a14a8c0。
  • @kiner_shah oookey...明白了!

标签: arrays c pointers byte


【解决方案1】:

如何使用 uint_32(无符号整数)来保存 IP 地址?

IP 地址只是 0 到 255 之间的 4 个数字,即 4 个字节。它非常适合uint32_t:

1.2.3.4       -> 0x01020304
127.0.0.1     -> 0x7F000001
192.168.10.20 -> 0xC0A80A14

在 C 语言中,如果您的机器是 big endian,则类似于:

uint32_t addr;
uint8_t *tmp = (uint8_t *)&addr;

tmp[0] = 192;
tmp[1] = 168;
tmp[2] = 10;
tmp[3] = 20;

如果是little endian,则需要以相反的顺序分配IP的4个字节以获得相同的值。 Endianness 可以使用编译器指令或运行时检查来检查(几乎总是第一个选项)。

这个函数是如何从字符串 IP 转换为网络字节序的?

inet_pton 只是将字符串中的每个数字转换为一个字节,然后像上面显示的那样组成最终值。这可以通过使用strtok 或类似函数将字符串拆分为各个点(.)来完成。由于网络顺序意味着 big endian,因此您的最终结果将始终是 big endian,因此无需交换东西。


inet_pton()在网上有几种实现方式,这里有两种:

事实证明,这两个实现都不使用strtok()、atoi()、strtol() 或任何类似函数,而是一次一个字符地手动解析字符串。转换是逐个字符完成的。

【讨论】:

  • 我认为inet_pton()的算法应该是这样的:首先用strtok从IP字符串中提取字符串数字,然后用atoi转换数字,然后像1.2.3.4一样反转它的字节顺序-> 0x04030201..如果我错了,请纠正我。
  • @GeekyJarvis 是的,这通常是有道理的,但要准确地说,(1)它不能直接在提供的字符串上使用strtok(),因为strtok() 会修改字符串(即@987654336 @),因此更有可能使用strchr() 来定位点,或者将其复制到临时缓冲区中,然后使用strtok(); (2) 它不能使用atoi(),因为该函数没有检测到错误,它几乎可以肯定使用strtol()。
  • @MarcoBoneli 啊……不明白,抱歉……如果可能的话,你能在上面的答案中分享它的实现吗……很荣幸。
  • @MarcoBoneli 我试过但没有按预期工作。
  • @GeekyJarvis 你可以很容易地看到inet_pton 是如何在 GLIBC 或 Musl LIBC 中实现的,因为它们是开源的。我在答案中添加了两个链接,请查看。如果您对特定实现有疑问,我建议您提出一个单独的问题,在哪里显示代码并解释什么不起作用。
【解决方案2】:

我想你得到了另一个答案,这里是实现部分:

这个函数是如何从字符串 IP 转换为网络字节顺序的?如果有人提供代码,我将不胜感激。


int inet_pton(int af, const char *restrict s, void *restrict a0)
{
    uint16_t ip[8];
    unsigned char *a = a0;
    int i, j, v, d, brk=-1, need_v4=0;

    if (af==AF_INET) {
        for (i=0; i<4; i++) {
            for (v=j=0; j<3 && isdigit(s[j]); j++)
                v = 10*v + s[j]-'0';
            if (j==0 || (j>1 && s[0]=='0') || v>255) return 0;
            a[i] = v;
            if (s[j]==0 && i==3) return 1;
            if (s[j]!='.') return 0;
            s += j+1;
        }
        return 0;
    } else if (af!=AF_INET6) {
        errno = EAFNOSUPPORT;
        return -1;
    }

    if (*s==':' && *++s!=':') return 0;

    for (i=0; ; i++) {
        if (s[0]==':' && brk<0) {
            brk=i;
            ip[i&7]=0;
            if (!*++s) break;
            if (i==7) return 0;
            continue;
        }
        for (v=j=0; j<4 && (d=hexval(s[j]))>=0; j++)
            v=16*v+d;
        if (j==0) return 0;
        ip[i&7] = v;
        if (!s[j] && (brk>=0 || i==7)) break;
        if (i==7) return 0;
        if (s[j]!=':') {
            if (s[j]!='.' || (i<6 && brk<0)) return 0;
            need_v4=1;
            i++;
            break;
        }
        s += j+1;
    }
    if (brk>=0) {
        memmove(ip+brk+7-i, ip+brk, 2*(i+1-brk));
        for (j=0; j<7-i; j++) ip[brk+j] = 0;
    }
    for (j=0; j<8; j++) {
        *a++ = ip[j]>>8;
        *a++ = ip[j];
    }
    if (need_v4 && inet_pton(AF_INET, (void *)s, a-4) <= 0) return 0;
    return 1;
}

【讨论】:

  • 除了盲目地从 Mustl libc (here) 复制粘贴一些代码而没有任何有用的解释,您至少可以链接或提及它。
猜你喜欢
  • 2013-02-28
  • 2010-09-28
  • 2016-04-13
  • 2014-10-29
  • 2018-02-14
  • 2011-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多