summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--lib/pure/asyncio2.nim469
-rw-r--r--lib/pure/net.nim40
-rw-r--r--lib/pure/sockets2.nim202
-rw-r--r--lib/windows/winlean.nim77
4 files changed, 788 insertions, 0 deletions
diff --git a/lib/pure/asyncio2.nim b/lib/pure/asyncio2.nim
new file mode 100644
index 000000000..38fa93452
--- /dev/null
+++ b/lib/pure/asyncio2.nim
@@ -0,0 +1,469 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2014 Dominik Picheta
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+import os, oids, tables, strutils
+
+import winlean
+
+import sockets2, net
+
+## Asyncio2 
+## --------
+##
+## This module implements a brand new asyncio module based on Futures.
+## IOCP is used under the hood on Windows and the selectors module is used for
+## other operating systems.
+
+# -- Futures
+
+type
+  PFuture*[T] = ref object
+    value: T
+    finished: bool
+    error: ref EBase
+    cb: proc (future: PFuture[T]) {.closure.}
+
+proc newFuture*[T](): PFuture[T] =
+  ## Creates a new future.
+  new(result)
+  result.finished = false
+
+proc complete*[T](future: PFuture[T], val: T) =
+  ## Completes ``future`` with value ``val``.
+  assert(not future.finished)
+  assert(future.error == nil)
+  future.value = val
+  future.finished = true
+  if future.cb != nil:
+    future.cb(future)
+
+proc fail*[T](future: PFuture[T], error: ref EBase) =
+  ## Completes ``future`` with ``error``.
+  assert(not future.finished)
+  future.finished = true
+  future.error = error
+  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)
+
+proc read*[T](future: PFuture[T]): T =
+  ## Retrieves the value of ``future``. Future must be finished otherwise
+  ## this function will fail with a ``EInvalidValue`` exception.
+  ##
+  ## If the result of the future is an error then that error will be raised.
+  if future.finished:
+    if future.error != nil: raise future.error
+    return future.value
+  else:
+    # TODO: Make a custom exception type for this?
+    raise newException(EInvalidValue, "Future still in progress.")
+
+proc finished*[T](future: PFuture[T]): bool =
+  ## Determines whether ``future`` has completed.
+  ##
+  ## ``True`` may indicate an error or a value. Use ``hasError`` to distinguish.
+  future.finished
+
+proc failed*[T](future: PFuture[T]): bool =
+  ## Determines whether ``future`` completed with an error.
+  future.error != nil
+
+when defined(windows):
+  type
+    TCompletionKey = dword
+
+    TCompletionData* = object
+      sock: TSocketHandle
+      cb: proc (sock: TSocketHandle, errcode: TOSErrorCode) {.closure.}
+
+    PDispatcher* = ref object
+      ioPort: THandle
+
+    TCustomOverlapped = object
+      Internal*: DWORD
+      InternalHigh*: DWORD
+      Offset*: DWORD
+      OffsetHigh*: DWORD
+      hEvent*: THANDLE
+      data*: TCompletionData
+
+    PCustomOverlapped = ptr TCustomOverlapped
+
+  proc newDispatcher*(): PDispatcher =
+    ## Creates a new Dispatcher instance.
+    new result
+    result.ioPort = CreateIOCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1)
+
+  proc register*(p: PDispatcher, sock: TSocketHandle) =
+    ## Registers ``sock`` with the dispatcher ``p``.
+    if CreateIOCompletionPort(sock.THandle, p.ioPort,
+                              cast[TCompletionKey](sock), 1) == 0:
+      OSError(OSLastError())
+
+  proc poll*(p: PDispatcher, timeout = 500) =
+    ## Waits for completion events and processes them.
+    let llTimeout =
+      if timeout ==  -1: winlean.INFINITE
+      else: timeout.int32
+    var lpNumberOfBytesTransferred: DWORD
+    var lpCompletionKey: ULONG
+    var lpOverlapped: POverlapped
+    let res = GetQueuedCompletionStatus(p.ioPort, addr lpNumberOfBytesTransferred,
+        addr lpCompletionKey, addr lpOverlapped, llTimeout).bool
+
+    # http://stackoverflow.com/a/12277264/492186
+    # TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html
+    var customOverlapped = cast[PCustomOverlapped](lpOverlapped)
+    if res:
+      assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle
+
+      customOverlapped.data.cb(customOverlapped.data.sock, TOSErrorCode(-1))
+      dealloc(customOverlapped)
+    else:
+      let errCode = OSLastError()
+      if lpOverlapped != nil:
+        assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle
+        dealloc(customOverlapped)
+        customOverlapped.data.cb(customOverlapped.data.sock, errCode)
+      else:
+        if errCode.int32 == WAIT_TIMEOUT:
+          # Timed out
+          discard
+        else: OSError(errCode)
+
+  var connectExPtr: pointer = nil
+  var acceptExPtr: pointer = nil
+  var getAcceptExSockAddrsPtr: 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() =
+    let dummySock = socket()
+    if not initPointer(dummySock, connectExPtr, WSAID_CONNECTEX):
+      OSError(OSLastError())
+    if not initPointer(dummySock, acceptExPtr, WSAID_ACCEPTEX):
+      OSError(OSLastError())
+    if not initPointer(dummySock, getAcceptExSockAddrsPtr, WSAID_GETACCEPTEXSOCKADDRS):
+      OSError(OSLastError())
+
+  proc connectEx(s: TSocketHandle, name: ptr TSockAddr, namelen: cint, 
+                  lpSendBuffer: pointer, dwSendDataLength: dword,
+                  lpdwBytesSent: PDWORD, lpOverlapped: POverlapped): bool =
+    if connectExPtr.isNil: raise newException(EInvalidValue, "Need to initialise ConnectEx().")
+    let func =
+      cast[proc (s: TSocketHandle, name: ptr TSockAddr, namelen: cint, 
+         lpSendBuffer: pointer, dwSendDataLength: dword,
+         lpdwBytesSent: PDWORD, lpOverlapped: POverlapped): bool {.stdcall.}](connectExPtr)
+
+    result = func(s, name, namelen, lpSendBuffer, dwSendDataLength, lpdwBytesSent,
+         lpOverlapped)
+
+  proc acceptEx(listenSock, acceptSock: TSocketHandle, lpOutputBuffer: pointer,
+                 dwReceiveDataLength, dwLocalAddressLength,
+                 dwRemoteAddressLength: DWORD, lpdwBytesReceived: PDWORD,
+                 lpOverlapped: POverlapped): bool =
+    if acceptExPtr.isNil: raise newException(EInvalidValue, "Need to initialise AcceptEx().")
+    let func =
+      cast[proc (listenSock, acceptSock: TSocketHandle, lpOutputBuffer: pointer,
+                 dwReceiveDataLength, dwLocalAddressLength,
+                 dwRemoteAddressLength: DWORD, lpdwBytesReceived: PDWORD,
+                 lpOverlapped: POverlapped): bool {.stdcall.}](acceptExPtr)
+    result = func(listenSock, acceptSock, lpOutputBuffer, dwReceiveDataLength,
+        dwLocalAddressLength, dwRemoteAddressLength, lpdwBytesReceived,
+        lpOverlapped)
+
+  proc getAcceptExSockaddrs(lpOutputBuffer: pointer,
+      dwReceiveDataLength, dwLocalAddressLength, dwRemoteAddressLength: DWORD,
+      LocalSockaddr: ptr ptr TSockAddr, LocalSockaddrLength: lpint,
+      RemoteSockaddr: ptr ptr TSockAddr, RemoteSockaddrLength: lpint) =
+    if getAcceptExSockAddrsPtr.isNil:
+      raise newException(EInvalidValue, "Need to initialise getAcceptExSockAddrs().")
+
+    let func =
+      cast[proc (lpOutputBuffer: pointer,
+                 dwReceiveDataLength, dwLocalAddressLength,
+                 dwRemoteAddressLength: DWORD, LocalSockaddr: ptr ptr TSockAddr,
+                 LocalSockaddrLength: lpint, RemoteSockaddr: ptr ptr TSockAddr,
+                RemoteSockaddrLength: lpint) {.stdcall.}](getAcceptExSockAddrsPtr)
+    
+    func(lpOutputBuffer, dwReceiveDataLength, dwLocalAddressLength,
+                  dwRemoteAddressLength, LocalSockaddr, LocalSockaddrLength,
+                  RemoteSockaddr, RemoteSockaddrLength)
+
+  proc connect*(p: PDispatcher, socket: 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.
+
+    var retFuture = newFuture[int]()# TODO: Change to void when that regression is fixed.
+    # Apparently ``ConnectEx`` expects the socket to be initially bound:
+    var saddr: Tsockaddr_in
+    saddr.sin_family = int16(toInt(af))
+    saddr.sin_port = 0
+    saddr.sin_addr.s_addr = INADDR_ANY
+    if bindAddr(socket, cast[ptr TSockAddr](addr(saddr)),
+                  sizeof(saddr).TSockLen) < 0'i32:
+      OSError(OSLastError())
+
+    var aiList = getAddrInfo(address, port, af)
+    var success = false
+    var lastError: TOSErrorCode
+    var it = aiList
+    while it != nil:
+      # "the OVERLAPPED structure must remain valid until the I/O completes"
+      # http://blogs.msdn.com/b/oldnewthing/archive/2011/02/02/10123392.aspx
+      var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
+      ol.data = TCompletionData(sock: socket, cb:
+        proc (sock: TSocketHandle, errcode: TOSErrorCode) =
+          if errcode == TOSErrorCode(-1):
+            retFuture.complete(0)
+          else:
+            retFuture.fail(newException(EOS, osErrorMsg(errcode)))
+      )
+      
+      var ret = connectEx(socket, it.ai_addr, sizeof(TSockAddrIn).cint,
+                          nil, 0, nil, cast[POverlapped](ol))
+      if ret:
+        # Request to connect completed immediately.
+        success = true
+        retFuture.complete(0)
+        dealloc(ol)
+        break
+      else:
+        lastError = OSLastError()
+        if lastError.int32 == ERROR_IO_PENDING:
+          # In this case ``ol`` will be deallocated in ``poll``.
+          success = true
+          break
+        else:
+          dealloc(ol)
+          success = false
+      it = it.ai_next
+
+    dealloc(aiList)
+    if not success:
+      retFuture.fail(newException(EOS, osErrorMsg(lastError)))
+    return retFuture
+
+  proc recv*(p: PDispatcher, socket: TSocketHandle, size: int): PFuture[string] =
+    ## Reads ``size`` bytes from ``socket``. Returned future will complete once
+    ## all of the requested data is read.
+
+    var retFuture = newFuture[string]()
+    
+    var dataBuf: TWSABuf
+    dataBuf.buf = newString(size)
+    dataBuf.len = size
+    
+    var bytesReceived, flags: DWord
+    var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
+    ol.data = TCompletionData(sock: socket, cb:
+      proc (sock: TSocketHandle, errcode: TOSErrorCode) =
+        if errcode == TOSErrorCode(-1):
+          var data = newString(size)
+          copyMem(addr data[0], addr dataBuf.buf[0], size)
+          retFuture.complete($data)
+        else:
+          retFuture.fail(newException(EOS, osErrorMsg(errcode)))
+    )
+    
+    let ret = WSARecv(socket, addr dataBuf, 1, addr bytesReceived,
+                      addr flags, cast[POverlapped](ol), nil)
+    if ret == -1:
+      let err = OSLastError()
+      if err.int32 != ERROR_IO_PENDING:
+        retFuture.fail(newException(EOS, osErrorMsg(err)))
+        dealloc(ol)
+    else:
+      # Request to read completed immediately.
+      var data = newString(size)
+      copyMem(addr data[0], addr dataBuf.buf[0], size)
+      retFuture.complete($data)
+      dealloc(ol)
+    return retFuture
+
+  proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[int] =
+    ## Sends ``data`` to ``socket``. The returned future will complete once all
+    ## data has been sent.
+    var retFuture = newFuture[int]()
+
+    var dataBuf: TWSABuf
+    dataBuf.buf = data
+    dataBuf.len = data.len
+
+    var bytesReceived, flags: DWord
+    var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
+    ol.data = TCompletionData(sock: socket, cb:
+      proc (sock: TSocketHandle, errcode: TOSErrorCode) =
+        if errcode == TOSErrorCode(-1):
+          retFuture.complete(0)
+        else:
+          retFuture.fail(newException(EOS, osErrorMsg(errcode)))
+    )
+
+    let ret = WSASend(socket, addr dataBuf, 1, addr bytesReceived,
+                      flags, cast[POverlapped](ol), nil)
+    if ret == -1:
+      let err = osLastError()
+      if err.int32 != ERROR_IO_PENDING:
+        retFuture.fail(newException(EOS, osErrorMsg(err)))
+        dealloc(ol)
+    else:
+      retFuture.complete(0)
+      dealloc(ol)
+    return retFuture
+
+  proc acceptAddr*(p: PDispatcher, socket: TSocketHandle): 
+      PFuture[tuple[address: string, client: TSocketHandle]] =
+    ## Accepts a new connection. Returns a future containing the client socket
+    ## corresponding to that connection and the remote address of the client.
+    ## The future will complete when the connection is successfully accepted.
+    
+    var retFuture = newFuture[tuple[address: string, client: TSocketHandle]]()
+
+    var clientSock = socket()
+    if clientSock == OSInvalidSocket: osError(osLastError())
+
+    const lpOutputLen = 1024
+    var lpOutputBuf = newString(lpOutputLen)
+    var dwBytesReceived: DWORD
+    let dwReceiveDataLength = 0.DWORD # We don't want any data to be read.
+    let dwLocalAddressLength = DWORD(sizeof (TSockaddr_in) + 16)
+    let dwRemoteAddressLength = DWORD(sizeof(TSockaddr_in) + 16)
+
+    template completeAccept(): stmt {.immediate, dirty.} =
+      var listenSock = socket
+      let setoptRet = setsockopt(clientSock, SOL_SOCKET,
+          SO_UPDATE_ACCEPT_CONTEXT, addr listenSock,
+          sizeof(listenSock).TSockLen)
+      if setoptRet != 0: osError(osLastError())
+
+      var LocalSockaddr, RemoteSockaddr: ptr TSockAddr
+      var localLen, remoteLen: int32
+      getAcceptExSockaddrs(addr lpOutputBuf[0], dwReceiveDataLength,
+                           dwLocalAddressLength, dwRemoteAddressLength,
+                           addr LocalSockaddr, addr localLen,
+                           addr RemoteSockaddr, addr remoteLen)
+      # TODO: IPv6. Check ``sa_family``. http://stackoverflow.com/a/9212542/492186
+      retFuture.complete(
+        (address: $inet_ntoa(cast[ptr Tsockaddr_in](remoteSockAddr).sin_addr),
+         client: clientSock)
+      )
+
+    var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
+    ol.data = TCompletionData(sock: socket, cb:
+      proc (sock: TSocketHandle, errcode: TOSErrorCode) =
+        if errcode == TOSErrorCode(-1):
+          completeAccept()
+        else:
+          retFuture.fail(newException(EOS, osErrorMsg(errcode)))
+    )
+
+    # http://msdn.microsoft.com/en-us/library/windows/desktop/ms737524%28v=vs.85%29.aspx
+    let ret = acceptEx(socket, clientSock, addr lpOutputBuf[0],
+                       dwReceiveDataLength, 
+                       dwLocalAddressLength,
+                       dwRemoteAddressLength,
+                       addr dwBytesReceived, cast[POverlapped](ol))
+
+    if not ret:
+      let err = osLastError()
+      if err.int32 != ERROR_IO_PENDING:
+        retFuture.fail(newException(EOS, osErrorMsg(err)))
+        dealloc(ol)
+    else:
+      completeAccept()
+      dealloc(ol)
+
+    return retFuture
+
+  proc accept*(p: PDispatcher, socket: TSocketHandle): PFuture[TSocketHandle] =
+    ## Accepts a new connection. Returns a future containing the client socket
+    ## corresponding to that connection.
+    ## The future will complete when the connection is successfully accepted.
+    var retFut = newFuture[TSocketHandle]()
+    var fut = p.acceptAddr(socket)
+    fut.callback =
+      proc (future: PFuture[tuple[address: string, client: TSocketHandle]]) =
+        assert future.finished
+        if future.failed:
+          retFut.fail(future.error)
+        else:
+          retFut.complete(future.read.client)
+    return retFut
+
+  initAll()
+else:
+  # TODO: Selectors.
+
+
+when isMainModule:
+  
+  var p = newDispatcher()
+  var sock = socket()
+  #sock.setBlocking false
+  p.register(sock)
+
+  when true:
+
+    var f = p.connect(sock, "irc.freenode.org", TPort(6667))
+    f.callback =
+      proc (future: PFuture[int]) =
+        echo("Connected in future!")
+        echo(future.read)
+        for i in 0 .. 50:
+          var recvF = p.recv(sock, 10)
+          recvF.callback =
+            proc (future: PFuture[string]) =
+              echo("Read: ", future.read)
+
+  else:
+
+    sock.bindAddr(TPort(6667))
+    sock.listen()
+    proc onAccept(future: PFuture[TSocketHandle]) =
+      echo "Accepted"
+      var t = p.send(future.read, "test\c\L")
+      t.callback =
+        proc (future: PFuture[int]) =
+          echo(future.read)
+      
+      var f = p.accept(sock)
+      f.callback = onAccept
+      
+    var f = p.accept(sock)
+    f.callback = onAccept
+  
+  while true:
+    p.poll()
+    echo "polled"
+
+
+
+
+
+  
+
+  
\ No newline at end of file
diff --git a/lib/pure/net.nim b/lib/pure/net.nim
new file mode 100644
index 000000000..bdcae677e
--- /dev/null
+++ b/lib/pure/net.nim
@@ -0,0 +1,40 @@
+#
+#
+#            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 high-level cross-platform sockets interface.
+
+import sockets2, os
+
+type
+  TSocket* = TSocketHandle
+
+proc bindAddr*(socket: TSocket, port = TPort(0), address = "") {.
+  tags: [FReadIO].} =
+
+  ## binds an address/port number to a socket.
+  ## Use address string in dotted decimal form like "a.b.c.d"
+  ## or leave "" for any address.
+
+  if address == "":
+    var name: TSockaddr_in
+    when defined(windows):
+      name.sin_family = toInt(AF_INET).int16
+    else:
+      name.sin_family = toInt(AF_INET)
+    name.sin_port = htons(int16(port))
+    name.sin_addr.s_addr = htonl(INADDR_ANY)
+    if bindAddr(socket, cast[ptr TSockAddr](addr(name)),
+                  sizeof(name).TSocklen) < 0'i32:
+      osError(osLastError())
+  else:
+    var aiList = getAddrInfo(address, port, AF_INET)
+    if bindAddr(socket, aiList.ai_addr, aiList.ai_addrlen.TSocklen) < 0'i32:
+      dealloc(aiList)
+      osError(osLastError())
+    dealloc(aiList)
\ No newline at end of file
diff --git a/lib/pure/sockets2.nim b/lib/pure/sockets2.nim
new file mode 100644
index 000000000..22624bbad
--- /dev/null
+++ b/lib/pure/sockets2.nim
@@ -0,0 +1,202 @@
+#
+#
+#            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
+
+export TSocketHandle, TSockaddr_in, TAddrinfo, INADDR_ANY, TSockAddr, TSockLen,
+  inet_ntoa
+
+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 bindAddr*(socket: TSocketHandle, name: ptr TSockAddr, namelen: TSockLen): cint =
+  result = bindSocket(socket, name, namelen)
+
+proc listen*(socket: TSocketHandle, backlog = SOMAXCONN) {.tags: [FReadIO].} =
+  ## Marks ``socket`` as accepting connections. 
+  ## ``Backlog`` specifies the maximum length of the 
+  ## queue of pending connections.
+  when defined(windows):
+    if winlean.listen(socket, cint(backlog)) < 0'i32: osError(osLastError())
+  else:
+    if posix.listen(socket, cint(backlog)) < 0'i32: osError(osLastError())
+
+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``!
+  var hints: TAddrInfo
+  result = nil
+  hints.ai_family = toInt(af)
+  hints.ai_socktype = toInt(typ)
+  hints.ai_protocol = toInt(prot)
+  var gaiResult = getAddrInfo(address, $port, addr(hints), result)
+  if gaiResult != 0'i32:
+    when defined(windows):
+      OSError(OSLastError())
+    else:
+      raise newException(EOS, $gai_strerror(gaiResult))
+
+proc dealloc*(ai: ptr TAddrInfo) =
+  freeaddrinfo(ai)
+
+proc ntohl*(x: int32): int32 = 
+  ## Converts 32-bit integers from network to host byte order.
+  ## On machines where the host byte order is the same as network byte order,
+  ## this is a no-op; otherwise, it performs a 4-byte swap operation.
+  when cpuEndian == bigEndian: result = x
+  else: result = (x shr 24'i32) or
+                 (x shr 8'i32 and 0xff00'i32) or
+                 (x shl 8'i32 and 0xff0000'i32) or
+                 (x shl 24'i32)
+
+proc ntohs*(x: int16): int16 =
+  ## Converts 16-bit integers from network to host byte order. On machines
+  ## where the host byte order is the same as network byte order, this is
+  ## a no-op; otherwise, it performs a 2-byte swap operation.
+  when cpuEndian == bigEndian: result = x
+  else: result = (x shr 8'i16) or (x shl 8'i16)
+
+proc htonl*(x: int32): int32 =
+  ## Converts 32-bit integers from host to network byte order. On machines
+  ## where the host byte order is the same as network byte order, this is
+  ## a no-op; otherwise, it performs a 4-byte swap operation.
+  result = sockets2.ntohl(x)
+
+proc htons*(x: int16): int16 =
+  ## Converts 16-bit positive integers from host to network byte order.
+  ## On machines where the host byte order is the same as network byte
+  ## order, this is a no-op; otherwise, it performs a 2-byte swap operation.
+  result = sockets2.ntohs(x)
+
+when defined(Windows):
+  var wsa: TWSADATA
+  if WSAStartup(0x0101'i16, addr wsa) != 0: OSError(OSLastError())
\ No newline at end of file
diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim
index 5b641185e..7713d503a 100644
--- a/lib/windows/winlean.nim
+++ b/lib/windows/winlean.nim
@@ -16,8 +16,12 @@ const
 type
   THandle* = int
   LONG* = int32
+  ULONG* = int
+  PULONG* = ptr int
   WINBOOL* = int32
   DWORD* = int32
+  PDWORD* = ptr DWORD
+  LPINT* = ptr int32
   HDC* = THandle
   HGLRC* = THandle
 
@@ -632,3 +636,76 @@ when not useWinUnicode:
 proc unmapViewOfFile*(lpBaseAddress: pointer): WINBOOL {.stdcall,
     dynlib: "kernel32", importc: "UnmapViewOfFile".}
 
+type
+  TOVERLAPPED* {.final, pure.} = object
+    Internal*: DWORD
+    InternalHigh*: DWORD
+    Offset*: DWORD
+    OffsetHigh*: DWORD
+    hEvent*: THANDLE
+
+  POVERLAPPED* = ptr TOVERLAPPED
+
+  POVERLAPPED_COMPLETION_ROUTINE* = proc (para1: DWORD, para2: DWORD,
+      para3: POVERLAPPED){.stdcall.}
+
+  TGUID* {.final, pure.} = object
+    D1*: int32
+    D2*: int16
+    D3*: int16
+    D4*: array [0..7, int8]
+
+const
+  ERROR_IO_PENDING* = 997
+
+proc CreateIoCompletionPort*(FileHandle: THANDLE, ExistingCompletionPort: THANDLE,
+                             CompletionKey: DWORD,
+                             NumberOfConcurrentThreads: DWORD): THANDLE{.stdcall,
+    dynlib: "kernel32", importc: "CreateIoCompletionPort".}
+
+proc GetQueuedCompletionStatus*(CompletionPort: THandle,
+    lpNumberOfBytesTransferred: PDWORD, lpCompletionKey: PULONG,
+                                lpOverlapped: ptr POverlapped,
+                                dwMilliseconds: DWORD): WINBOOL{.stdcall,
+    dynlib: "kernel32", importc: "GetQueuedCompletionStatus".}
+
+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
+  SO_UPDATE_ACCEPT_CONTEXT* = 0x700B
+
+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])
+  WSAID_ACCEPTEX*: TGUID = TGUID(D1: 0xb5367df1'i32, D2: 0xcbac'i16, D3: 0x11cf, D4: [
+    0x95'i8, 0xca'i8, 0x00'i8, 0x80'i8, 0x5f'i8, 0x48'i8, 0xa1'i8, 0x92'i8])
+  WSAID_GETACCEPTEXSOCKADDRS*: TGUID = TGUID(D1: 0xb5367df2'i32, D2: 0xcbac'i16, D3: 0x11cf, D4: [
+    0x95'i8, 0xca'i8, 0x00'i8, 0x80'i8, 0x5f'i8, 0x48'i8, 0xa1'i8, 0x92'i8])
+
+proc WSAIoctl*(s: TSocketHandle, dwIoControlCode: dword, lpvInBuffer: pointer,
+  cbInBuffer: dword, lpvOutBuffer: pointer, cbOutBuffer: dword,
+  lpcbBytesReturned: PDword, lpOverlapped: POVERLAPPED,
+  lpCompletionRoutine: POVERLAPPED_COMPLETION_ROUTINE): cint 
+  {.stdcall, importc: "WSAIoctl", dynlib: "Ws2_32.dll".}
+
+type
+  TWSABuf* {.importc: "WSABUF", header: "winsock2.h".} = object
+    len*: ULONG
+    buf*: cstring
+
+proc WSARecv*(s: TSocketHandle, buf: ptr TWSABuf, bufCount: DWORD,
+  bytesReceived, flags: PDWORD, lpOverlapped: POverlapped,
+  completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {.
+  stdcall, importc: "WSARecv", dynlib: "Ws2_32.dll".}
+
+proc WSASend*(s: TSocketHandle, buf: ptr TWSABuf, bufCount: DWORD,
+  bytesSent: PDWord, flags: DWORD, lpOverlapped: POverlapped,
+  completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {.
+  stdcall, importc: "WSASend", dynlib: "Ws2_32.dll".}