diff options
author | Matthias Einwag <matthias.einwag@live.com> | 2014-03-04 00:49:08 +0100 |
---|---|---|
committer | Matthias Einwag <matthias.einwag@live.com> | 2014-03-04 00:49:08 +0100 |
commit | e328d015f12c03a1f8391360d5dafc09596d0b72 (patch) | |
tree | 43f342218a39587fcc4a54d9a1b34a3d329bf3a2 /lib | |
parent | 5009ea56c0f54653f6817874262d5d076fb76cf6 (diff) | |
download | Nim-e328d015f12c03a1f8391360d5dafc09596d0b72.tar.gz |
Added a IpAddress structure to the net module
Diffstat (limited to 'lib')
-rw-r--r-- | lib/pure/net.nim | 232 |
1 files changed, 231 insertions, 1 deletions
diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 0ec007009..cf5b4634e 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 |