From e328d015f12c03a1f8391360d5dafc09596d0b72 Mon Sep 17 00:00:00 2001 From: Matthias Einwag Date: Tue, 4 Mar 2014 00:49:08 +0100 Subject: [PATCH 1/5] Added a IpAddress structure to the net module --- lib/pure/net.nim | 232 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 231 insertions(+), 1 deletion(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 0ec007009c..cf5b4634ed 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -9,7 +9,237 @@ ## This module implements a high-level cross-platform sockets interface. -import sockets2, os +import sockets2, os, strutils, unsigned + +type + IpAddressFamily* {.pure.} = enum ## Describes the type of an IP address + IPv6, ## IPv6 address + IPv4 ## IPv4 address + + TIpAddress* = object ## stores an arbitrary IP address + case family*: IpAddressFamily ## the type of the IP address (IPv4 or IPv6) + of IpAddressFamily.IPv6: + address_v6*: array[0..15, uint8] ## Contains the IP address in bytes in case of IPv6 + of IpAddressFamily.IPv4: + address_v4*: array[0..3, uint8] ## Contains the IP address in bytes in case of IPv4 + +proc IPv4_any*(): TIpAddress = + ## Returns the IPv4 any address, which can be used to listen on all available + ## network adapters + result = TIpAddress( + family: IpAddressFamily.IPv4, + address_v4: [0'u8, 0'u8, 0'u8, 0'u8]) + +proc IPv4_loopback*(): TIpAddress = + ## Returns the IPv4 loopback address (127.0.0.1) + result = TIpAddress( + family: IpAddressFamily.IPv4, + address_v4: [127'u8, 0'u8, 0'u8, 1'u8]) + +proc IPv4_broadcast*(): TIpAddress = + ## Returns the IPv4 broadcast address (255.255.255.255) + result = TIpAddress( + family: IpAddressFamily.IPv4, + address_v4: [255'u8, 255'u8, 255'u8, 255'u8]) + +proc IPv6_any*(): TIpAddress = + ## Returns the IPv6 any address (::0), which can be used + ## to listen on all available network adapters + result = TIpAddress( + family: IpAddressFamily.IPv6, + address_v6: [0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8]) + +proc IPv6_loopback*(): TIpAddress = + ## Returns the IPv6 loopback address (::1) + result = TIpAddress( + family: IpAddressFamily.IPv6, + address_v6: [0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,1'u8]) + +proc `$`*(address: TIpAddress): string = + ## Converts an TIpAddress into the textual representation + result = "" + case address.family + of IpAddressFamily.IPv4: + for i in 0 .. 3: + if i != 0: + result.add('.') + result.add($address.address_v4[i]) + of IpAddressFamily.IPv6: + var + currentZeroStart = -1 + currentZeroCount = 0 + biggestZeroStart = -1 + biggestZeroCount = 0 + # Look for the largest block of zeros + for i in 0..7: + var isZero = address.address_v6[i*2] == 0 and address.address_v6[i*2+1] == 0 + if isZero: + if currentZeroStart == -1: + currentZeroStart = i + currentZeroCount = 1 + else: + currentZeroCount.inc() + if currentZeroCount > biggestZeroCount: + biggestZeroCount = currentZeroCount + biggestZeroStart = currentZeroStart + else: + currentZeroStart = -1 + + if biggestZeroCount == 8: # Special case ::0 + result.add("::") + else: # Print address + var printedLastGroup = false + for i in 0..7: + var word:uint16 = (cast[uint16](address.address_v6[i*2])) shl 8 + word = word or cast[uint16](address.address_v6[i*2+1]) + + if biggestZeroCount != 0 and # Check if in skip group + (i >= biggestZeroStart and i < (biggestZeroStart + biggestZeroCount)): + if i == biggestZeroStart: # skip start + result.add("::") + printedLastGroup = false + else: + if printedLastGroup: + result.add(':') + result.add(toHex(BiggestInt(word),4)) # this has too many digits + printedLastGroup = true + +proc parseIPv4Address(address_str: string): TIpAddress = + ## Parses IPv4 adresses + ## Raises EInvalidValue on errors + var + byteCount = 0 + currentByte:uint16 = 0 + seperatorValid = false + + result.family = IpAddressFamily.IPv4 + + for i in 0 .. high(address_str): + if address_str[i] in {'0'..'9'}: # Character is a number + currentByte = currentByte * 10 + cast[uint16](ord(address_str[i]) - ord('0')) + if currentByte > 255'u16: raise new EInvalidValue + seperatorValid = true + elif address_str[i] == '.': # IPv4 address separator + if not seperatorValid or byteCount >= 3: + raise new EInvalidValue + result.address_v4[byteCount] = cast[uint8](currentByte) + currentByte = 0 + byteCount.inc + seperatorValid = false + else: + raise new EInvalidValue # Invalid character + + if byteCount != 3 or not seperatorValid: + raise new EInvalidValue + result.address_v4[byteCount] = cast[uint8](currentByte) + +proc parseIPv6Address(address_str: string): TIpAddress = + ## Parses IPv6 adresses + ## Raises EInvalidValue on errors + result.family = IpAddressFamily.IPv6 + if address_str.len < 2: raise new EInvalidValue + + var + groupCount = 0 + currentGroupStart = 0 + currentShort:uint32 = 0 + seperatorValid = true + dualColonGroup = -1 + lastWasColon = false + v4StartPos = -1 + byteCount = 0 + + for i,c in address_str: + if c == ':': + if not seperatorValid: raise new EInvalidValue + if lastWasColon: + if dualColonGroup != -1: raise new EInvalidValue + dualColonGroup = groupCount + seperatorValid = false + elif i != 0 and i != high(address_str): + if groupCount >= 8: raise new EInvalidValue + result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8) + result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF) + currentShort = 0 + groupCount.inc() + if dualColonGroup != -1: seperatorValid = false + elif i == 0: # only valid if address starts with :: + if address_str[1] != ':': raise new EInvalidValue + else: # i == high(address_str) - only valid if address ends with :: + if address_str[high(address_str)-1] != ':': raise new EInvalidValue + lastWasColon = true + currentGroupStart = i + 1 + elif c == '.': # Switch to parse IPv4 mode + if i < 3 or not seperatorValid or groupCount >= 7: raise new EInvalidValue + v4StartPos = currentGroupStart + currentShort = 0 + seperatorValid = false + break + elif c in {'0'..'9','a'..'f','A'..'F'}: + if c in {'0'..'9'}: # Normal digit + currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('0')) + elif c >= 'a' and c <= 'f': # Lower case hex + currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('a')) + 10 + else: # Upper case hex + currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('A')) + 10 + if currentShort > 65535'u32: raise new EInvalidValue + lastWasColon = false + seperatorValid = true + else: + raise new EInvalidValue + + + if v4StartPos == -1: # Don't parse v4. Copy the remaining v6 stuff + if seperatorValid: # Copy remaining data + if groupCount >= 8: raise new EInvalidValue + result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8) + result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF) + groupCount.inc() + else: # Must parse IPv4 address + for i,c in address_str[v4StartPos..high(address_str)]: + if c in {'0'..'9'}: # Character is a number + currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0')) + if currentShort > 255'u32: raise new EInvalidValue + seperatorValid = true + elif c == '.': # IPv4 address separator + if not seperatorValid or byteCount >= 3: + raise new EInvalidValue + result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort) + currentShort = 0 + byteCount.inc() + seperatorValid = false + else: # Invalid character + raise new EInvalidValue + + if byteCount != 3 or not seperatorValid: + raise new EInvalidValue + result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort) + groupCount += 2 + + # Shift and fill zeros in case of :: + if groupCount > 8: + raise new EInvalidValue + elif groupCount < 8: # must fill + if dualColonGroup == -1: raise new EInvalidValue + var toFill = 8 - groupCount # The number of groups to fill + var toShift = groupCount - dualColonGroup # Nr of known groups after :: + for i in 0..2*toShift-1: # shift + result.address_v6[15-i] = result.address_v6[groupCount*2-i-1] + for i in 0..2*toFill-1: # fill with 0s + result.address_v6[dualColonGroup*2+i] = 0 + elif dualColonGroup != -1: raise new EInvalidValue + + +proc parseIpAddress*(address_str: string): TIpAddress = + ## Parses an IP address + ## Throws EInvalidValue on error + if address_str == nil: + raise new EInvalidValue + if address_str.contains(':'): + return parseIPv6Address(address_str) + else: + return parseIPv4Address(address_str) + type TSocket* = TSocketHandle From 0f88ee7d0c17852cbd622b65f1b02422b006a81b Mon Sep 17 00:00:00 2001 From: Matthias Einwag Date: Tue, 4 Mar 2014 22:41:53 +0100 Subject: [PATCH 2/5] Use character ranges from strutils. --- lib/pure/net.nim | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index cf5b4634ed..90e21c6447 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -55,6 +55,17 @@ proc IPv6_loopback*(): TIpAddress = family: IpAddressFamily.IPv6, address_v6: [0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,1'u8]) +proc `==`*(lhs, rhs: TIpAddress): bool = + ## Compares two IpAddresses for Equality. Returns two if the addresses are equal + if lhs.family != rhs.family: return false + if lhs.family == IpAddressFamily.IPv4: + for i in low(lhs.address_v4) .. high(lhs.address_v4): + if lhs.address_v4[i] != rhs.address_v4[i]: return false + else: # IPv6 + for i in low(lhs.address_v6) .. high(lhs.address_v6): + if lhs.address_v6[i] != rhs.address_v6[i]: return false + return true + proc `$`*(address: TIpAddress): string = ## Converts an TIpAddress into the textual representation result = "" @@ -115,7 +126,7 @@ proc parseIPv4Address(address_str: string): TIpAddress = result.family = IpAddressFamily.IPv4 for i in 0 .. high(address_str): - if address_str[i] in {'0'..'9'}: # Character is a number + if address_str[i] in strutils.Digits: # Character is a number currentByte = currentByte * 10 + cast[uint16](ord(address_str[i]) - ord('0')) if currentByte > 255'u16: raise new EInvalidValue seperatorValid = true @@ -175,8 +186,8 @@ proc parseIPv6Address(address_str: string): TIpAddress = currentShort = 0 seperatorValid = false break - elif c in {'0'..'9','a'..'f','A'..'F'}: - if c in {'0'..'9'}: # Normal digit + elif c in strutils.HexDigits: + if c in strutils.Digits: # Normal digit currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('0')) elif c >= 'a' and c <= 'f': # Lower case hex currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('a')) + 10 @@ -197,7 +208,7 @@ proc parseIPv6Address(address_str: string): TIpAddress = groupCount.inc() else: # Must parse IPv4 address for i,c in address_str[v4StartPos..high(address_str)]: - if c in {'0'..'9'}: # Character is a number + if c in strutils.Digits: # Character is a number currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0')) if currentShort > 255'u32: raise new EInvalidValue seperatorValid = true @@ -232,7 +243,7 @@ proc parseIPv6Address(address_str: string): TIpAddress = proc parseIpAddress*(address_str: string): TIpAddress = ## Parses an IP address - ## Throws EInvalidValue on error + ## Raises EInvalidValue on error if address_str == nil: raise new EInvalidValue if address_str.contains(':'): @@ -282,4 +293,4 @@ proc setBlocking*(s: TSocket, blocking: bool) {.tags: [].} = else: var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK if fcntl(s, F_SETFL, mode) == -1: - osError(osLastError()) \ No newline at end of file + osError(osLastError()) From 74d51a77000144f6ddf53e366e4a7e58393201ab Mon Sep 17 00:00:00 2001 From: Matthias Einwag Date: Tue, 4 Mar 2014 23:02:23 +0100 Subject: [PATCH 3/5] $ for TIpAddress now prints in the recommended format --- lib/pure/net.nim | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 90e21c6447..ab9c3ac23f 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -104,7 +104,7 @@ proc `$`*(address: TIpAddress): string = var word:uint16 = (cast[uint16](address.address_v6[i*2])) shl 8 word = word or cast[uint16](address.address_v6[i*2+1]) - if biggestZeroCount != 0 and # Check if in skip group + if biggestZeroCount != 0 and # Check if group is in skip group (i >= biggestZeroStart and i < (biggestZeroStart + biggestZeroCount)): if i == biggestZeroStart: # skip start result.add("::") @@ -112,7 +112,18 @@ proc `$`*(address: TIpAddress): string = else: if printedLastGroup: result.add(':') - result.add(toHex(BiggestInt(word),4)) # this has too many digits + var + afterLeadingZeros = false + mask = 0xF000'u16 + for j in 0'u16..3'u16: + var val = (mask and word) shr (4'u16*(3'u16-j)) + if val != 0 or afterLeadingZeros: + if val < 0xA: + result.add(chr(uint16(ord('0'))+val)) + else: # val >= 0xA + result.add(chr(uint16(ord('a'))+val-0xA)) + afterLeadingZeros = true + mask = mask shr 4 printedLastGroup = true proc parseIPv4Address(address_str: string): TIpAddress = From 7b055388b7acfc7b7db11dd8102a0de23e3547ae Mon Sep 17 00:00:00 2001 From: Matthias Einwag Date: Sun, 9 Mar 2014 11:47:22 +0100 Subject: [PATCH 4/5] raise exceptions through newException --- lib/pure/net.nim | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index ab9c3ac23f..82b24585f5 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -139,7 +139,7 @@ proc parseIPv4Address(address_str: string): TIpAddress = for i in 0 .. high(address_str): if address_str[i] in strutils.Digits: # Character is a number currentByte = currentByte * 10 + cast[uint16](ord(address_str[i]) - ord('0')) - if currentByte > 255'u16: raise new EInvalidValue + if currentByte > 255'u16: raise newException(EInvalidValue, "Invalid IP Address") seperatorValid = true elif address_str[i] == '.': # IPv4 address separator if not seperatorValid or byteCount >= 3: @@ -149,7 +149,7 @@ proc parseIPv4Address(address_str: string): TIpAddress = byteCount.inc seperatorValid = false else: - raise new EInvalidValue # Invalid character + raise newException(EInvalidValue, "Invalid IP Address") # Invalid character if byteCount != 3 or not seperatorValid: raise new EInvalidValue @@ -159,7 +159,7 @@ proc parseIPv6Address(address_str: string): TIpAddress = ## Parses IPv6 adresses ## Raises EInvalidValue on errors result.family = IpAddressFamily.IPv6 - if address_str.len < 2: raise new EInvalidValue + if address_str.len < 2: raise newException(EInvalidValue, "Invalid IP Address") var groupCount = 0 @@ -173,26 +173,26 @@ proc parseIPv6Address(address_str: string): TIpAddress = for i,c in address_str: if c == ':': - if not seperatorValid: raise new EInvalidValue + if not seperatorValid: raise newException(EInvalidValue, "Invalid IP Address") if lastWasColon: - if dualColonGroup != -1: raise new EInvalidValue + if dualColonGroup != -1: raise newException(EInvalidValue, "Invalid IP Address") dualColonGroup = groupCount seperatorValid = false elif i != 0 and i != high(address_str): - if groupCount >= 8: raise new EInvalidValue + if groupCount >= 8: raise newException(EInvalidValue, "Invalid IP Address") result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8) result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF) currentShort = 0 groupCount.inc() if dualColonGroup != -1: seperatorValid = false elif i == 0: # only valid if address starts with :: - if address_str[1] != ':': raise new EInvalidValue + if address_str[1] != ':': raise newException(EInvalidValue, "Invalid IP Address") else: # i == high(address_str) - only valid if address ends with :: - if address_str[high(address_str)-1] != ':': raise new EInvalidValue + if address_str[high(address_str)-1] != ':': raise newException(EInvalidValue, "Invalid IP Address") lastWasColon = true currentGroupStart = i + 1 elif c == '.': # Switch to parse IPv4 mode - if i < 3 or not seperatorValid or groupCount >= 7: raise new EInvalidValue + if i < 3 or not seperatorValid or groupCount >= 7: raise newException(EInvalidValue, "Invalid IP Address") v4StartPos = currentGroupStart currentShort = 0 seperatorValid = false @@ -204,16 +204,16 @@ proc parseIPv6Address(address_str: string): TIpAddress = currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('a')) + 10 else: # Upper case hex currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('A')) + 10 - if currentShort > 65535'u32: raise new EInvalidValue + if currentShort > 65535'u32: raise newException(EInvalidValue, "Invalid IP Address") lastWasColon = false seperatorValid = true else: - raise new EInvalidValue + raise newException(EInvalidValue, "Invalid IP Address") if v4StartPos == -1: # Don't parse v4. Copy the remaining v6 stuff if seperatorValid: # Copy remaining data - if groupCount >= 8: raise new EInvalidValue + if groupCount >= 8: raise newException(EInvalidValue, "Invalid IP Address") result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8) result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF) groupCount.inc() @@ -221,7 +221,7 @@ proc parseIPv6Address(address_str: string): TIpAddress = for i,c in address_str[v4StartPos..high(address_str)]: if c in strutils.Digits: # Character is a number currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0')) - if currentShort > 255'u32: raise new EInvalidValue + if currentShort > 255'u32: raise newException(EInvalidValue, "Invalid IP Address") seperatorValid = true elif c == '.': # IPv4 address separator if not seperatorValid or byteCount >= 3: @@ -231,7 +231,7 @@ proc parseIPv6Address(address_str: string): TIpAddress = byteCount.inc() seperatorValid = false else: # Invalid character - raise new EInvalidValue + raise newException(EInvalidValue, "Invalid IP Address") if byteCount != 3 or not seperatorValid: raise new EInvalidValue @@ -240,23 +240,23 @@ proc parseIPv6Address(address_str: string): TIpAddress = # Shift and fill zeros in case of :: if groupCount > 8: - raise new EInvalidValue + raise newException(EInvalidValue, "Invalid IP Address") elif groupCount < 8: # must fill - if dualColonGroup == -1: raise new EInvalidValue + if dualColonGroup == -1: raise newException(EInvalidValue, "Invalid IP Address") var toFill = 8 - groupCount # The number of groups to fill var toShift = groupCount - dualColonGroup # Nr of known groups after :: for i in 0..2*toShift-1: # shift result.address_v6[15-i] = result.address_v6[groupCount*2-i-1] for i in 0..2*toFill-1: # fill with 0s result.address_v6[dualColonGroup*2+i] = 0 - elif dualColonGroup != -1: raise new EInvalidValue + elif dualColonGroup != -1: raise newException(EInvalidValue, "Invalid IP Address") proc parseIpAddress*(address_str: string): TIpAddress = ## Parses an IP address ## Raises EInvalidValue on error if address_str == nil: - raise new EInvalidValue + raise newException(EInvalidValue, "IP Address string is nil") if address_str.contains(':'): return parseIPv6Address(address_str) else: From 95e4ab99e7830877fd67844b69640e1e31b705e4 Mon Sep 17 00:00:00 2001 From: Matthias Einwag Date: Fri, 14 Mar 2014 22:17:44 +0100 Subject: [PATCH 5/5] Make the IP address exceptions more detailed --- lib/pure/net.nim | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 82b24585f5..9ee98cbe69 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -139,20 +139,21 @@ proc parseIPv4Address(address_str: string): TIpAddress = for i in 0 .. high(address_str): if address_str[i] in strutils.Digits: # Character is a number currentByte = currentByte * 10 + cast[uint16](ord(address_str[i]) - ord('0')) - if currentByte > 255'u16: raise newException(EInvalidValue, "Invalid IP Address") + if currentByte > 255'u16: + raise newException(EInvalidValue, "Invalid IP Address. Value is out of range") seperatorValid = true elif address_str[i] == '.': # IPv4 address separator if not seperatorValid or byteCount >= 3: - raise new EInvalidValue + raise newException(EInvalidValue, "Invalid IP Address. The address consists of too many groups") result.address_v4[byteCount] = cast[uint8](currentByte) currentByte = 0 byteCount.inc seperatorValid = false else: - raise newException(EInvalidValue, "Invalid IP Address") # Invalid character + raise newException(EInvalidValue, "Invalid IP Address. Address contains an invalid character") if byteCount != 3 or not seperatorValid: - raise new EInvalidValue + raise newException(EInvalidValue, "Invalid IP Address") result.address_v4[byteCount] = cast[uint8](currentByte) proc parseIPv6Address(address_str: string): TIpAddress = @@ -173,22 +174,24 @@ proc parseIPv6Address(address_str: string): TIpAddress = for i,c in address_str: if c == ':': - if not seperatorValid: raise newException(EInvalidValue, "Invalid IP Address") + if not seperatorValid: raise newException(EInvalidValue, "Invalid IP Address. Address contains an invalid seperator") if lastWasColon: - if dualColonGroup != -1: raise newException(EInvalidValue, "Invalid IP Address") + if dualColonGroup != -1: raise newException(EInvalidValue, "Invalid IP Address. Address contains more than one \"::\" seperator") dualColonGroup = groupCount seperatorValid = false elif i != 0 and i != high(address_str): - if groupCount >= 8: raise newException(EInvalidValue, "Invalid IP Address") + if groupCount >= 8: raise newException(EInvalidValue, "Invalid IP Address. The address consists of too many groups") result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8) result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF) currentShort = 0 groupCount.inc() if dualColonGroup != -1: seperatorValid = false elif i == 0: # only valid if address starts with :: - if address_str[1] != ':': raise newException(EInvalidValue, "Invalid IP Address") + if address_str[1] != ':': + raise newException(EInvalidValue, "Invalid IP Address. Address may not start with \":\"") else: # i == high(address_str) - only valid if address ends with :: - if address_str[high(address_str)-1] != ':': raise newException(EInvalidValue, "Invalid IP Address") + if address_str[high(address_str)-1] != ':': + raise newException(EInvalidValue, "Invalid IP Address. Address may not end with \":\"") lastWasColon = true currentGroupStart = i + 1 elif c == '.': # Switch to parse IPv4 mode @@ -204,16 +207,17 @@ proc parseIPv6Address(address_str: string): TIpAddress = currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('a')) + 10 else: # Upper case hex currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('A')) + 10 - if currentShort > 65535'u32: raise newException(EInvalidValue, "Invalid IP Address") + if currentShort > 65535'u32: + raise newException(EInvalidValue, "Invalid IP Address. Value is out of range") lastWasColon = false seperatorValid = true else: - raise newException(EInvalidValue, "Invalid IP Address") + raise newException(EInvalidValue, "Invalid IP Address. Address contains an invalid character") if v4StartPos == -1: # Don't parse v4. Copy the remaining v6 stuff if seperatorValid: # Copy remaining data - if groupCount >= 8: raise newException(EInvalidValue, "Invalid IP Address") + if groupCount >= 8: raise newException(EInvalidValue, "Invalid IP Address. The address consists of too many groups") result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8) result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF) groupCount.inc() @@ -221,35 +225,36 @@ proc parseIPv6Address(address_str: string): TIpAddress = for i,c in address_str[v4StartPos..high(address_str)]: if c in strutils.Digits: # Character is a number currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0')) - if currentShort > 255'u32: raise newException(EInvalidValue, "Invalid IP Address") + if currentShort > 255'u32: + raise newException(EInvalidValue, "Invalid IP Address. Value is out of range") seperatorValid = true elif c == '.': # IPv4 address separator if not seperatorValid or byteCount >= 3: - raise new EInvalidValue + raise newException(EInvalidValue, "Invalid IP Address") result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort) currentShort = 0 byteCount.inc() seperatorValid = false else: # Invalid character - raise newException(EInvalidValue, "Invalid IP Address") + raise newException(EInvalidValue, "Invalid IP Address. Address contains an invalid character") if byteCount != 3 or not seperatorValid: - raise new EInvalidValue + raise newException(EInvalidValue, "Invalid IP Address") result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort) groupCount += 2 # Shift and fill zeros in case of :: if groupCount > 8: - raise newException(EInvalidValue, "Invalid IP Address") + raise newException(EInvalidValue, "Invalid IP Address. The address consists of too many groups") elif groupCount < 8: # must fill - if dualColonGroup == -1: raise newException(EInvalidValue, "Invalid IP Address") + if dualColonGroup == -1: raise newException(EInvalidValue, "Invalid IP Address. The address consists of too few groups") var toFill = 8 - groupCount # The number of groups to fill var toShift = groupCount - dualColonGroup # Nr of known groups after :: for i in 0..2*toShift-1: # shift result.address_v6[15-i] = result.address_v6[groupCount*2-i-1] for i in 0..2*toFill-1: # fill with 0s result.address_v6[dualColonGroup*2+i] = 0 - elif dualColonGroup != -1: raise newException(EInvalidValue, "Invalid IP Address") + elif dualColonGroup != -1: raise newException(EInvalidValue, "Invalid IP Address. The address consists of too many groups") proc parseIpAddress*(address_str: string): TIpAddress =