summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--lib/windows/winlean.nim89
-rw-r--r--tests/stdlib/tnet_ll.nim39
2 files changed, 118 insertions, 10 deletions
diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim
index 4962186fb..d12c661d6 100644
--- a/lib/windows/winlean.nim
+++ b/lib/windows/winlean.nim
@@ -12,9 +12,18 @@
 
 {.deadCodeElim:on.}
 
+import dynlib
+
 const
   useWinUnicode* = not defined(useWinAnsi)
 
+when useWinUnicode:
+  type WinChar* = Utf16Char
+  {.deprecated: [TWinChar: WinChar].}
+else:
+  type WinChar* = char
+  {.deprecated: [TWinChar: WinChar].}
+
 type
   Handle* = int
   LONG* = int32
@@ -74,17 +83,18 @@ type
     nFileIndexHigh*: DWORD
     nFileIndexLow*: DWORD
 
+  OSVERSIONINFO* {.final, pure.} = object
+    dwOSVersionInfoSize*: DWORD
+    dwMajorVersion*: DWORD
+    dwMinorVersion*: DWORD
+    dwBuildNumber*: DWORD
+    dwPlatformId*: DWORD
+    szCSDVersion*: array[0..127, WinChar];
+
 {.deprecated: [THandle: Handle, TSECURITY_ATTRIBUTES: SECURITY_ATTRIBUTES,
     TSTARTUPINFO: STARTUPINFO, TPROCESS_INFORMATION: PROCESS_INFORMATION,
     TFILETIME: FILETIME, TBY_HANDLE_FILE_INFORMATION: BY_HANDLE_FILE_INFORMATION].}
 
-when useWinUnicode:
-  type WinChar* = Utf16Char
-  {.deprecated: [TWinChar: WinChar].}
-else:
-  type WinChar* = char
-  {.deprecated: [TWinChar: WinChar].}
-
 const
   STARTF_USESHOWWINDOW* = 1'i32
   STARTF_USESTDHANDLES* = 256'i32
@@ -117,6 +127,13 @@ const
 
   CREATE_NO_WINDOW* = 0x08000000'i32
 
+when useWinUnicode:
+  proc getVersionExW*(lpVersionInfo: ptr OSVERSIONINFO): WINBOOL {.stdcall, dynlib: "kernel32", importc: "GetVersionExW".}
+else:
+  proc getVersionExA*(lpVersionInfo: ptr OSVERSIONINFO): WINBOOL {.stdcall, dynlib: "kernel32", importc: "GetVersionExA".}
+
+proc getVersion*(): DWORD {.stdcall, dynlib: "kernel32", importc: "GetVersion".}
+
 proc closeHandle*(hObject: Handle): WINBOOL {.stdcall, dynlib: "kernel32",
     importc: "CloseHandle".}
 
