如何设计优雅的IP检查API以支持IPv4和IPv6

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何设计优雅的IP检查API以支持IPv4和IPv6相关的知识,希望对你有一定的参考价值。

在我的项目中,有很多关于IP检查的要求。因为我们为网络设备编写了许多功能。在某些情况下,我们不需要环回IP和广播IP,在其他情况下,它是有效的。有些功能支持IPv6,有些则不支持。

所以我设计了一些基本的API:

Ext.define('Common.util.IPUtils', {
  singleton: true,
  
  isIpv4 : function(ip){},
  isMulticast_v4 : function(ip){},
  isBroadcast_v4 : function(ip, mask){},
  isLoopback_v4 : function(ip){},
  isAny_v4 : function(ip){},
  isIpv6 : function(ip){},
  isMulticast_v6 : function(ip){},
  isBroadcast_v6 : function(ip){},
  isLoopback_v6 : function(ip){},
  isAny_v6 : function(ip){}

});

通过这种方式,开发人员必须这样调用:

(Common.Util.IPUtil.isIpv4(ip) 
          && !Common.Util.IPUtil.isLoopback_v4(ip) 
          && !Common.Util.IPUtil.isBroadcast_v4(ip, mask) 
          && !Common.Util.IPUtil.isMulticast_v4(ip)
          && !Common.Util.IPUtil.isAny_v4(ip))// etc
     ||
(Common.Util.IPUtil.isIpv6(ip) 
          && !Common.Util.IPUtil.isLoopback_v6(ip) 
          && !Common.Util.IPUtil.isBroadcast_v6(ip) 
          && !Common.Util.IPUtil.isMulticast_v6(ip)
          && !Common.Util.IPUtil.isAny_v6(ip))// etc

是否有可能为潜在的开发人员设计更优雅的API?

Ext.define('Common.util.IPUtils', {
  singleton: true,
  
  SUPPORT_V6 : 1,
  MULTICASE : 0x0001,
  BROADCAST : 0x0002,
  LOOPBACK  : 0x0004,
  ANY       : 0x0008,//0.0.0.0
  
  checkIP : function(ip, mask, supportV6, excludeIPType){
    if(Common.util.IPUtils.isIPv4(ip)){
      if(excludeIPType&MULTICASE > 0 && Common.util.IPUtils.isMulticast_v4(ip)){
        return false;
      }
      else if(excludeIPType&BROADCAST > 0 && Common.util.IPUtils.isBroadcast_v4(ip, mask)){
        return false;
      }
      else if(excludeIPType&LOOPBACK > 0 && Common.util.IPUtils.isLoopback_v4(ip)){
        return false;
      }
      else if(excludeIPType&ANY > 0 && Common.util.IPUtils.isAny_v4(ip)){
        return false;
      }
    }
    if(supportV6 == Common.util.IPUtils.SUPPORT_V6 
		&& Common.util.IPUtils.isIPv6(ip)){
      if(excludeIPType&MULTICASE > 0 && Common.util.IPUtils.isMulticast_v6(ip)){
        return false;
      }
      else if(excludeIPType&BROADCAST > 0 && Common.util.IPUtils.isBroadcast_v6(ip)){
        return false;
      }
      else if(excludeIPType&LOOPBACK > 0 && Common.util.IPUtils.isLoopback_v6(ip)){
        return false;
      }
      else if(excludeIPType&ANY > 0 && Common.util.IPUtils.isAny_v6(ip)){
        return false;
      }
    }
	  return true;
  }
});

现在,开发人员会这样打电话:

Common.util.IPUtils.checkIP('10.180.0.1', 16, Common.util.IPUtils.SUPPORT_V6, Common.util.IPUtils.MULTICASE|Common.util.IPUtils.BROADCAST|Common.util.IPUtils.LOOPBACK|Common.util.IPUtils.ANY)

我使用Extjs来做这个,这是一个javascript框架。有什么更好的想法来改善它吗?

答案

没有人给我更多的建议。现在让我发表我的决议来回答我的问题。

我们可以在谷歌上搜索一些基本的IP验证器,但这不能满足我的需求。例如。 IP address utilities for node.js。因为我们是网络供应商,所以我们对IP检查有很多要求。

我用ExtJS实现了这个实用程序,但核心设计可以用许多其他语言实现。

以下是核心片段,它基于位图来组装许多原子验证函数。

