1.测试用例
.1.1.111//测试开头为点的情况
.1.1.1//长度不够+开头为点
1.1.1.//结尾为点+长度不够
11.11.11.//结尾为点
11..11.1//中间存在连续点
..111.111//开始存在连续点
111.111..//结尾存在连续点
1.11111.11111.1111//数值不合法,长度不合法
1.257.1.1//数值不合法
122.524.13.1//数值不合法
1.1.1.256//数值不合法
111.112.113//数值元数和点数不对
111.112//数值元数和点数不对
111.//数值元数和点数不对
01.3.2.56//开头有非法的零
-100.22.45.134//非法字符
1.1.1.1//合法
2.3.5.1//合法
111.111.1.255//合法
0.1.3.4//合法
114.114.114.114//合法
注:notepad++中,把//[\S]*\r\n替换为\r\n可去除注释
2.修改后的代码
//#define __IP_DEBUG__
int Char_Num_Check(const char c);/*字符是否为数字*/
int Section_Legal_Check(const int sec);/*地址段是否合法*/
inline int Char_Num_Check(const char c) {return (c >= '0' && c <= '9');}
inline int Section_Legal_Check(const int sec) {return (sec >= 0 && sec <= 255);}
/**************************************************
IP合法性鉴别函数
功能:判断一个IP是否合法并打印出不合法的原因
原因包括:1.字符串长度非法
2.非法数字规则(零在最高位)
3.非法的取值(不在0~255之间)
4.非法的点(连续的点)
5.非法的地址段/点数目
**************************************************/
enum NextCharType
{
NUM=1,
DOT=0,
NUMORDOT=-1
};
int is_valid_ip(const char *ip)
{
int section = 0;
int DotCount = 0;//实现点计数
int NumCount=0;//实现section计数
int num_dot=NUM;//此时应接收数字还是点,1表示应该接收数字,0表示应该接收点,-1表示任意
int count=3;//用于计算每个数字段的位数,避免单个数字段过长
int flag_num=0;//读取过数字就置为1,方便后续检验合法性
if(strlen(ip)>15||strlen(ip)<7)//预判,ip太短或太长
{
#ifdef __IP_DEBUG__
printf("illegal length!\n");
#endif
return 0;
}
while (*ip)
{
while(*ip!='.'&&*ip!='\0')
{
if (Char_Num_Check(*ip))
{
flag_num=1;
if(count==3&&*ip=='0')//最高位是0,下一位一定是点或者\0
{
count=0;
num_dot=DOT;
ip++;
break;
}
else
{
num_dot=NUMORDOT;//最高位不为0时,下一位可能是数字或者点
section = section * 10 + (*ip - '0');//计入,最高位为0时由于section已经为0,不用此步
count--;//可用的数字位数减1
ip++;
if(count==0)//可用数字位耗尽,下一位必定为点或者\0
{
num_dot=DOT;
break;
}
}
}
else//出现非法字符
{
#ifdef __IP_DEBUG__
printf("illegal char!\n");
#endif
return 0;
}
}
if(*ip=='.')//为点
{
//避免出现连续点
if(num_dot==NUM)
{
#ifdef __IP_DEBUG__
printf("illegal dots!\n");
#endif
return 0;
}
else
{
num_dot=NUM;//点后面必定是数字
}
DotCount++;//点计入
count=3;//后续最多出现3位数字
ip++;
if (DotCount > 3)
{
#ifdef __IP_DEBUG__
printf("too many dots!\n");
#endif
return 0;
}
}
//section结束,校验、重置并计数
if(flag_num)//确实经过了读取数字的while循环
{
if (Section_Legal_Check(section))//校验section合法性并清零
{
section = 0;
}
else//非法section值
{
#ifdef __IP_DEBUG__
printf("illegal section value!\n");
#endif
return 0;
}
flag_num=0;
NumCount++;
}
}
if (3 == DotCount&&NumCount==4)//点数目为3,数字段数目为4
{
return 1;
}
else
{
#ifdef __IP_DEBUG__
printf("illegal section/dot number!\n");
#endif
return 0;
}
}
3.新版官方代码
#ifndef INADDR_NONE
#define INADDR_NONE 0xffffffff
#endif
int
inet_aton(const char *cp, struct in_addr *addr)
{
addr->s_addr = inet_addr(cp);
return (addr->s_addr == INADDR_NONE) ? 0 : 1;
}
4.测试程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc,char*argv[])
{
FILE*fp;
char buf[1000];
struct in_addr *in_ad;
in_ad=malloc(sizeof(struct in_addr));
if(NULL==(fp=fopen(argv[1],"r")))//只读方式打开文件
{
#ifdef __IP_DEBUG__
printf("error in open file!\n");
#endif
exit(1);
}
while(!feof(fp))
{
memset(buf, 0, 15);//IPV4地址最大15字节,故不需要全部清零
fgets(buf, sizeof(buf) - 1, fp); // 包含了\n
buf[strlen(buf)-2]='\0';//修改buf尾部,去除\r\n
printf("buf=%s\n", buf);
#ifdef __IP_DEBUG__
printf("length=%d\n", (int)strlen(buf));
#endif
//对比结果:修改后VS93版VS官方新版
//自己的
if(is_valid_ip(buf)) printf("PASS!\n");
else printf("FAIL!\n");
if(inet_aton(buf,in_ad)) printf("PASS!\n");
else printf("FAIL!\n");
if(inet_aton2(buf,in_ad)) printf("PASS!\n");
else printf("FAIL!\n");
printf("%s\n",inet_ntoa(*in_ad));//打印地址
in_ad->s_addr=0;//清零
}
fclose(fp);
return 0;
}
5.测试结果
buf=.1.1.111
FAIL!
FAIL!
PASS!
0.1.1.111
buf=.1.1.1
FAIL!
FAIL!
PASS!
0.1.1.1
buf=1.1.1.
FAIL!
FAIL!
PASS!
1.1.1.0
buf=11.11.11.
FAIL!
FAIL!
PASS!
11.11.11.0
buf=11..11.1
FAIL!
FAIL!
PASS!
11.0.11.1
buf=..111.111
FAIL!
FAIL!
PASS!
0.0.111.111
buf=111.111..
FAIL!
FAIL!
PASS!
111.111.0.0
buf=1.11111.11111.1111
FAIL!
FAIL!
FAIL!
0.0.0.0
buf=1.257.1.1
FAIL!
FAIL!
FAIL!
0.0.0.0
buf=122.524.13.1
FAIL!
FAIL!
FAIL!
0.0.0.0
buf=1.1.1.256
FAIL!
FAIL!
FAIL!
0.0.0.0
buf=111.112.113
FAIL!
PASS!
PASS!
111.112.113.0
buf=111.112
FAIL!
PASS!
PASS!
111.112.0.0
buf=111.
FAIL!
FAIL!
PASS!
111.0.0.0
buf=01.3.2.56
FAIL!
PASS!
PASS!
1.3.2.56
buf=-100.22.45.134
FAIL!
FAIL!
FAIL!
0.0.0.0
buf=1.1.1.1
PASS!
PASS!
PASS!
1.1.1.1
buf=2.3.5.1
PASS!
PASS!
PASS!
2.3.5.1
buf=111.111.1.255
PASS!
PASS!
PASS!
111.111.1.255
buf=0.1.3.4
PASS!
PASS!
PASS!
0.1.3.4
buf=114.114.114.114
PASS!
PASS!
PASS!
114.114.114.114
buf=
FAIL!
FAIL!
PASS!
0.0.0.0
可以看出,93版程序有很多误判,填充规则也不太符合常识(段数不够时,扩充最后一个地址段,高位填零),新版和修改后的版本主要是存在一些歧异,本身都不能算错:
1.地址段不够的,用零填充。
2.最高位的零会被忽略。
3.如果出现连续的点,则认为两个点之间默认填充0。
源码及测例下载链接: