Skip to content

Instantly share code, notes, and snippets.

@Varriount
Last active January 4, 2016 21:39
Show Gist options
  • Save Varriount/5ec6370dd37bb51cd947 to your computer and use it in GitHub Desktop.
Save Varriount/5ec6370dd37bb51cd947 to your computer and use it in GitHub Desktop.
I don't know how I fixed it, but I did. Also, keep in mind that when taking the address of parameters, the parameters must be passed by ref or ptr
import os, oids, strutils, winlean, socketsll
from windows import nil
## Proactor
## --------
##
## This module implements the proactor pattern, unlike selectors the
## asynchronous I/O operation is first initiated and then waited upon.
# Wrapped API Structures
type
TGUID* {.final, pure.} = object
D1*: int32
D2*: int16
D3*: int16
D4*: array [0..7, int8]
Overlapped* {.final, pure.} = object
## Be very careful when passing this object around. An overlapped object
## passed to a windows API call like connectEx MUST remain allocated at
## the same address untils internal window mechanisms are done with it.
## Such mechanisms include the Completion Queues, events, etc.
Internal*: ptr LONG
InternalHigh*: ptr LONG
Offset*: DWORD
OffsetHigh*: DWORD
hEvent*: THandle
POverlapped* {.final, pure.} = ptr Overlapped
CompletionRoutine {.final, pure.} = proc (
dwError, cbTransferred : DWORD,
lpOverlapped: POverlapped,
dwFlags: DWORD
)
ConnectExProc* {.final, pure.} = proc (
socket: TSocketHandle,
name: ptr TSockAddr,
namelen: cint,
lpSendBuffer: pointer, dwSendDataLength: DWORD,
lpdwBytesSent: ptr DWORD,
lpOverlapped: POverlapped
): bool {.stdcall.}
const
IOC_OUT = 0x40000000
IOC_IN = 0x80000000
IOC_WS2 = 0x08000000
IOC_INOUT = IOC_IN or IOC_OUT
template WSAIORW(x,y): expr = (IOC_INOUT or x or y)
const
SIO_GET_EXTENSION_FUNCTION_POINTER* = WSAIORW(IOC_WS2,6).DWORD
var
WSAID_CONNECTEX* = TGUID(
D1: 0x25a207b9,
D2: 0xddf3'i16,
D3: 0x4660'i16,
D4: [0x8e'i8, 0xe9'i8, 0x76'i8, 0xe5'i8,
0x8c'i8, 0x74'i8, 0x06'i8, 0x3e'i8]
)
# Wrapped API Procedures
proc CreateIoCompletionPort*(FileHandle: THandle, ExistingCompletionPort: THandle,
CompletionKey: ptr LONG,
NumberOfConcurrentThreads: DWORD): THandle{.stdcall,
dynlib: "kernel32", importc: "CreateIoCompletionPort".}
proc GetQueuedCompletionStatus*(CompletionPort: THandle,
lpNumberOfBytesTransferred: ptr DWORD, lpCompletionKey: ptr int,
lpOverlapped: ptr POverlapped,
dwMilliseconds: DWORD): WINBOOL{.stdcall,
dynlib: "kernel32", importc: "GetQueuedCompletionStatus".}
proc WSAIoctl*(s: TSocketHandle, dwIoControlCode: DWORD, lpvInBuffer: pointer,
cbInBuffer: DWORD, lpvOutBuffer: pointer, cbOutBuffer: dword,
lpcbBytesReturned: ptr DWORD, lpOverlapped: POverlapped,
lpCompletionRoutine: CompletionRoutine): cint {.stdcall, importc: "WSAIoctl", dynlib: "Ws2_32.dll".}
# Helper Structures and Procedures
var
nilKey: LONG
nilHandle: THandle
overlappedStruct: Overlapped
proc createIOCP(maxThreads: int32 = 0): THandle =
result = CreateIoCompletionPort(
INVALID_HANDLE_VALUE,
nilHandle,
addr nilKey,
0
)
proc addHandle(iocp, hFile: THandle, key: var LONG = nilKey): THandle =
result = CreateIoCompletionPort(
hFile,
iocp,
addr key,
0)
proc getConnectEx*(s: TSocketHandle, guid: var TGUID): ConnectExProc =
# Ref: https://github.com/powdahound/twisted/blob/master/twisted/internet/iocpreactor/iocpsupport/winsock_pointers.c
var bytesRet: DWORD
var connectExPtr: pointer = nil
let state = WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, addr guid,
sizeof(TGUID).dword, addr connectExPtr, sizeof(pointer).DWORD,
addr bytesRet, nil, nil) == 0
if not state:
assert(false)
result = cast[ConnectExProc](connectExPtr)
# Other stuff
type
TCompletionKey {.pure.} = dword
TCompletionData = object
sock: TSocketHandle
cb: proc (sock: TSocketHandle) {.closure.}
PProactor* {.pure.} = ref object
ioPort: THandle
overlapps: seq[Overlapped]
keys: seq[TCompletionKey]
datas: seq[TCompletionData]
PFuture*[T] = ref object
value*: T
finished*: bool
cb: proc (future: PFuture[T]) {.closure.}
proc newProactor*(): PProactor =
## Creates a new proactor instance.
new result
result.overlapps = newSeq[Overlapped](16)
result.keys = newSeq[TCompletionKey]()
result.datas = newSeq[TCompletionData]()
result.ioPort = createIOCP()
proc register*(p: PProactor, sock: TSocketHandle) =
# If sock is a local copy, and it's address is given as a key,
# what happens when it goes out of scope?
discard p.ioPort.addHandle(sock.THandle)
if OSLastError().int32 != 0'i32:
OSError(OSLastError())
proc setCallback*(p: PProactor, sock: TSocketHandle, cb: proc (sock: TSocketHandle) {.closure.}) =
## Sets the proc to call when a completion event is received for ``sock``.
echo("Setting callback with key: ", $sock.int)
p.keys.add(sock.TCompletionKey)
echo("Middle")
var tdata: TCompletionData
tdata.sock = sock
tdata.cb = cb
p.datas.add(tdata)
echo("End")
proc poll*(p: PProactor, timeout = 500) =
let llTimeout =
if timeout == -1: windows.INFINITE
else: timeout.int32
var
bytesTransferred:DWORD
key: LONG
statusOverlapped: Overlapped
statusOverlappedPtr = addr statusOverlapped
keyPtr = addr key
echo(repr(p.ioPort))
var res = GetQueuedCompletionStatus(
p.ioPort,
addr bytesTransferred,
keyPtr,
addr statusOverlappedPtr,
llTimeout.DWORD
)
key = keyPtr[]
if res.bool:
# We got a completion event.
echo("Got a completion event: ", key)
let dataIndex = p.keys.find(key.TCompletionKey)
if dataIndex < 0:
echo("Possible error: ", dataIndex)
let data = p.datas[0]
data.cb(data.sock)
else:
var errCode = OSLastError()
if errCode.int32 == WAIT_TIMEOUT:
# Timed out
nil
else: OSError(errCode)
# -- Futures
proc newFuture*[T](): PFuture[T] =
new(result)
result.finished = false
proc complete*[T](future: PFuture[T], val: T) =
assert(not future.finished)
future.value = val
future.finished = true
if future.cb != nil:
future.cb(future)
proc `callback=`*[T](future: PFuture[T],
cb: proc (future: PFuture[T]) {.closure.}) =
## Sets the callback proc to be called when the future completes.
##
## If future has already completed then ``cb`` will be called immediately.
future.cb = cb
if future.finished:
future.cb(future)
# template WSAIORW(x,y): expr = (IOC_INOUT or x or y)
# const
# SIO_GET_EXTENSION_FUNCTION_POINTER = WSAIORW(IOC_WS2,6).DWORD
# var
# WSAID_CONNECTEX: TGUID = TGUID(D1: 0x25a207b9, D2: 0xddf3'i16, D3: 0x4660, D4: [
# 0x8e'i8, 0xe9'i8, 0x76'i8, 0xe5'i8, 0x8c'i8, 0x74'i8,
# 0x06'i8, 0x3e'i8])
var connectEx: pointer = nil
proc initPointer*(s: TSocketHandle, func: var pointer, guid: var TGUID): bool =
# Ref: https://github.com/powdahound/twisted/blob/master/twisted/internet/iocpreactor/iocpsupport/winsock_pointers.c
var bytesRet: DWord
func = nil
result = WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, addr guid,
sizeof(TGUID).dword, addr func, sizeof(pointer).DWORD,
addr bytesRet, nil, nil) == 0
proc initAll*() =
connectEx = getConnectEx(socket(), WSAID_CONNECTEX)
proc connect(p: PProactor, ssocket: TSocketHandle, address: string, port: TPort,
af = AF_INET): PFuture[int] =
## Connects ``socket`` to server at ``address:port``.
##
## Returns a ``PFuture`` which will complete when the connection succeeds
## or an error occurs.
let dummySocket = socket()
let connectExPtr = getConnectEx(dummySocket, WSAID_CONNECTEX)
var retFuture = newFuture[int]()# TODO: Change to void when that regression is fixed.
# Apparently ``ConnectEx`` expects the socket to be initially bound:
var sockAddress: Tsockaddr_in
var sockAddressPtr = cast[ptr TSockAddr](addr(sockAddress))
sockAddress.sin_family = int16(toInt(AF_INET))
sockAddress.sin_port = 0
sockAddress.sin_addr.s_addr = INADDR_ANY
if bindSocket(ssocket, sockAddressPtr, sizeof(sockAddress).TSockLen) < 0'i32:
OSError(OSLastError())
var aiList = getAddrInfo(address, port, af)
var success = false
var lastError: TOSErrorCode
var it = aiList
while it != nil:
when true:
var ret = connectExPtr(ssocket, it.ai_addr, sizeof(TSockAddrIn).cint,
nil, 0, nil, addr overlappedStruct)
else:
var ol = cast[ptr TOverlapped](alloc0(sizeof(TOverlapped)))
var ret = connectEx(socket, it.ai_addr, sizeof(TSockAddrIn).cint,
nil, 0, nil, ol)
dealloc(ol)
if ret:
echo "Connected"
success = true
retFuture.finished = true
break
else:
lastError = OSLastError()
when defined(windows):
if lastError.int32 == windows.ERROR_IO_PENDING or lastError.int32 == 0:
success = true
p.setCallback(ssocket,
proc (sock: TSocketHandle) {.closure.} =
echo("Connected")
retFuture.complete(0)
)
break
else:
OSError(lastError)
it = it.ai_next
dealloc(aiList)
if not success: OSError(lastError)
return retFuture
when isMainModule:
initAll()
var p = newProactor()
var sock = socket()
#sock.setBlocking false
p.register(sock)
var f = p.connect(sock, "irc.freenode.net", TPort(6667))
f.callback = proc (future: PFuture[int]) = echo("Connected in future!")
#p.recv(sock, 10)
while true:
p.poll()
echo "polled"
#
#
# Nimrod's Runtime Library
# (c) Copyright 2014 Dominik Picheta
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements a low-level cross-platform sockets interface. Look
## at the ``net`` module for the higher-level version.
import unsigned, os
when hostos == "solaris":
{.passl: "-lsocket -lnsl".}
when defined(Windows):
import winlean
else:
import posix
type
TPort* = distinct uint16 ## port type
TDomain* = enum ## domain, which specifies the protocol family of the
## created socket. Other domains than those that are listed
## here are unsupported.
AF_UNIX, ## for local socket (using a file). Unsupported on Windows.
AF_INET = 2, ## for network protocol IPv4 or
AF_INET6 = 23 ## for network protocol IPv6.
TType* = enum ## second argument to `socket` proc
SOCK_STREAM = 1, ## reliable stream-oriented service or Stream Sockets
SOCK_DGRAM = 2, ## datagram service or Datagram Sockets
SOCK_RAW = 3, ## raw protocols atop the network layer.
SOCK_SEQPACKET = 5 ## reliable sequenced packet service
TProtocol* = enum ## third argument to `socket` proc
IPPROTO_TCP = 6, ## Transmission control protocol.
IPPROTO_UDP = 17, ## User datagram protocol.
IPPROTO_IP, ## Internet protocol. Unsupported on Windows.
IPPROTO_IPV6, ## Internet Protocol Version 6. Unsupported on Windows.
IPPROTO_RAW, ## Raw IP Packets Protocol. Unsupported on Windows.
IPPROTO_ICMP ## Control message protocol. Unsupported on Windows.
TServent* {.pure, final.} = object ## information about a service
name*: string
aliases*: seq[string]
port*: TPort
proto*: string
Thostent* {.pure, final.} = object ## information about a given host
name*: string
aliases*: seq[string]
addrtype*: TDomain
length*: int
addrList*: seq[string]
when defined(windows):
let
OSInvalidSocket* = winlean.INVALID_SOCKET
else:
let
OSInvalidSocket* = posix.INVALID_SOCKET
proc `==`*(a, b: TPort): bool {.borrow.}
## ``==`` for ports.
proc `$`*(p: TPort): string {.borrow.}
## returns the port number as a string
proc toInt*(domain: TDomain): cint
## Converts the TDomain enum to a platform-dependent ``cint``.
proc toInt*(typ: TType): cint
## Converts the TType enum to a platform-dependent ``cint``.
proc toInt*(p: TProtocol): cint
## Converts the TProtocol enum to a platform-dependent ``cint``.
when defined(posix):
proc toInt(domain: TDomain): cint =
case domain
of AF_UNIX: result = posix.AF_UNIX
of AF_INET: result = posix.AF_INET
of AF_INET6: result = posix.AF_INET6
else: nil
proc toInt(typ: TType): cint =
case typ
of SOCK_STREAM: result = posix.SOCK_STREAM
of SOCK_DGRAM: result = posix.SOCK_DGRAM
of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET
of SOCK_RAW: result = posix.SOCK_RAW
else: nil
proc toInt(p: TProtocol): cint =
case p
of IPPROTO_TCP: result = posix.IPPROTO_TCP
of IPPROTO_UDP: result = posix.IPPROTO_UDP
of IPPROTO_IP: result = posix.IPPROTO_IP
of IPPROTO_IPV6: result = posix.IPPROTO_IPV6
of IPPROTO_RAW: result = posix.IPPROTO_RAW
of IPPROTO_ICMP: result = posix.IPPROTO_ICMP
else: nil
else:
proc toInt(domain: TDomain): cint =
result = toU16(ord(domain))
proc toInt(typ: TType): cint =
result = cint(ord(typ))
proc toInt(p: TProtocol): cint =
result = cint(ord(p))
proc socket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
protocol: TProtocol = IPPROTO_TCP): TSocketHandle =
## Creates a new socket; returns `InvalidSocket` if an error occurs.
# TODO: The function which will use this will raise EOS.
socket(toInt(domain), toInt(typ), toInt(protocol))
proc close*(socket: TSocketHandle) =
## closes a socket.
when defined(windows):
discard winlean.closeSocket(socket)
else:
discard posix.close(socket)
# TODO: These values should not be discarded. An EOS should be raised.
# http://stackoverflow.com/questions/12463473/what-happens-if-you-call-close-on-a-bsd-socket-multiple-times
proc getAddrInfo*(address: string, port: TPort, af: TDomain = AF_INET, typ: TType = SOCK_STREAM,
prot: TProtocol = IPPROTO_TCP): ptr TAddrInfo =
## **Warning**: The resulting ``ptr TAddrInfo`` must be freed using ``dealloc``!
# Set up the address hints structure
var hints: TAddrInfo
hints.ai_family = toInt(af)
hints.ai_socktype = toInt(typ)
hints.ai_protocol = toInt(prot)
# Get the address info structure
var addressInfoPtr: ptr TAddrInfo
var gaiResult = getAddrInfo(address, $port, addr hints, addressInfoPtr)
if gaiResult != 0'i32:
when defined(windows):
OSError(OSLastError())
else:
raise newException(EOS, $gai_strerror(gaiResult))
return addressInfoPtr
proc dealloc*(ai: ptr TAddrInfo) =
freeaddrinfo(ai)
when defined(Windows):
var wsa: TWSADATA
if WSAStartup(0x0101'i16, addr wsa) != 0: OSError(OSLastError())
#
#
# Nimrod's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements a small wrapper for some needed Win API procedures,
## so that the Nimrod compiler does not depend on the huge Windows module.
const
useWinUnicode* = not defined(useWinAnsi)
type
THandle* = int
LONG* = int32
WINBOOL* = int32
DWORD* = int32
HDC* = THandle
HGLRC* = THandle
TSECURITY_ATTRIBUTES* {.final, pure.} = object
nLength*: int32
lpSecurityDescriptor*: pointer
bInheritHandle*: WINBOOL
TSTARTUPINFO* {.final, pure.} = object
cb*: int32
lpReserved*: cstring
lpDesktop*: cstring
lpTitle*: cstring
dwX*: int32
dwY*: int32
dwXSize*: int32
dwYSize*: int32
dwXCountChars*: int32
dwYCountChars*: int32
dwFillAttribute*: int32
dwFlags*: int32
wShowWindow*: int16
cbReserved2*: int16
lpReserved2*: pointer
hStdInput*: THandle
hStdOutput*: THandle
hStdError*: THandle
TPROCESS_INFORMATION* {.final, pure.} = object
hProcess*: THandle
hThread*: THandle
dwProcessId*: int32
dwThreadId*: int32
TFILETIME* {.final, pure.} = object ## CANNOT BE int64 BECAUSE OF ALIGNMENT
dwLowDateTime*: DWORD
dwHighDateTime*: DWORD
TBY_HANDLE_FILE_INFORMATION* {.final, pure.} = object
dwFileAttributes*: DWORD
ftCreationTime*: TFILETIME
ftLastAccessTime*: TFILETIME
ftLastWriteTime*: TFILETIME
dwVolumeSerialNumber*: DWORD
nFileSizeHigh*: DWORD
nFileSizeLow*: DWORD
nNumberOfLinks*: DWORD
nFileIndexHigh*: DWORD
nFileIndexLow*: DWORD
when useWinUnicode:
type TWinChar* = TUtf16Char
else:
type TWinChar* = char
const
STARTF_USESHOWWINDOW* = 1'i32
STARTF_USESTDHANDLES* = 256'i32
HIGH_PRIORITY_CLASS* = 128'i32
IDLE_PRIORITY_CLASS* = 64'i32
NORMAL_PRIORITY_CLASS* = 32'i32
REALTIME_PRIORITY_CLASS* = 256'i32
WAIT_OBJECT_0* = 0'i32
WAIT_TIMEOUT* = 0x00000102'i32
WAIT_FAILED* = 0xFFFFFFFF'i32
INFINITE* = -1'i32
STD_INPUT_HANDLE* = -10'i32
STD_OUTPUT_HANDLE* = -11'i32
STD_ERROR_HANDLE* = -12'i32
DETACHED_PROCESS* = 8'i32
SW_SHOWNORMAL* = 1'i32
INVALID_HANDLE_VALUE* = THandle(-1)
CREATE_UNICODE_ENVIRONMENT* = 1024'i32
proc closeHandle*(hObject: THandle): WINBOOL {.stdcall, dynlib: "kernel32",
importc: "CloseHandle".}
proc readFile*(hFile: THandle, Buffer: pointer, nNumberOfBytesToRead: int32,
lpNumberOfBytesRead: var int32, lpOverlapped: pointer): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "ReadFile".}
proc writeFile*(hFile: THandle, Buffer: pointer, nNumberOfBytesToWrite: int32,
lpNumberOfBytesWritten: var int32,
lpOverlapped: pointer): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "WriteFile".}
proc createPipe*(hReadPipe, hWritePipe: var THandle,
lpPipeAttributes: var TSECURITY_ATTRIBUTES,
nSize: int32): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "CreatePipe".}
when useWinUnicode:
proc createProcessW*(lpApplicationName, lpCommandLine: WideCString,
lpProcessAttributes: ptr TSECURITY_ATTRIBUTES,
lpThreadAttributes: ptr TSECURITY_ATTRIBUTES,
bInheritHandles: WINBOOL, dwCreationFlags: int32,
lpEnvironment, lpCurrentDirectory: WideCString,
lpStartupInfo: var TSTARTUPINFO,
lpProcessInformation: var TPROCESS_INFORMATION): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "CreateProcessW".}
else:
proc createProcessA*(lpApplicationName, lpCommandLine: cstring,
lpProcessAttributes: ptr TSECURITY_ATTRIBUTES,
lpThreadAttributes: ptr TSECURITY_ATTRIBUTES,
bInheritHandles: WINBOOL, dwCreationFlags: int32,
lpEnvironment: pointer, lpCurrentDirectory: cstring,
lpStartupInfo: var TSTARTUPINFO,
lpProcessInformation: var TPROCESS_INFORMATION): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "CreateProcessA".}
proc suspendThread*(hThread: THandle): int32 {.stdcall, dynlib: "kernel32",
importc: "SuspendThread".}
proc resumeThread*(hThread: THandle): int32 {.stdcall, dynlib: "kernel32",
importc: "ResumeThread".}
proc waitForSingleObject*(hHandle: THandle, dwMilliseconds: int32): int32 {.
stdcall, dynlib: "kernel32", importc: "WaitForSingleObject".}
proc terminateProcess*(hProcess: THandle, uExitCode: int): WINBOOL {.stdcall,
dynlib: "kernel32", importc: "TerminateProcess".}
proc getExitCodeProcess*(hProcess: THandle, lpExitCode: var int32): WINBOOL {.
stdcall, dynlib: "kernel32", importc: "GetExitCodeProcess".}
proc getStdHandle*(nStdHandle: int32): THandle {.stdcall, dynlib: "kernel32",
importc: "GetStdHandle".}
proc setStdHandle*(nStdHandle: int32, hHandle: THandle): WINBOOL {.stdcall,
dynlib: "kernel32", importc: "SetStdHandle".}
proc flushFileBuffers*(hFile: THandle): WINBOOL {.stdcall, dynlib: "kernel32",
importc: "FlushFileBuffers".}
proc getLastError*(): int32 {.importc: "GetLastError",
stdcall, dynlib: "kernel32".}
when useWinUnicode:
proc formatMessageW*(dwFlags: int32, lpSource: pointer,
dwMessageId, dwLanguageId: int32,
lpBuffer: pointer, nSize: int32,
Arguments: pointer): int32 {.
importc: "FormatMessageW", stdcall, dynlib: "kernel32".}
else:
proc formatMessageA*(dwFlags: int32, lpSource: pointer,
dwMessageId, dwLanguageId: int32,
lpBuffer: pointer, nSize: int32,
Arguments: pointer): int32 {.
importc: "FormatMessageA", stdcall, dynlib: "kernel32".}
proc localFree*(p: pointer) {.
importc: "LocalFree", stdcall, dynlib: "kernel32".}
when useWinUnicode:
proc getCurrentDirectoryW*(nBufferLength: int32,
lpBuffer: WideCString): int32 {.
importc: "GetCurrentDirectoryW", dynlib: "kernel32", stdcall.}
proc setCurrentDirectoryW*(lpPathName: WideCString): int32 {.
importc: "SetCurrentDirectoryW", dynlib: "kernel32", stdcall.}
proc createDirectoryW*(pathName: WideCString, security: pointer=nil): int32 {.
importc: "CreateDirectoryW", dynlib: "kernel32", stdcall.}
proc removeDirectoryW*(lpPathName: WideCString): int32 {.
importc: "RemoveDirectoryW", dynlib: "kernel32", stdcall.}
proc setEnvironmentVariableW*(lpName, lpValue: WideCString): int32 {.
stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableW".}
proc getModuleFileNameW*(handle: THandle, buf: WideCString,
size: int32): int32 {.importc: "GetModuleFileNameW",
dynlib: "kernel32", stdcall.}
else:
proc getCurrentDirectoryA*(nBufferLength: int32, lpBuffer: cstring): int32 {.
importc: "GetCurrentDirectoryA", dynlib: "kernel32", stdcall.}
proc setCurrentDirectoryA*(lpPathName: cstring): int32 {.
importc: "SetCurrentDirectoryA", dynlib: "kernel32", stdcall.}
proc createDirectoryA*(pathName: cstring, security: Pointer=nil): int32 {.
importc: "CreateDirectoryA", dynlib: "kernel32", stdcall.}
proc removeDirectoryA*(lpPathName: cstring): int32 {.
importc: "RemoveDirectoryA", dynlib: "kernel32", stdcall.}
proc setEnvironmentVariableA*(lpName, lpValue: cstring): int32 {.
stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableA".}
proc getModuleFileNameA*(handle: THandle, buf: CString, size: int32): int32 {.
importc: "GetModuleFileNameA", dynlib: "kernel32", stdcall.}
const
FILE_ATTRIBUTE_ARCHIVE* = 32'i32
FILE_ATTRIBUTE_COMPRESSED* = 2048'i32
FILE_ATTRIBUTE_NORMAL* = 128'i32
FILE_ATTRIBUTE_DIRECTORY* = 16'i32
FILE_ATTRIBUTE_HIDDEN* = 2'i32
FILE_ATTRIBUTE_READONLY* = 1'i32
FILE_ATTRIBUTE_SYSTEM* = 4'i32
FILE_ATTRIBUTE_TEMPORARY* = 256'i32
MAX_PATH* = 260
type
TWIN32_FIND_DATA* {.pure.} = object
dwFileAttributes*: int32
ftCreationTime*: TFILETIME
ftLastAccessTime*: TFILETIME
ftLastWriteTime*: TFILETIME
nFileSizeHigh*: int32
nFileSizeLow*: int32
dwReserved0: int32
dwReserved1: int32
cFileName*: array[0..(MAX_PATH) - 1, TWinChar]
cAlternateFileName*: array[0..13, TWinChar]
when useWinUnicode:
proc findFirstFileW*(lpFileName: WideCString,
lpFindFileData: var TWIN32_FIND_DATA): THandle {.
stdcall, dynlib: "kernel32", importc: "FindFirstFileW".}
proc findNextFileW*(hFindFile: THandle,
lpFindFileData: var TWIN32_FIND_DATA): int32 {.
stdcall, dynlib: "kernel32", importc: "FindNextFileW".}
else:
proc findFirstFileA*(lpFileName: cstring,
lpFindFileData: var TWIN32_FIND_DATA): THANDLE {.
stdcall, dynlib: "kernel32", importc: "FindFirstFileA".}
proc findNextFileA*(hFindFile: THANDLE,
lpFindFileData: var TWIN32_FIND_DATA): int32 {.
stdcall, dynlib: "kernel32", importc: "FindNextFileA".}
proc findClose*(hFindFile: THandle) {.stdcall, dynlib: "kernel32",
importc: "FindClose".}
when useWinUnicode:
proc getFullPathNameW*(lpFileName: WideCString, nBufferLength: int32,
lpBuffer: WideCString,
lpFilePart: var WideCString): int32 {.
stdcall, dynlib: "kernel32",
importc: "GetFullPathNameW".}
proc getFileAttributesW*(lpFileName: WideCString): int32 {.
stdcall, dynlib: "kernel32",
importc: "GetFileAttributesW".}
proc setFileAttributesW*(lpFileName: WideCString,
dwFileAttributes: int32): WINBOOL {.
stdcall, dynlib: "kernel32", importc: "SetFileAttributesW".}
proc copyFileW*(lpExistingFileName, lpNewFileName: WideCString,
bFailIfExists: cint): cint {.
importc: "CopyFileW", stdcall, dynlib: "kernel32".}
proc getEnvironmentStringsW*(): WideCString {.
stdcall, dynlib: "kernel32", importc: "GetEnvironmentStringsW".}
proc freeEnvironmentStringsW*(para1: WideCString): int32 {.
stdcall, dynlib: "kernel32", importc: "FreeEnvironmentStringsW".}
proc getCommandLineW*(): WideCString {.importc: "GetCommandLineW",
stdcall, dynlib: "kernel32".}
else:
proc getFullPathNameA*(lpFileName: cstring, nBufferLength: int32,
lpBuffer: cstring, lpFilePart: var cstring): int32 {.
stdcall, dynlib: "kernel32",
importc: "GetFullPathNameA".}
proc getFileAttributesA*(lpFileName: cstring): int32 {.
stdcall, dynlib: "kernel32",
importc: "GetFileAttributesA".}
proc setFileAttributesA*(lpFileName: cstring,
dwFileAttributes: int32): WINBOOL {.
stdcall, dynlib: "kernel32", importc: "SetFileAttributesA".}
proc copyFileA*(lpExistingFileName, lpNewFileName: CString,
bFailIfExists: cint): cint {.
importc: "CopyFileA", stdcall, dynlib: "kernel32".}
proc getEnvironmentStringsA*(): cstring {.
stdcall, dynlib: "kernel32", importc: "GetEnvironmentStringsA".}
proc freeEnvironmentStringsA*(para1: cstring): int32 {.
stdcall, dynlib: "kernel32", importc: "FreeEnvironmentStringsA".}
proc getCommandLineA*(): cstring {.
importc: "GetCommandLineA", stdcall, dynlib: "kernel32".}
proc rdFileTime*(f: TFILETIME): int64 =
result = ze64(f.dwLowDateTime) or (ze64(f.dwHighDateTime) shl 32)
proc rdFileSize*(f: TWIN32_FIND_DATA): int64 =
result = ze64(f.nFileSizeLow) or (ze64(f.nFileSizeHigh) shl 32)
proc getSystemTimeAsFileTime*(lpSystemTimeAsFileTime: var TFILETIME) {.
importc: "GetSystemTimeAsFileTime", dynlib: "kernel32", stdcall.}
proc sleep*(dwMilliseconds: int32){.stdcall, dynlib: "kernel32",
importc: "Sleep".}
when useWinUnicode:
proc shellExecuteW*(HWND: THandle, lpOperation, lpFile,
lpParameters, lpDirectory: WideCString,
nShowCmd: int32): THandle{.
stdcall, dynlib: "shell32.dll", importc: "ShellExecuteW".}
else:
proc shellExecuteA*(HWND: THandle, lpOperation, lpFile,
lpParameters, lpDirectory: cstring,
nShowCmd: int32): THandle{.
stdcall, dynlib: "shell32.dll", importc: "ShellExecuteA".}
proc getFileInformationByHandle*(hFile: THandle,
lpFileInformation: ptr TBY_HANDLE_FILE_INFORMATION): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "GetFileInformationByHandle".}
const
WSADESCRIPTION_LEN* = 256
WSASYS_STATUS_LEN* = 128
FD_SETSIZE* = 64
MSG_PEEK* = 2
INADDR_ANY* = 0
INADDR_LOOPBACK* = 0x7F000001
INADDR_BROADCAST* = -1
INADDR_NONE* = -1
ws2dll = "Ws2_32.dll"
WSAEWOULDBLOCK* = 10035
WSAEINPROGRESS* = 10036
proc wsaGetLastError*(): cint {.importc: "WSAGetLastError", dynlib: ws2dll.}
type
TSocketHandle* = distinct int
type
TWSAData* {.pure, final, importc: "WSADATA", header: "Winsock2.h".} = object
wVersion, wHighVersion: int16
szDescription: array[0..WSADESCRIPTION_LEN, char]
szSystemStatus: array[0..WSASYS_STATUS_LEN, char]
iMaxSockets, iMaxUdpDg: int16
lpVendorInfo: cstring
TSockAddr* {.pure, final, importc: "SOCKADDR", header: "Winsock2.h".} = object
sa_family*: int16 # unsigned
sa_data: array[0..13, char]
TInAddr* {.pure, final, importc: "IN_ADDR", header: "Winsock2.h".} = object
s_addr*: int32 # IP address
Tsockaddr_in* {.pure, final, importc: "SOCKADDR_IN",
header: "Winsock2.h".} = object
sin_family*: int16
sin_port*: int16 # unsigned
sin_addr*: TInAddr
sin_zero*: array[0..7, char]
Tin6_addr* {.pure, final, importc: "IN6_ADDR", header: "Winsock2.h".} = object
bytes*: array[0..15, char]
Tsockaddr_in6* {.pure, final, importc: "SOCKADDR_IN6",
header: "Winsock2.h".} = object
sin6_family*: int16
sin6_port*: int16 # unsigned
sin6_flowinfo*: int32 # unsigned
sin6_addr*: Tin6_addr
sin6_scope_id*: int32 # unsigned
Tsockaddr_in6_old* {.pure, final.} = object
sin6_family*: int16
sin6_port*: int16 # unsigned
sin6_flowinfo*: int32 # unsigned
sin6_addr*: Tin6_addr
TServent* {.pure, final.} = object
s_name*: cstring
s_aliases*: cstringArray
when defined(cpu64):
s_proto*: cstring
s_port*: int16
else:
s_port*: int16
s_proto*: cstring
Thostent* {.pure, final.} = object
h_name*: cstring
h_aliases*: cstringArray
h_addrtype*: int16
h_length*: int16
h_addr_list*: cstringArray
TFdSet* {.pure, final.} = object
fd_count*: cint # unsigned
fd_array*: array[0..FD_SETSIZE-1, TSocketHandle]
TTimeval* {.pure, final.} = object
tv_sec*, tv_usec*: int32
TAddrInfo* {.pure, final.} = object
ai_flags*: cint ## Input flags.
ai_family*: cint ## Address family of socket.
ai_socktype*: cint ## Socket type.
ai_protocol*: cint ## Protocol of socket.
ai_addrlen*: int ## Length of socket address.
ai_canonname*: cstring ## Canonical name of service location.
ai_addr*: ptr TSockAddr ## Socket address of socket.
ai_next*: ptr TAddrInfo ## Pointer to next in list.
TSockLen* = cuint
var
SOMAXCONN* {.importc, header: "Winsock2.h".}: cint
INVALID_SOCKET* {.importc, header: "Winsock2.h".}: TSocketHandle
SOL_SOCKET* {.importc, header: "Winsock2.h".}: cint
SO_DEBUG* {.importc, header: "Winsock2.h".}: cint ## turn on debugging info recording
SO_ACCEPTCONN* {.importc, header: "Winsock2.h".}: cint # socket has had listen()
SO_REUSEADDR* {.importc, header: "Winsock2.h".}: cint # allow local address reuse
SO_KEEPALIVE* {.importc, header: "Winsock2.h".}: cint # keep connections alive
SO_DONTROUTE* {.importc, header: "Winsock2.h".}: cint # just use interface addresses
SO_BROADCAST* {.importc, header: "Winsock2.h".}: cint # permit sending of broadcast msgs
SO_USELOOPBACK* {.importc, header: "Winsock2.h".}: cint # bypass hardware when possible
SO_LINGER* {.importc, header: "Winsock2.h".}: cint # linger on close if data present
SO_OOBINLINE* {.importc, header: "Winsock2.h".}: cint # leave received OOB data in line
SO_DONTLINGER* {.importc, header: "Winsock2.h".}: cint
SO_EXCLUSIVEADDRUSE* {.importc, header: "Winsock2.h".}: cint # disallow local address reuse
proc `==`*(x, y: TSocketHandle): bool {.borrow.}
proc getservbyname*(name, proto: cstring): ptr TServent {.
stdcall, importc: "getservbyname", dynlib: ws2dll.}
proc getservbyport*(port: cint, proto: cstring): ptr TServent {.
stdcall, importc: "getservbyport", dynlib: ws2dll.}
proc gethostbyaddr*(ip: ptr TInAddr, len: cuint, theType: cint): ptr Thostent {.
stdcall, importc: "gethostbyaddr", dynlib: ws2dll.}
proc gethostbyname*(name: cstring): ptr Thostent {.
stdcall, importc: "gethostbyname", dynlib: ws2dll.}
proc socket*(af, typ, protocol: cint): TSocketHandle {.
stdcall, importc: "socket", dynlib: ws2dll.}
proc closesocket*(s: TSocketHandle): cint {.
stdcall, importc: "closesocket", dynlib: ws2dll.}
proc accept*(s: TSocketHandle, a: ptr TSockAddr, addrlen: ptr TSockLen): TSocketHandle {.
stdcall, importc: "accept", dynlib: ws2dll.}
proc bindSocket*(s: TSocketHandle, name: ptr TSockAddr, namelen: TSockLen): cint {.
stdcall, importc: "bind", dynlib: ws2dll.}
proc connect*(s: TSocketHandle, name: ptr TSockAddr, namelen: TSockLen): cint {.
stdcall, importc: "connect", dynlib: ws2dll.}
proc getsockname*(s: TSocketHandle, name: ptr TSockAddr,
namelen: ptr TSockLen): cint {.
stdcall, importc: "getsockname", dynlib: ws2dll.}
proc getsockopt*(s: TSocketHandle, level, optname: cint, optval: pointer,
optlen: ptr TSockLen): cint {.
stdcall, importc: "getsockopt", dynlib: ws2dll.}
proc setsockopt*(s: TSocketHandle, level, optname: cint, optval: pointer,
optlen: TSockLen): cint {.
stdcall, importc: "setsockopt", dynlib: ws2dll.}
proc listen*(s: TSocketHandle, backlog: cint): cint {.
stdcall, importc: "listen", dynlib: ws2dll.}
proc recv*(s: TSocketHandle, buf: pointer, len, flags: cint): cint {.
stdcall, importc: "recv", dynlib: ws2dll.}
proc recvfrom*(s: TSocketHandle, buf: cstring, len, flags: cint,
fromm: ptr TSockAddr, fromlen: ptr TSockLen): cint {.
stdcall, importc: "recvfrom", dynlib: ws2dll.}
proc select*(nfds: cint, readfds, writefds, exceptfds: ptr TFdSet,
timeout: ptr TTimeval): cint {.
stdcall, importc: "select", dynlib: ws2dll.}
proc send*(s: TSocketHandle, buf: pointer, len, flags: cint): cint {.
stdcall, importc: "send", dynlib: ws2dll.}
proc sendto*(s: TSocketHandle, buf: pointer, len, flags: cint,
to: ptr TSockAddr, tolen: TSockLen): cint {.
stdcall, importc: "sendto", dynlib: ws2dll.}
proc shutdown*(s: TSocketHandle, how: cint): cint {.
stdcall, importc: "shutdown", dynlib: ws2dll.}
proc getnameinfo*(a1: ptr TSockAddr, a2: TSockLen,
a3: cstring, a4: TSockLen, a5: cstring,
a6: TSockLen, a7: cint): cint {.
stdcall, importc: "getnameinfo", dynlib: ws2dll.}
proc inet_addr*(cp: cstring): int32 {.
stdcall, importc: "inet_addr", dynlib: ws2dll.}
proc WSAFDIsSet(s: TSocketHandle, FDSet: var TFdSet): bool {.
stdcall, importc: "__WSAFDIsSet", dynlib: ws2dll.}
proc FD_ISSET*(Socket: TSocketHandle, FDSet: var TFdSet): cint =
result = if WSAFDIsSet(Socket, FDSet): 1'i32 else: 0'i32
proc FD_SET*(Socket: TSocketHandle, FDSet: var TFdSet) =
if FDSet.fd_count < FD_SETSIZE:
FDSet.fd_array[int(FDSet.fd_count)] = Socket
inc(FDSet.fd_count)
proc FD_ZERO*(FDSet: var TFdSet) =
FDSet.fd_count = 0
proc wsaStartup*(wVersionRequired: int16, WSData: ptr TWSAData): cint {.
stdcall, importc: "WSAStartup", dynlib: ws2dll.}
proc getaddrinfo*(nodename, servname: cstring, hints: ptr TAddrInfo,
res: var ptr TAddrInfo): cint {.
stdcall, importc: "getaddrinfo", dynlib: ws2dll.}
proc freeaddrinfo*(ai: ptr TAddrInfo) {.
stdcall, importc: "freeaddrinfo", dynlib: ws2dll.}
proc inet_ntoa*(i: TInAddr): cstring {.
stdcall, importc, dynlib: ws2dll.}
const
MAXIMUM_WAIT_OBJECTS* = 0x00000040
type
TWOHandleArray* = array[0..MAXIMUM_WAIT_OBJECTS - 1, THandle]
PWOHandleArray* = ptr TWOHandleArray
proc waitForMultipleObjects*(nCount: DWORD, lpHandles: PWOHandleArray,
bWaitAll: WINBOOL, dwMilliseconds: DWORD): DWORD{.
stdcall, dynlib: "kernel32", importc: "WaitForMultipleObjects".}
# for memfiles.nim:
const
GENERIC_READ* = 0x80000000'i32
GENERIC_ALL* = 0x10000000'i32
FILE_SHARE_READ* = 1'i32
FILE_SHARE_DELETE* = 4'i32
FILE_SHARE_WRITE* = 2'i32
CREATE_ALWAYS* = 2'i32
OPEN_EXISTING* = 3'i32
FILE_BEGIN* = 0'i32
INVALID_SET_FILE_POINTER* = -1'i32
NO_ERROR* = 0'i32
PAGE_READONLY* = 2'i32
PAGE_READWRITE* = 4'i32
FILE_MAP_READ* = 4'i32
FILE_MAP_WRITE* = 2'i32
INVALID_FILE_SIZE* = -1'i32
FILE_FLAG_BACKUP_SEMANTICS* = 33554432'i32
# Error Constants
const
ERROR_ACCESS_DENIED* = 5
when useWinUnicode:
proc createFileW*(lpFileName: WideCString, dwDesiredAccess, dwShareMode: DWORD,
lpSecurityAttributes: pointer,
dwCreationDisposition, dwFlagsAndAttributes: DWORD,
hTemplateFile: THandle): THandle {.
stdcall, dynlib: "kernel32", importc: "CreateFileW".}
proc deleteFileW*(pathName: WideCString): int32 {.
importc: "DeleteFileW", dynlib: "kernel32", stdcall.}
else:
proc createFileA*(lpFileName: cstring, dwDesiredAccess, dwShareMode: DWORD,
lpSecurityAttributes: pointer,
dwCreationDisposition, dwFlagsAndAttributes: DWORD,
hTemplateFile: THANDLE): THANDLE {.
stdcall, dynlib: "kernel32", importc: "CreateFileA".}
proc deleteFileA*(pathName: cstring): int32 {.
importc: "DeleteFileA", dynlib: "kernel32", stdcall.}
proc setEndOfFile*(hFile: THandle): WINBOOL {.stdcall, dynlib: "kernel32",
importc: "SetEndOfFile".}
proc setFilePointer*(hFile: THandle, lDistanceToMove: LONG,
lpDistanceToMoveHigh: ptr LONG,
dwMoveMethod: DWORD): DWORD {.
stdcall, dynlib: "kernel32", importc: "SetFilePointer".}
proc getFileSize*(hFile: THandle, lpFileSizeHigh: ptr DWORD): DWORD{.stdcall,
dynlib: "kernel32", importc: "GetFileSize".}
proc mapViewOfFileEx*(hFileMappingObject: THandle, dwDesiredAccess: DWORD,
dwFileOffsetHigh, dwFileOffsetLow: DWORD,
dwNumberOfBytesToMap: DWORD,
lpBaseAddress: pointer): pointer{.
stdcall, dynlib: "kernel32", importc: "MapViewOfFileEx".}
proc createFileMappingW*(hFile: THandle,
lpFileMappingAttributes: pointer,
flProtect, dwMaximumSizeHigh: DWORD,
dwMaximumSizeLow: DWORD,
lpName: pointer): THandle {.
stdcall, dynlib: "kernel32", importc: "CreateFileMappingW".}
when not useWinUnicode:
proc createFileMappingA*(hFile: THANDLE,
lpFileMappingAttributes: pointer,
flProtect, dwMaximumSizeHigh: DWORD,
dwMaximumSizeLow: DWORD, lpName: cstring): THANDLE {.
stdcall, dynlib: "kernel32", importc: "CreateFileMappingA".}
proc unmapViewOfFile*(lpBaseAddress: pointer): WINBOOL {.stdcall,
dynlib: "kernel32", importc: "UnmapViewOfFile".}
type
TGUID* {.final, pure.} = object
D1*: int32
D2*: int16
D3*: int16
D4*: array [0..7, int8]
Overlapped* {.final, pure.} = object
Internal*: ptr LONG
InternalHigh*: ptr LONG
Offset*: DWORD
OffsetHigh*: DWORD
hEvent*: THandle
POverlapped* {.final, pure.} = ptr Overlapped
CompletionRoutine* {.final, pure.} = proc (
dwError, cbTransferred : DWORD,
lpOverlapped: POverlapped,
dwFlags: DWORD
)
ConnectExProc* {.final, pure.} = proc (
socket: TSocketHandle,
name: ptr TSockAddr,
namelen: cint,
lpSendBuffer: pointer, dwSendDataLength: DWORD,
lpdwBytesSent: ptr DWORD,
lpOverlapped: POverlapped
): bool {.stdcall.}
const
IOC_OUT = 0x40000000
IOC_IN = 0x80000000
IOC_WS2 = 0x08000000
IOC_INOUT = IOC_IN or IOC_OUT
template WSAIORW(x,y): expr = (IOC_INOUT or x or y)
const
SIO_GET_EXTENSION_FUNCTION_POINTER* = WSAIORW(IOC_WS2,6).DWORD
var
WSAID_CONNECTEX*: TGUID = TGUID(D1: 0x25a207b9, D2: 0xddf3'i16, D3: 0x4660, D4: [
0x8e'i8, 0xe9'i8, 0x76'i8, 0xe5'i8, 0x8c'i8, 0x74'i8,
0x06'i8, 0x3e'i8])
proc CreateIoCompletionPort*(FileHandle: THandle, ExistingCompletionPort: THandle,
CompletionKey: ptr LONG,
NumberOfConcurrentThreads: DWORD): THandle{.stdcall,
dynlib: "kernel32", importc: "CreateIoCompletionPort".}
proc GetQueuedCompletionStatus*(CompletionPort: THandle,
lpNumberOfBytesTransferred: ptr DWORD, lpCompletionKey: ptr(ptr LONG),
lpOverlapped: ptr POverlapped,
dwMilliseconds: DWORD): WINBOOL{.stdcall,
dynlib: "kernel32", importc: "GetQueuedCompletionStatus".}
proc WSAIoctl*(s: TSocketHandle, dwIoControlCode: DWORD, lpvInBuffer: pointer,
cbInBuffer: DWORD, lpvOutBuffer: pointer, cbOutBuffer: dword,
lpcbBytesReturned: ptr DWORD, lpOverlapped: POverlapped,
lpCompletionRoutine: CompletionRoutine): cint {.stdcall, importc: "WSAIoctl", dynlib: "Ws2_32.dll".}
proc getConnectEx*(s: TSocketHandle, guid: var TGUID): ConnectExProc =
# Ref: https://github.com/powdahound/twisted/blob/master/twisted/internet/iocpreactor/iocpsupport/winsock_pointers.c
var bytesRet: DWORD
var connectExPtr: pointer = nil
let state = WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, addr guid,
sizeof(TGUID).dword, addr connectExPtr, sizeof(pointer).DWORD,
addr bytesRet, nil, nil) == 0
if not state:
assert(false)
result = cast[ConnectExProc](connectExPtr)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment