简介
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 %
fh@Feihu-3 glibc-2.34 % vim include/libc-symbols.h
里面涉及到强引用和弱引用,请自行查阅相关资料。
接口源码实现
#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);
}