
/*!
	@brief  IPアドレスを表すクラス
	@author OverTaker
	@date   2008/10/18
*/


Namespace System
Namespace Net

Class IPAddress
	item As *Byte
	size As Long
	family As DWord
Public

	/*!
	@brief  アドレスをバイト配列で取得する
	@return アドレスを表したバイト列
	@note   このメソッドはライブラリ内のみで使用する目的で作られています。
	*/
	Function Address() As *Byte
		Return item As *Byte
	End Function

	/*!
	@brief コンストラクタ
	*/
	Sub IPAddress()
	End Sub

	/*!
	@brief コンストラクタ
	@param IPアドレスを表した32bit値（ネットワークバイトオーダー）
	*/
	Sub IPAddress(ip As DWord)
		family = AF_INET
		size = SizeOf(in_addr)
		item = malloc(size)
		memcpy(item, VarPtr(ip), size)
	End Sub

	/*!
	@brief コンストラクタ
	@param SocketAddressクラスから作成
	@note  このコンストラクタはライブラリ内のみで使用する目的で作られています。
	*/
	Sub IPAddress(addr As *sockaddr)
		family = addr.sa_family
		Select Case family
			Case AF_INET
				Dim addr4 = addr As *sockaddr_in
				size = SizeOf(in_addr)
				item = malloc(size)
				memcpy(item, VarPtr(addr4->sin_addr.s_addr), size)
			Case AF_INET6
				Dim addr6 = addr As *sockaddr_in6
				size = SizeOf(in6_addr)
				item = malloc(size)
				memcpy(item, VarPtr(addr6->sin6_addr.s6_addr), size)
			Case Else
				Throw New NotSupportedException("IPAddress: Not Supported AddressFamily.")
		End Select
	End Sub

	/*!
	@brief コンストラクタ
	@param IPアドレスを表したバイト列
	@param IPアドレスの長さ
	@note  このコンストラクタはライブラリ内のみで使用する目的で作られています。
	*/
	Sub IPAddress(af As DWord, address As *Byte, len As Long)
		family = af
		size = len
		item = malloc(size)
		memcpy(item, address, size)
	End Sub

	/*!
	@brief デストラクタ
	*/
	Sub ~IPAddress()
		If item <> NULL Then free(item)
	End Sub

	Function Operator== (address As IPAddress) As Boolean
		Return Equals(address)
	End Function

	/*!
	@brief アドレスファミリを取得する。
	@return アドレスファミリ 
	*/
	Function AddressFamily() As Sockets.AddressFamily
		Return New System.Net.Sockets.AddressFamily(family, "AddressFamily")
	End Function

	/*!
	@brief スコープを取得する。
	@return スコープ 
	*/
