diff --git a/default.config b/default.config index 69c3882..86a7def 100755 --- a/default.config +++ b/default.config @@ -172,12 +172,16 @@ GroupFile BlockIP # IPSubstituting ,,..... -# 替换 DNS 数据包中的 IP 地址(只支持 IPv4 地址) (since 5.0.1) +# 替换 DNS 数据包中的 IP 地址 +# 要替换的 IP 支持 CIDR 写法,替换为的 IP 不支持,并且必须保持相同版本 # 例如: # IPSubstituting 127.0.0.1 1.2.0.127 -# 的效果是把 DNS 数据包中所有的 127.0.0.1 地址替换为 1.2.0.127 +# 的效果是把 DNS 数据包中所有的 127.0.0.1 地址替换为 1.2.0.127 (since 5.0.1) +# IPSubstituting 173.245.48.0/20 173.245.48.22 +# 的效果是把 DNS 数据包中所有 173.245.48.0/20 内的地址替换为 173.245.48.22 (since 6.5.0) # 仅替换通过服务器(TCP 和 UDP)查询而来的 DNS 数据包,对于缓存中和 Hosts 中的结果无效 # 多条替换项目用半角逗号 (`,') 分隔,也可以写多行 `IPSubstituting' +# 先匹配 IP 规则,再匹配 CIDR 规则 IPSubstituting # BlockNegativeResponse diff --git a/default.en.config b/default.en.config index c6998a8..40f7ea6 100755 --- a/default.en.config +++ b/default.en.config @@ -201,12 +201,16 @@ GroupFile BlockIP # IPSubstituting ,,..... -# Replace an IP with another in every response from servers(only IPv4 supported) (since 5.0.1) +# Replace an IP with another in every response from servers +# The IPs to be replaced support CIDR form, but the desired IP does NOT, and MUST keep the same version # e.g. # IPSubstituting 127.0.0.1 1.2.0.127 -# Which is, replacing 127.0.0.1 with 1.2.0.127 in every response from servers +# Which is, replacing 127.0.0.1 with 1.2.0.127 in every response from servers (since 5.0.1) +# IPSubstituting 173.245.48.0/20 173.245.48.22 +# will replace all IPs in 173.245.48.0/20 with 173.245.48.22 in every response from servers (since 6.5.0) # This option only affects responses from servers, but not responses from hosts and cache # Adjacent items should be separated by a comma, or split them to different lines of `IPSubstituting' +# IP rules are tested first, and then CIDR rules. IPSubstituting # BlockNegativeResponse diff --git a/ipchunk.c b/ipchunk.c index a89f96e..f326250 100755 --- a/ipchunk.c +++ b/ipchunk.c @@ -2,33 +2,235 @@ #include "utils.h" #include "ipchunk.h" -static int Compare(IpElement *_1, IpElement *_2) +static const char *Z0 = NULL; +static const char *Z4 = "\x01"; +static const char *Z6noz = "\x02"; + +/* +IPv6: https://datatracker.ietf.org/doc/html/rfc8200 +Zone: https://datatracker.ietf.org/doc/html/rfc6874 +CIDR: https://datatracker.ietf.org/doc/html/rfc4632 + https://datatracker.ietf.org/doc/html/rfc4291#section-2.3 +*/ + +static int IpAddr_BitLength(IpAddr *ipAddr) { - if( _1->IpLength != _2->IpLength ) + if( ipAddr->Zone == Z0 ) { - return _1->IpLength - _2->IpLength; + return 0; + } else if( ipAddr->Zone == Z4 ) + { + return 32; + } + + return 128; +} + +static int IpAddr_Is6(IpAddr *ipAddr) +{ + return ipAddr->Zone != Z0 && ipAddr->Zone != Z4; +} + +static int IpAddr_HasZone(IpAddr *ipAddr) +{ + return IpAddr_Is6(ipAddr) && ipAddr->Zone != Z6noz; +} + +static int IpAddr_IsValid(IpAddr *ipAddr) +{ + return ipAddr->Zone != Z0; +} + +static void IpAddr_SetPrefix4(unsigned char Addr[16]) +{ + memset(Addr, 0, 10); + *(uint16_t *)(Addr + 10) = 0xffff; +} + +static void IpAddr_From4(unsigned char Addr[4], IpAddr *ipAddr) +{ + ipAddr->Zone = Z4; + IpAddr_SetPrefix4(ipAddr->Addr); + *(uint32_t *)(ipAddr->Addr + 12) = *(uint32_t *)Addr; +} + +static void IpAddr_From6(unsigned char Addr[16], IpAddr *ipAddr) +{ + ipAddr->Zone = Z6noz; + memcpy(ipAddr->Addr, Addr, 16); +} + +static int IpAddr_Parse(const char *s, IpAddr *ipAddr) +{ + const char *p = s; + + for(; *p; ++p) + { + switch( *p ) + { + case '.': + ipAddr->Zone = Z4; + IpAddr_SetPrefix4(ipAddr->Addr); + return IPv4AddressToNum(s, ipAddr->Addr + 12) != 4; + case ':': + p = strchr(p, '%'); + if( p == NULL ) + { + ipAddr->Zone = Z6noz; + } else { + ipAddr->Zone = p + 1; + } + return IPv6AddressToNum(s, ipAddr->Addr) != 16; + case '%': + break; + } + } + + ipAddr->Zone = Z0; + memset(ipAddr->Addr, 0, 16); + return -1; +} + +static BOOL IpSet_IsSingleIp(IpSet *ipSet) +{ + return ipSet->PrefixBits != 0 && ipSet->PrefixBits == IpAddr_BitLength(&(ipSet->Ip)); +} + +static int IpSet_Parse(const char *s, const char *p, IpSet *ipSet) +{ + int n, L; + IpAddr *ipAddr = &(ipSet->Ip); + + if( IpAddr_Parse(s, ipAddr) != 0 ) + { + return -1; + } + + L = IpAddr_BitLength(ipAddr); + + if( p != NULL && *p ) + { + n = atoi(p); + if( n < 0 ) + { + n = -1; + } else if( n > L ) { + n = L; + } + + if( IpAddr_Is6(ipAddr) ) + { + ipSet->Ip.Zone = Z6noz; + } + + } else { + n = L; + } + + ipSet->PrefixBits = n; + + return 0; +} + + +static int Contain(IpElement *New, IpElement *Elm) +{ + IpSet *ipSetNew = &(New->IpSet); + IpSet *ipSetElm = &(Elm->IpSet); + IpAddr *ipAddrNew = &(ipSetNew->Ip); + IpAddr *ipAddrElm = &(ipSetElm->Ip); + int BitsNew; + int BitsElm; + + if( IpAddr_IsValid(ipAddrElm) == FALSE ) + { + return -1; + } + + BitsNew = IpAddr_BitLength(ipAddrNew); + BitsElm = IpAddr_BitLength(ipAddrElm); + + // 1st: type + if( BitsNew != BitsElm ) + { + return BitsNew - BitsElm; } else { - if( _1->IpLength == 4 ) + unsigned char *bn = ipAddrNew->Addr; + unsigned char *be = ipAddrElm->Addr; + int prefixBitsNew = ipSetNew->PrefixBits; + int prefixBitsElm = ipSetElm->PrefixBits; + int ret = 0; + + // 2nd: prefix bits + if( prefixBitsNew < prefixBitsElm ) { - return _1->Ip.Ipv4 - _2->Ip.Ipv4; - } else { - return memcmp(_1->Ip.Ipv6, _2->Ip.Ipv6, _1->IpLength); + return 1; + } + + // 3rd: prefix value + if( BitsElm == 32 ) + { + bn += 12; + be += 12; + } + + for(; prefixBitsElm >= 32; prefixBitsElm -= 32) + { + ret = memcmp(bn, be, 4); + + if(ret != 0) + { + return ret; + } + + bn += 4; + be += 4; } + + if( IpSet_IsSingleIp(ipSetElm) == FALSE && prefixBitsElm > 0 ) { + uint32_t u32New, u32Elm, mask; + + mask = htonl(~(~0U >> prefixBitsElm)); + u32New = *(uint32_t *)bn & mask; + u32Elm = *(uint32_t *)be & mask; + ret = memcmp(&u32New, &u32Elm, 4); + } + + // 4th: zone + if( ret == 0 ) + { + if( IpAddr_HasZone(ipAddrElm) ) + { + if( IpAddr_HasZone(ipAddrNew) ) + { + return strcmp(ipAddrNew->Zone, ipAddrElm->Zone); + } else { + return 1; + } + } + } + + return ret; } } + void IpChunk_Free(IpChunk *ic) { - ic->Chunk.Free(&(ic->Chunk)); + ic->AddrChunk.Free(&(ic->AddrChunk)); + ic->CidrChunk.Free(&(ic->CidrChunk)); ic->Datas.Free(&(ic->Datas)); + ic->Extra.Free(&(ic->Extra)); } int IpChunk_Init(IpChunk *ic) { - IpElement Root; - Root.IpLength = 10; /* 4 < 10 < 16 */ + if( Bst_Init(&(ic->AddrChunk), sizeof(IpElement), (CompareFunc)Contain) != 0 ) + { + return -1; + } - if( Bst_Init(&(ic->Chunk), sizeof(IpElement), (CompareFunc)Compare) != 0 ) + if( Bst_Init(&(ic->CidrChunk), sizeof(IpElement), (CompareFunc)Contain) != 0 ) { return -1; } @@ -38,149 +240,101 @@ int IpChunk_Init(IpChunk *ic) return -1; } - if( ic->Chunk.Add(&(ic->Chunk), &Root) == NULL ) + if( StableBuffer_Init(&(ic->Extra)) != 0 ) { - return -37; + return -1; } return 0; } int IpChunk_Add(IpChunk *ic, - uint32_t Ip, + const char *Ip, int Type, const void *Data, uint32_t DataLength ) { - StableBuffer *sb = &(ic->Datas); - + char *p; IpElement New; - New.IpLength = 4; - New.Ip.Ipv4 = Ip; - New.Type = Type; - New.Data = NULL; + const IpElement *elm; - if( Data != NULL ) + char *ip = ic->Extra.Add(&(ic->Extra), Ip, strlen(Ip) + 1, FALSE); + if( ip == NULL ) { - New.Data = sb->Add(sb, Data, DataLength, TRUE); + return -1; } - return ic->Chunk.Add(&(ic->Chunk), &New) == NULL; -} - -int IpChunk_AddFromString(IpChunk *ic, - const char *Ip, - int Type, - const char *Data, - uint32_t DataLength - ) -{ - uint32_t IpNum = 0; - - IPv4AddressToNum(Ip, &IpNum); - - return IpChunk_Add(ic, IpNum, Type, Data, DataLength); -} + p = ip; + for(; *p; ++p) + { + if( *p == '/' ) + { + *p = 0; + p++; + break; + } + } -int IpChunk_Add6(IpChunk *ic, const char *Ipv6, int Type, const char *Data, uint32_t DataLength) -{ - StableBuffer *sb = &(ic->Datas); + if( IpSet_Parse(ip, p, &(New.IpSet)) != 0 ) + { + return -1; + } - IpElement New; - New.IpLength = 16; - memcpy(New.Ip.Ipv6, Ipv6, 16); New.Type = Type; New.Data = NULL; if( Data != NULL ) { - New.Data = sb->Add(sb, Data, DataLength, TRUE); + New.Data = ic->Datas.Add(&(ic->Datas), Data, DataLength, TRUE); } - return ic->Chunk.Add(&(ic->Chunk), &New) == NULL; -} - -int IpChunk_Add6FromString(IpChunk *ic, - const char *Ip, - int Type, - const char *Data, - uint32_t DataLength - ) -{ - char IpNum[16]; - - IPv6AddressToNum(Ip, IpNum); - return IpChunk_Add6(ic, IpNum, Type, Data, DataLength); -} - -int IpChunk_AddAnyFromString(IpChunk *ic, - const char *Ip, - int Type, - const char *Data, - uint32_t DataLength - ) -{ - if( strchr(Ip, ':') != NULL ) + if( IpSet_IsSingleIp(&(New.IpSet)) ) { - return IpChunk_Add6FromString(ic, Ip, Type, Data, DataLength); + elm = ic->AddrChunk.Add(&(ic->AddrChunk), &New); } else { - return IpChunk_AddFromString(ic, Ip, Type, Data, DataLength); + elm = ic->CidrChunk.Add(&(ic->CidrChunk), &New); } + + return elm == NULL; } -BOOL IpChunk_Find(IpChunk *ic, uint32_t Ip, int *Type, const char **Data) +BOOL IpChunk_Find(IpChunk *ic, unsigned char *Ip, int IpBytes, int *Type, const char **Data) { IpElement Key; - const IpElement *Result; + const IpElement *Result = NULL; if( ic == NULL ) { return FALSE; } - Key.IpLength = 4; - Key.Ip.Ipv4 = Ip; - Key.Type = 0; - Key.Data = NULL; - - Result = ic->Chunk.Search(&(ic->Chunk), &Key, NULL); - - if( Result == NULL ) + switch( IpBytes ) { + case 4: + IpAddr_From4(Ip, &(Key.IpSet.Ip)); + Key.IpSet.PrefixBits = 32; + break; + case 16: + IpAddr_From6(Ip, &(Key.IpSet.Ip)); + Key.IpSet.PrefixBits = 128; + break; + default: return FALSE; - } else { - if( Type != NULL ) - { - *Type = Result->Type; - } - - if( Data != NULL ) - { - *Data = Result->Data; - } - - return TRUE; } -} -BOOL IpChunk_Find6(IpChunk *ic, const char *Ipv6, int *Type, const char **Data) -{ - IpElement Key; - const IpElement *Result; - - if( ic == NULL ) - { - return FALSE; - } - - Key.IpLength = 16; - memcpy(Key.Ip.Ipv6, Ipv6, 16); Key.Type = 0; Key.Data = NULL; - Result = ic->Chunk.Search(&(ic->Chunk), &Key, NULL); + if( IpSet_IsSingleIp(&(Key.IpSet)) ) + { + Result = ic->AddrChunk.Search(&(ic->AddrChunk), &Key, NULL); + } + if( Result == NULL ) + { + Result = ic->CidrChunk.Search(&(ic->CidrChunk), &Key, NULL); + } if( Result == NULL ) { diff --git a/ipchunk.h b/ipchunk.h index cbb29a8..f4cc80c 100755 --- a/ipchunk.h +++ b/ipchunk.h @@ -5,19 +5,32 @@ #include "stablebuffer.h" #include "common.h" +typedef struct _IpAddr { + unsigned char Addr[16]; /* Both v4 and v6. In big-endian order. */ + const char *Zone; +} IpAddr; + +typedef struct _IpPort { + IpAddr Ip; + uint16_t Port; +} IpPort; + +typedef struct _IpSet { + IpAddr Ip; + int PrefixBits; +} IpSet; + typedef struct _IpElement { - int IpLength; - union { - uint32_t Ipv4; - char Ipv6[16]; - } Ip; - int Type; - void *Data; + IpSet IpSet; + int Type; + void *Data; } IpElement; typedef struct _IpChunk{ - Bst Chunk; + Bst AddrChunk; + Bst CidrChunk; StableBuffer Datas; + StableBuffer Extra; } IpChunk; void IpChunk_Free(IpChunk *ic); @@ -25,38 +38,12 @@ void IpChunk_Free(IpChunk *ic); int IpChunk_Init(IpChunk *ic); int IpChunk_Add(IpChunk *ic, - uint32_t Ip, + const char *Ip, int Type, const void *Data, uint32_t DataLength ); -int IpChunk_AddFromString(IpChunk *ic, - const char *Ip, - int Type, - const char *Data, - uint32_t DataLength - ); - -int IpChunk_Add6(IpChunk *ic, const char *Ipv6, int Type, const char *Data, uint32_t DataLength); - -int IpChunk_Add6FromString(IpChunk *ic, - const char *Ip, - int Type, - const char *Data, - uint32_t DataLength - ); - -int IpChunk_AddAnyFromString(IpChunk *ic, - const char *Ip, - int Type, - const char *Data, - uint32_t DataLength - ); - -BOOL IpChunk_Find(IpChunk *ic, uint32_t Ip, int *Type, const char **Data); - -BOOL IpChunk_Find6(IpChunk *ic, const char *Ipv6, int *Type, const char **Data); - +BOOL IpChunk_Find(IpChunk *ic, unsigned char *Ip, int IpBytes, int *Type, const char **Data); #endif // IPCHUNK_H_INCLUDED diff --git a/ipmisc.c b/ipmisc.c index f80879f..9161a18 100755 --- a/ipmisc.c +++ b/ipmisc.c @@ -4,12 +4,12 @@ static int IPMisc_AddBlockFromString(IPMisc *m, const char *Ip) { - return IpChunk_AddAnyFromString(&(m->c), - Ip, - (int)IP_MISC_TYPE_BLOCK, - NULL, - 0 - ); + return IpChunk_Add(&(m->c), + Ip, + (int)IP_MISC_TYPE_BLOCK, + NULL, + 0 + ); } static int IPMisc_AddSubstituteFromString(IPMisc *m, @@ -23,24 +23,24 @@ static int IPMisc_AddSubstituteFromString(IPMisc *m, IPv6AddressToNum(Substituter, IpSubstituter); - return IpChunk_Add6FromString(&(m->c), - Ip, - (int)IP_MISC_TYPE_SUBSTITUTE, - IpSubstituter, - 16 - ); + return IpChunk_Add(&(m->c), + Ip, + (int)IP_MISC_TYPE_SUBSTITUTE, + IpSubstituter, + 16 + ); } else { /* IPv4 */ char IpSubstituter[4]; IPv4AddressToNum(Substituter, IpSubstituter); - return IpChunk_AddFromString(&(m->c), - Ip, - (int)IP_MISC_TYPE_SUBSTITUTE, - IpSubstituter, - 4 - ); + return IpChunk_Add(&(m->c), + Ip, + (int)IP_MISC_TYPE_SUBSTITUTE, + IpSubstituter, + 4 + ); } } @@ -86,28 +86,23 @@ static int IPMisc_Process(IPMisc *m, switch( i.Type ) { case DNS_TYPE_A: - if( IpChunk_Find(&(m->c), - *(uint32_t *)RowDataPos, - (int *)&ActionType, - &Data) - == FALSE ) - { - continue; - } DataLength = 4; break; - case DNS_TYPE_AAAA: - if( IpChunk_Find6(&(m->c), RowDataPos, (int *)&ActionType, &Data) == FALSE ) - { - continue; - } DataLength = 16; break; - default: continue; - break; + } + + if( IpChunk_Find(&(m->c), + (unsigned char *)RowDataPos, + DataLength, + (int *)&ActionType, + &Data) + == FALSE ) + { + continue; } switch( ActionType ) @@ -156,21 +151,20 @@ int IPMisc_Init(IPMisc *m) return 0; } -/** Singleton */ +/** Mapping */ -static IPMisc IpMiscSingleton; -static BOOL SingletonInited = FALSE; +static IPMisc IpMiscMapping; +static BOOL MappingInited = FALSE; -static void IpMiscSingleton_Cleanup(void) +static void IpMiscMapping_Cleanup(void) { - IPMisc_Free(&IpMiscSingleton); + IPMisc_Free(&IpMiscMapping); } -int IpMiscSingleton_Init(ConfigFileInfo *ConfigInfo) +int IpMiscMapping_Init(ConfigFileInfo *ConfigInfo) { StringList *BlockIP = ConfigGetStringList(ConfigInfo, "BlockIP"); - StringList *IPSubstituting = - ConfigGetStringList(ConfigInfo, "IPSubstituting"); + StringList *IPSubstituting = ConfigGetStringList(ConfigInfo, "IPSubstituting"); BOOL BlockNegative = ConfigGetBoolean(ConfigInfo, "BlockNegativeResponse"); @@ -181,13 +175,13 @@ int IpMiscSingleton_Init(ConfigFileInfo *ConfigInfo) return 0; } - if( IPMisc_Init(&IpMiscSingleton) != 0 ) + if( IPMisc_Init(&IpMiscMapping) != 0 ) { return -147; } - atexit(IpMiscSingleton_Cleanup); + atexit(IpMiscMapping_Cleanup); - IpMiscSingleton.SetBlockNegative(&IpMiscSingleton, BlockNegative); + IpMiscMapping.SetBlockNegative(&IpMiscMapping, BlockNegative); if( BlockIP != NULL ) { @@ -200,7 +194,7 @@ int IpMiscSingleton_Init(ConfigFileInfo *ConfigInfo) while( (Itr = i.Next(&i)) != NULL ) { - IpMiscSingleton.AddBlockFromString(&IpMiscSingleton, Itr); + IpMiscMapping.AddBlockFromString(&IpMiscMapping, Itr); } } @@ -217,25 +211,27 @@ int IpMiscSingleton_Init(ConfigFileInfo *ConfigInfo) Itr2 = i.Next(&i); while( Itr != NULL && Itr2 != NULL ) { - IpMiscSingleton.AddSubstituteFromString(&IpMiscSingleton, Itr, Itr2); + IpMiscMapping.AddSubstituteFromString(&IpMiscMapping, Itr, Itr2); Itr = i.Next(&i); Itr2 = i.Next(&i); } } - SingletonInited = TRUE; + MappingInited = TRUE; return 0; } -int IPMiscSingleton_Process(IHeader *h /* Entity followed */) +int IPMiscMapping_Process(MsgContext *MsgCtx) { - if( !SingletonInited ) + IHeader *h = (IHeader *)MsgCtx; + + if( !MappingInited ) { return IP_MISC_NOTHING; } - return IpMiscSingleton.Process(&IpMiscSingleton, + return IpMiscMapping.Process(&IpMiscMapping, IHEADER_TAIL(h), h->EntityLength ); diff --git a/ipmisc.h b/ipmisc.h index da6ef6c..891e6ab 100755 --- a/ipmisc.h +++ b/ipmisc.h @@ -38,10 +38,10 @@ struct _IPMisc{ int IPMisc_Init(IPMisc *m); -/** Singleton */ +/** Mapping */ -int IpMiscSingleton_Init(ConfigFileInfo *ConfigInfo); +int IpMiscMapping_Init(ConfigFileInfo *ConfigInfo); -int IPMiscSingleton_Process(IHeader *h /* Entity followed */); +int IPMiscMapping_Process(MsgContext *MsgCtx); #endif // IPMISC_H_INCLUDED diff --git a/mmgr.c b/mmgr.c index f566bec..36a0601 100755 --- a/mmgr.c +++ b/mmgr.c @@ -716,15 +716,21 @@ int MMgr_Init(ConfigFileInfo *ConfigInfo) { int ret; - if( Filter_Init(ConfigInfo) != 0 ) + EnableUDPtoTCP = ConfigGetBoolean(ConfigInfo, "EnableUDPtoTCP"); + EnableTCPtoUDP = ConfigGetBoolean(ConfigInfo, "EnableTCPtoUDP"); + + RWLock_Init(ModulesLock); + + ret = Modules_Load(ConfigInfo); + if( ret != 0 ) { - return -159; + return ret; } + atexit(Modules_Cleanup); - /* Hosts & Cache */ - if( Hosts_Init(ConfigInfo) != 0 ) + if( Filter_Init(ConfigInfo) != 0 ) { - return -165; + return -159; } if( DNSCache_Init(ConfigInfo) != 0 ) @@ -732,23 +738,20 @@ int MMgr_Init(ConfigFileInfo *ConfigInfo) return -164; } - if( IpMiscSingleton_Init(ConfigInfo) != 0 ) + if( IpMiscMapping_Init(ConfigInfo) != 0 ) { return -176; } - /* Ordinary modeles */ - RWLock_Init(ModulesLock); - - EnableUDPtoTCP = ConfigGetBoolean(ConfigInfo, "EnableUDPtoTCP"); - EnableTCPtoUDP = ConfigGetBoolean(ConfigInfo, "EnableTCPtoUDP"); - - ret = Modules_Load(ConfigInfo); - atexit(Modules_Cleanup); + /* The last: reloading */ + if( Hosts_Init(ConfigInfo) != 0 ) + { + return -165; + } INFO("Loading Configuration completed.\n"); - return ret; + return 0; } int Modules_Update(void) diff --git a/tcpm.c b/tcpm.c index 3e6b4cb..68f1022 100755 --- a/tcpm.c +++ b/tcpm.c @@ -707,7 +707,7 @@ static int TcpM_Works(TcpM *m) NULL ); - switch( IPMiscSingleton_Process(Header) ) + switch( IPMiscMapping_Process(MsgCtx) ) { case IP_MISC_NOTHING: break; diff --git a/udpm.c b/udpm.c index 25daeac..60299b7 100755 --- a/udpm.c +++ b/udpm.c @@ -186,7 +186,7 @@ static void UdpM_Works(UdpM *m) NULL ); - switch( IPMiscSingleton_Process(Header) ) + switch( IPMiscMapping_Process(MsgCtx) ) { case IP_MISC_NOTHING: break; diff --git a/utils.c b/utils.c index 26d61e8..f47e04c 100755 --- a/utils.c +++ b/utils.c @@ -508,7 +508,8 @@ int IPv6AddressToNum(const char *asc, void *Buffer) break; } } - return 0; + + return 16; } int IPv4AddressToNum(const char *asc, void *Buffer)