diff --git a/Network/Socket/Enumerator.hs b/Network/Socket/Enumerator.hs
new file mode 100644
--- /dev/null
+++ b/Network/Socket/Enumerator.hs
@@ -0,0 +1,85 @@
+-----------------------------------------------------------------------------
+-- |
+-- Module: Network.Socket.Enumerator
+-- Copyright: 2010 John Millikin
+-- License: MIT
+--
+-- Maintainer: jmillikin@gmail.com
+-- Portability: portable
+--
+-----------------------------------------------------------------------------
+module Network.Socket.Enumerator
+	( enumSocket
+	, enumSocketFrom
+	, iterSocket
+	, iterSocketTo
+	) where
+import qualified Control.Exception as Exc
+import Control.Monad.IO.Class (MonadIO, liftIO)
+import qualified Data.ByteString as B
+import Data.Enumerator ((>>==))
+import qualified Data.Enumerator as E
+import qualified Network.Socket as S
+import Network.Socket.ByteString (sendMany, sendManyTo, recv, recvFrom)
+
+-- | Enumerate binary data from a 'S.Socket', using 'recv'. The socket
+-- must be connected.
+enumSocket :: MonadIO m
+           => Integer -- ^ Buffer size
+           -> S.Socket
+           -> E.Enumerator B.ByteString m b
+enumSocket bufferSize sock = loop where
+	intSize = fromInteger bufferSize
+	
+	loop (E.Continue k) = do
+		bytes <- try (recv sock intSize)
+		if B.null bytes
+			then E.continue k
+			else k (E.Chunks [bytes]) >>== loop
+	
+	loop step = E.returnI step
+
+-- | Enumerate binary data from a 'S.Socket', using 'recvFrom'. The socket
+-- does not have to be connected. Each chunk of data received will be paired
+-- with its address.
+enumSocketFrom :: MonadIO m
+               => Integer -- ^ Buffer size
+               -> S.Socket
+               -> E.Enumerator (B.ByteString, S.SockAddr) m b
+enumSocketFrom bufferSize sock = loop where
+	intSize = fromInteger bufferSize
+	
+	loop (E.Continue k) = do
+		(bytes, addr) <- try (recvFrom sock intSize)
+		if B.null bytes
+			then E.continue k
+			else k (E.Chunks [(bytes, addr)]) >>== loop
+	
+	loop step = E.returnI step
+
+-- | Write data to a 'S.Socket', using 'sendMany'. The socket must be connected.
+iterSocket :: MonadIO m
+           => S.Socket
+           -> E.Iteratee B.ByteString m ()
+iterSocket sock = E.continue step where
+	step E.EOF = E.yield () E.EOF
+	step (E.Chunks []) = E.continue step
+	step (E.Chunks xs) = try (sendMany sock xs)
+
+-- | Write data to a 'S.Socket', using 'sendManyTo'. The socket does not
+-- have to be connected.
+iterSocketTo :: MonadIO m
+           => S.Socket
+           -> S.SockAddr
+           -> E.Iteratee B.ByteString m ()
+iterSocketTo sock addr = E.continue step where
+	step E.EOF = E.yield () E.EOF
+	step (E.Chunks []) = E.continue step
+	step (E.Chunks xs) = try (sendManyTo sock xs addr)
+
+try :: MonadIO m => IO b -> E.Iteratee a m b
+try io = do
+	tried <- liftIO (Exc.try io)
+	case tried of
+		Left err -> E.throwError (err :: Exc.SomeException)
+		Right b -> return b
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/license.txt b/license.txt
new file mode 100644
--- /dev/null
+++ b/license.txt
@@ -0,0 +1,22 @@
+Copyright (c) 2010 John Millikin
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/network-enumerator.cabal b/network-enumerator.cabal
new file mode 100644
--- /dev/null
+++ b/network-enumerator.cabal
@@ -0,0 +1,41 @@
+name: network-enumerator
+version: 0.1
+synopsis: Enumerators for network sockets
+license: MIT
+license-file: license.txt
+author: John Millikin <jmillikin@gmail.com>
+maintainer: jmillikin@gmail.com
+copyright: Copyright (c) John Millikin 2010
+build-type: Simple
+cabal-version: >=1.6
+category: Data, Enumerator
+stability: experimental
+homepage: http://john-millikin.com/software/network-enumerator/
+bug-reports: mailto:jmillikin@gmail.com
+tested-with: GHC==6.12.1
+
+source-repository head
+  type: bazaar
+  location: http://john-millikin.com/software/network-enumerator/
+
+flag network-includes-bytestring
+
+library
+  ghc-options: -Wall
+
+  build-depends:
+      base >= 4 && < 5
+    , transformers >= 0.2 && < 0.3
+    , bytestring >= 0.9 && < 0.10
+    , enumerator >= 0.4 && < 0.5
+
+  if flag(network-includes-bytestring)
+    build-depends:
+        network >= 2.3 && < 2.4
+  else
+    build-depends:
+        network >= 2.2 && < 2.3
+      , network-bytestring >= 0.1.2 && < 0.2
+
+  exposed-modules:
+    Network.Socket.Enumerator