@@ -192,6 +209,9 @@ proc flushFileBuffers*(hFile: Handle): WINBOOL {.stdcall, dynlib: "kernel32",
 proc getLastError*(): int32 {.importc: "GetLastError",
     stdcall, dynlib: "kernel32".}
 
+proc setLastError*(error: int32) {.importc: "SetLastError",
+    stdcall, dynlib: "kernel32".}
+
 when useWinUnicode:
   proc formatMessageW*(dwFlags: int32, lpSource: pointer,
                       dwMessageId, dwLanguageId: int32,
@@ -597,9 +617,6 @@ proc freeaddrinfo*(ai: ptr AddrInfo) {.
 proc inet_ntoa*(i: InAddr): cstring {.
   stdcall, importc, dynlib: ws2dll.}
 
-proc inet_ntop*(family: cint, paddr: pointer, pStringBuffer: cstring,
-            stringBufSize: int32): cstring {.stdcall, importc, dynlib: ws2dll.}
-
 const
   MAXIMUM_WAIT_OBJECTS* = 0x00000040
 
@@ -645,6 +662,7 @@ const
 const
   ERROR_ACCESS_DENIED* = 5
   ERROR_HANDLE_EOF* = 38
+  ERROR_BAD_ARGUMENTS* = 165
 
 proc duplicateHandle*(hSourceProcessHandle: HANDLE, hSourceHandle: HANDLE,
                       hTargetProcessHandle: HANDLE,
@@ -806,3 +824,54 @@ proc getSystemTimes*(lpIdleTime, lpKernelTime,
 proc getProcessTimes*(hProcess: Handle; lpCreationTime, lpExitTime,
   lpKernelTime, lpUserTime: var FILETIME): WINBOOL {.stdcall,
   dynlib: "kernel32", importc: "GetProcessTimes".}
+
+type inet_ntop_proc = proc(family: cint, paddr: pointer, pStringBuffer: cstring,
+                      stringBufSize: int32): cstring {.stdcall.}
+
+var inet_ntop_real: inet_ntop_proc = nil
+
+let l = loadLib(ws2dll)
+if l != nil:
+  inet_ntop_real = cast[inet_ntop_proc](symAddr(l, "inet_ntop"))
+
+proc WSAAddressToStringA(pAddr: ptr SockAddr, addrSize: DWORD, unused: pointer, pBuff: cstring, pBuffSize: ptr DWORD): cint {.stdcall, importc, dynlib: ws2dll.}
+proc inet_ntop_emulated(family: cint, paddr: pointer, pStringBuffer: cstring,
+                  stringBufSize: int32): cstring {.stdcall.} =
+    case family
+    of AF_INET:
+      var sa: Sockaddr_in
+      sa.sin_family = AF_INET
+      sa.sin_addr = cast[ptr InAddr](paddr)[]
+      var bs = stringBufSize.DWORD
+      let r = WSAAddressToStringA(cast[ptr SockAddr](sa.addr), sa.sizeof.DWORD, nil, pStringBuffer, bs.addr)
+      if r != 0:
+        result = nil
+      else:
+        result = pStringBuffer
+    of AF_INET6:
+      var sa: Sockaddr_in6
+      sa.sin6_family = AF_INET6
+      sa.sin6_addr = cast[ptr In6_addr](paddr)[]
+      var bs = stringBufSize.DWORD
+      let r = WSAAddressToStringA(cast[ptr SockAddr](sa.addr), sa.sizeof.DWORD, nil, pStringBuffer, bs.addr)
+      if r != 0:
+        result = nil
+      else:
+        result = pStringBuffer
+    else:
+      setLastError(ERROR_BAD_ARGUMENTS)
+      result = nil
+
+proc inet_ntop*(family: cint, paddr: pointer, pStringBuffer: cstring,
+                  stringBufSize: int32): cstring {.stdcall.} =
+  var ver: OSVERSIONINFO
+  ver.dwOSVersionInfoSize = sizeof(ver).DWORD
+  let res = when useWinUnicode: getVersionExW(ver.addr) else: getVersionExA(ver.addr)
+  if res == 0:
+    result = nil
+  elif ver.dwMajorVersion >= 6:
+    if inet_ntop_real == nil:
+      quit("Can't load inet_ntop proc from " & ws2dll)
+    result = inet_ntop_real(family, paddr, pStringBuffer, stringBufSize)
+  else:
+    result = inet_ntop_emulated(family, paddr, pStringBuffer, stringBufSize)
diff --git a/tests/stdlib/tnet_ll.nim b/tests/stdlib/tnet_ll.nim
new file mode 100644
index 000000000..4d4df7c13
--- /dev/null
+++ b/tests/stdlib/tnet_ll.nim
@@ -0,0 +1,39 @@
+discard """

+  action: run

+"""

+

+when defined(windows):

+  import winlean

+elif defined(posix):

+  import posix

+else:

+  {.error: "Unsupported OS".}

+

+import unittest, strutils

+

+suite "inet_ntop tests":

+

+  setup:

+    when defined(windows):

+      var wsa: WSAData

+      discard wsaStartup(0x101'i16, wsa.addr)

+  

+  test "IP V4":

+    var ip4 = 0x10111213

+    var buff: array[0..255, char]

+    let r = inet_ntop(AF_INET, ip4.addr, buff[0].addr, buff.sizeof.int32)

+    let res = if r == nil: "" else: $r

+    check: res == "19.18.17.16"

+      

+

+  test "IP V6":

+    when defined(windows):

+      let ipv6Support = (getVersion() and 0xff) > 0x5

+    else:

+      let ipv6Support = true

+          

+    var ip6 = [0x1000'u16, 0x1001, 0x2000, 0x2001, 0x3000, 0x3001, 0x4000, 0x4001]

+    var buff: array[0..255, char]

+    let r = inet_ntop(AF_INET6, ip6[0].addr, buff[0].addr, buff.sizeof.int32)

+    let res = if r == nil: "" else: $r

+    check: not ipv6Support or res == "10:110:20:120:30:130:40:140"