Ext.define('Common.utils.IPDomainUtil', {
	alternateClassName : 'IPUtil',
	singleton : true,
	
	lang : 'en',

	IPv4 		: 0x0001,// If support IPv4
	IPv6 		: 0x0002,// If support IPv6
	
	//=============== Specific type of IP ==== begin ================
	// Types supported to exclude from IP pool
	EXCL_NONE	 			: 0x0000,// Exclude none, means any address is valid
	EXCL_MULTICAST 			: 0x0001,// Exclude multicast address ( Class D ) : 224.0.0.0-239.255.255.255
	EXCL_CLASSE 			: 0x0002,// Exclude address in class E ( Class E ) : 240.0.0.0-255.255.255.254
	EXCL_GLOBAL_BROADCAST 	: 0x0004,// Exclude global broadcast address : 255.255.255.255  
	EXCL_BROADCAST			: 0x0008,// Exclude broadcast address ( host-identification with all ones ): 10.180.255.255/16
	EXCL_LOOPBACK  			: 0x0010,// Exclude loopback address : 127.0.0.0/8
	EXCL_ANY       			: 0x0020,// Exclude any : 0.0.0.0
	EXCL_NETWORK  	 		: 0x0040,// Exclude network address ( host-identification with all zeros ) : 10.180.0.0/16
	EXCL_NETMASK   			: 0x0080,// Exclude network mask : 255.255.0.0
	EXCL_LINKLOCAL			: 0x0100,// Exclude IPv6 linklocal address
	EXCL_SITELOCAL 			: 0x0200,// Exclude IPv6 sitelocal address
	EXCL_GLOBAL				: 0x0400,// Exclude IPv6 global address
	
	// Types supported to include which maybe consider as invalid by default.
	ALLOW_INT				: 0x0001,// Include IP as int format.
	BEGIN_0 				: 0x0002,// Include IP begin with 0 : 0.0.0.1
	SUFFIX_MASK				: 0x0004,// Allow format : 10.180.0.1/16
	//=============== Specific type of IP ==== end ===================
	

	/**
	 * Check IP whether is valid base on params.
	 *  
	 * @param ipField		IP field which will be input IP to validate
	 * @param netmask	 	ItemId or Id of netmask component, or the instance of netmask, or left empty.
	 * @param ipVersion 	IPv4 or IPv6 or both.
	 * @param excludeBitmap	IP matched {excludeBitmap} conditions is invalid.
	 * @param includeBitmap	IP matched {includeBitmap} conditions is valid, no matter whether exist in excludeBitmap.
	 * 
	 * @example 1 : Set validator which get from IPUtil. Now you can set netmask itemId or ID.
	 * 	[{
	 * 		xtype : 'textfield',
	 * 		validator : IPUtil.getIPValidator("netmask", IPUtil.IPv4, IPUtil.EXCL_MULTI_E_BROAD_LOOP_ANY_MASK)
	 * 	}, {
	 * 		xtype : 'textfield',	
	 * 		itemid : 'netmask'
	 * 	}]
	 * 
	 * @example 2 : Set anonymous function as validator to call IPUtil.checkIp. Now you can set netmask instance.
	 * 	[{
	 * 		xtype : 'textfield',
	 * 		validator : function(){
	 * 			var netmask = me.down('mask');
	 * 			return IPUtil.checkIp(this, netmask, IPUtil.IPv4, IPUtil.EXCL_MULTI_E_BROAD_LOOP_ANY_MASK);
	 * 		}
	 * 	}]
	 * 
	 * @example 3 : If no netmask field, you can set IPUtil.NO_MASK or leave blank.
	 * 	IPUtil.getIPValidator(IPUtil.NO_MASK, IPUtil.IPv4, IPUtil.EXCL_MULTI_E_BROAD_LOOP_ANY_MASK)
	 * */
	checkIp : function(ipField, netmask, ipVersion, excludeBitmap, includeBitmap){
		excludeBitmap = excludeBitmap || 0;
		includeBitmap = includeBitmap || 0;
		if(!IPUtil.validateParameter(ipField, netmask, ipVersion, excludeBitmap, includeBitmap)) return true;
		
		var ip, maskIpString, canCheckMask = false;
		ip = Ext.isString(ipField) ? ipField : ipField.getValue();
		
		// Check whether accept integer IP
		if((includeBitmap & IPUtil.ALLOW_INT) != 0){
			if(IPUtil.is32BitsDecimalInt(ip)) return true;
		}
		
		// Check whether accept suffix mask
		if((includeBitmap & IPUtil.SUFFIX_MASK) != 0){
			if(!IPUtil.isIpWithSuffixMask(ip)) return getLangStr('iputil_checkip_suffixmask_invalid');
		}
		
		// Match IPv4
		if((ipVersion & IPUtil.IPv4) != 0 && IPUtil.isIpv4(ip)){
			// Check whether accept IP format begin with 0, eg : 0.0.1.1
			if(IPUtil.isBeginWithZero(ip) && !IPUtil.isAny_v4(ip)){
				if((includeBitmap & IPUtil.BEGIN_0) != 0){
					return true;
				}else{
					return getLangStr('iputil_checkip_begin0_invalid');
				}
			}
			
			ip = IPUtil.ipStringToInt(ip);
			if((excludeBitmap & IPUtil.EXCL_MULTICAST) != 0 && IPUtil.isMulticast_v4 (ip)){return getLangStr('iputil_checkip_v4_multicast_invalid');}
			if((excludeBitmap & IPUtil.EXCL_CLASSE) != 0 && IPUtil.isClassEAddress_v4(ip)) {return getLangStr('iputil_checkip_v4_classe_invalid');}
			if((excludeBitmap & IPUtil.EXCL_GLOBAL_BROADCAST) != 0 && IPUtil.isGlobalBroadcast_v4(ip)) {return getLangStr('iputil_checkip_v4_globalbroad_invalid');}
			if(canCheckMask){
				if((excludeBitmap & IPUtil.EXCL_BROADCAST) != 0 && IPUtil.isBroadcast_v4 (ip, maskIpString)){return getLangStr('iputil_checkip_v4_broadcast_invalid');}
			}
			if((excludeBitmap & IPUtil.EXCL_LOOPBACK) != 0 && IPUtil.isLoopback_v4(ip)) {return getLangStr('iputil_checkip_v4_loopback_invalid');}
			if((excludeBitmap & IPUtil.EXCL_ANY) != 0 && IPUtil.isAny_v4 (ip)){return getLangStr('iputil_checkip_v4_any_invalid');}
			if(canCheckMask){
				if((excludeBitmap & IPUtil.EXCL_NETWORK) != 0 && IPUtil.isNetwork_v4(ip, maskIpString)){return getLangStr('iputil_checkip_v4_network_invalid');}
			}
			if((excludeBitmap & IPUtil.EXCL_NETMASK) != 0 && IPUtil.isNetworkMask_v4(ip)){return getLangStr('iputil_checkip_v4_netmask_invalid');}
			return true;
		}
		
		// Match IPv6
		if((ipVersion & IPUtil.IPv6) != 0 && IPUtil.isIpv6(ip)){
			if((excludeBitmap & IPUtil.EXCL_MULTICAST) != 0 && IPUtil.isMulticast_v6(ip)) 	return getLangStr('iputil_checkip_v6_multicast_invalid');
			if((excludeBitmap & IPUtil.EXCL_LOOPBACK) != 0 	&& IPUtil.isLoopback_v6(ip)) 	return getLangStr('iputil_checkip_v6_loopback_invalid');
			if((excludeBitmap & IPUtil.EXCL_ANY) != 0 		&& IPUtil.isAny_v6 (ip)) 		return getLangStr('iputil_checkip_v6_any_invalid');
			if((excludeBitmap & IPUtil.EXCL_LINKLOCAL) != 0 && IPUtil.isLinklocal_v6(ip)) 	return getLangStr('iputil_checkip_v6_linklocal_invalid');
			if((excludeBitmap & IPUtil.EXCL_SITELOCAL) != 0 && IPUtil.isSitelocal_v6 (ip)) 	return getLangStr('iputil_checkip_v6_sitelocal_invalid');
			if((excludeBitmap & IPUtil.EXCL_GLOBAL) != 0 	&& IPUtil.isGlobal_v6 (ip)) 	return getLangStr('iputil_checkip_v6_global_invalid');
			return true;
		}
		
		return getLangStr('iputil_checkip_invalid');
	}
});

以上是关于如何设计优雅的IP检查API以支持IPv4和IPv6的主要内容,如果未能解决你的问题,请参考以下文章

如何利用 IP 归属地查询 API 精准锁定用户位置

ipv4与ipv6如何转换

如何判断自己的网络环境是不是支持ipv6

将公共静态 ipv4 地址添加到 AWS 负载均衡器

window10自动配置ipv4 169.254.130.49首选

ipv6和ipv4的区别