/*	Function ScopeId() As DWord
	End Function
*/
	/*!
	@brief リンクローカルアドレスかどうかを取得する。
	@retval True  リンクローカルアドレス
	@retval Flase リンクローカルアドレスではない
	*/
	Function IsIPv6LinkLocal() As Boolean
		If family = AF_INET6 Then
			Return (item[0] = &HFF) And (item[1] = &H00)
		Else
			Throw New System.Net.Sockets.SocketException("IPAddress.IsIPv6LinkLocal: AddressFamily is not IPv6.")
		End If
	End Function

	/*!
	@brief IPv6のマルチキャストアドレスかどうかを取得する。
	@retval True  IPv6のマルチキャストアドレス
	@retval Flase IPv6のマルチキャストアドレス
	*/
	Function IsIPv6Multicast() As Boolean
		If family = AF_INET6 Then
			Return (item[0] = &HFE) And (item[1] = &H80)
		Else
			Throw New System.Net.Sockets.SocketException("IPAddress.IsIPv6Multicast: AddressFamily is not IPv6.")
		End If
	End Function

	/*!
	@brief IPアドレスが等しいかどうかを取得する
	@retval True  等しい
	@retval Flase 等しくない
	*/
	Function Equals(ip As IPAddress) As Boolean
		If family = ip.family Then
			Dim i As Long
			For i = 0 To ELM(size)
				If item[i] <> ip.item[i] Then Return False
			Next
			Return True
		Else
			Return False
		End If
	End Function

	/*!
	@brief IPアドレスを文字列で取得する
	@return 文字列
	*/
	Override Function ToString() As String
		Dim temp[ELM(64)] As TCHAR
		Dim ret As Long
		Select Case family
			Case AF_INET
				Dim addr4 As sockaddr_in
				addr4.sin_family = family As Integer
				addr4.sin_addr.s_addr = GetDWord(item)
				ret = WSAAddressToString(addr4 As sockaddr, SizeOf(sockaddr_in), ByVal 0, temp, Len(temp))
			Case AF_INET6
				Dim addr6 As sockaddr_in6
				addr6.sin6_family = family As Word
				memcpy(addr6.sin6_addr.s6_addr, item, size)
				ret = WSAAddressToString(addr6 As sockaddr, SizeOf(sockaddr_in6), ByVal 0, temp, Len(temp))
			Case Else
				Throw New NotSupportedException("IPAddress.ToString: Unknown Protocol.")
		End Select

		If ret Then
			Throw New System.Net.Sockets.SocketException(WSAGetLastError(), "IPAddress.ToString: Failed to WSAAddressToString.")
		Else
			Return New String(temp)
		End If
	End Function


	/*!
	@brief 文字列からIPAddressクラスを作成する
	@param 文字列で表したIPアドレス
	@return IPAddressクラス
	@exception 作成に失敗した場合
	*/
	Static Function Parse(ip As String) As IPAddress
		Dim ipAddress = New IPAddress
		If TryParse(ip, ipAddress) Then
			Return ipAddress
		Else
			Throw New System.Net.Sockets.SocketException(WSAGetLastError(), "IPAddress.Parse: Failed to Parse.")
		End If
	End Function

	/*!
	@brief 文字列からIPAddressクラスを作成する
	@param[in]  文字列で表したIPアドレス
	@param[out] 作成したIPアドレスを格納するIPAddressクラス
	@retval True  成功
	@retval False 失敗
	*/
	Static Function TryParse(ip As String, ByRef address As IPAddress) As Boolean
		address = New IPAddress
		If ip.Contains(".") Then
			address.family = AF_INET
			address.item = Detail.CreateAddressFromIPv4String(ip)
			address.size = SizeOf(in_addr)
		ElseIf ip.Contains(":") Then
			address.family = AF_INET6
			address.item = Detail.CreateAddressFromIPv6String(ip)
			address.size = SizeOf(in6_addr)
		Else
			Return False
		End If

		If address.item <> NULL Then
			Return True
		Else
			address = Nothing
			Return False
		End If
	End Function

	/*!
	@brief Anyアドレスを作成
	@return IPAddressクラス
	*/
	Static Function Any() As IPAddress
		Return New IPAddress(0 As DWord)
	End Function

	/*!
	@brief ブロードキャストアドレスを作成
	@return IPAddressクラス
	*/
	Static Function Broadcast() As IPAddress
		Return New IPAddress(&HFFFFFFFF)
	End Function

	/*!
	@brief ループバックアドレスを作成
	@return IPAddressクラス
	*/
	Static Function Loopback() As IPAddress
		Return New IPAddress( HostToNetworkOrder(&H7F000001) )
	End Function

	/*!
	@brief Noneアドレスを作成
	@return IPAddressクラス
	*/
	Static Function None() As IPAddress
		Return New IPAddress(&HFFFFFFFF)
	End Function

	/*!
	@brief IPv6 Anyアドレスを作成
	@return IPAddressクラス
	*/
	Static Function IPv6Any() As IPAddress
		Return IPAddress.Parse("::")
	End Function

	/*!
	@brief IPv6 ループバックアドレスを作成
	@return IPAddressクラス
	*/
	Static Function IPv6Loopback() As IPAddress
		Return IPAddress.Parse("::1")
	End Function

	/*!
	@brief IPv6 Noneアドレスを作成
	@return IPAddressクラス
	*/
	Static Function IPv6None() As IPAddress
		Return IPAddress.Parse("::")
	End Function



	/*!
	@brief 16bitのホストバイトオーダーをネットワークバイトオーダーに変換する
	@param ホストバイトオーダー
	@return ネットワークバイトオーダー
	*/
	Static Function HostToNetworkOrder(host As Word) As Word
		Return htons(host)
	End Function

	/*!
	@brief 32bitのホストバイトオーダーをネットワークバイトオーダーに変換する
	@param ホストバイトオーダー
	@return ネットワークバイトオーダー
	*/
	Static Function HostToNetworkOrder(host As DWord) As DWord
		Return htonl(host)
	End Function

	/*!
	@brief 16bitのネットワークバイトオーダーをホストバイトオーダーに変換する
	@param ネットワークバイトオーダー
	@return ホストバイトオーダー
	*/
	Static Function NetworkToHostOrder(network As Word) As Word
		Return ntohs(network)
	End Function

	/*!
	@brief 32bitのネットワークバイトオーダーをホストバイトオーダーに変換する
	@param ネットワークバイトオーダー
	@return ホストバイトオーダー
	*/
	Static Function NetworkToHostOrder(network As DWord) As DWord
		Return ntohl(network)
	End Function
End Class

Namespace Detail

	/*!
	@brief IPv4アドレスを表した文字列からアドレスを作成する
	@param IPv4アドレスを表したバイト列
	@return バイト列
	*/
	Function CreateAddressFromIPv4String(ip As String) As *Byte
		Dim addr4 As sockaddr_in
		If WSAStringToAddress(ToTCStr(ip), AF_INET, ByVal 0, addr4 As sockaddr, SizeOf(sockaddr_in)) Then
			Return NULL
		Else
			Dim item = malloc(SizeOf(in_addr)) As *Byte
			memcpy(item, VarPtr(addr4.sin_addr.s_addr), SizeOf(in_addr))
			Return item
		End If
	End Function

	/*!
	@brief IPv6アドレスを表した文字列からアドレスを作成する
	@param IPv6アドレスを表したバイト列
	@return バイト列
	*/
	Function CreateAddressFromIPv6String(ip As String) As *Byte
		Dim addr6 As sockaddr_in6
		If WSAStringToAddress(ToTCStr(ip), AF_INET6, ByVal 0, addr6 As sockaddr, SizeOf(sockaddr_in6)) Then
			Return NULL
		Else
			Dim item = malloc(SizeOf(in6_addr)) As *Byte
			memcpy(item, addr6.sin6_addr.s6_addr, SizeOf(in6_addr))
			Return item
		End If
	End Function

End Namespace

End Namespace
End Namespace