Project import
diff --git a/CFSockets/CFSocket.h b/CFSockets/CFSocket.h
new file mode 100755
index 0000000..910f596
--- /dev/null
+++ b/CFSockets/CFSocket.h
@@ -0,0 +1,181 @@
+// CFSockets CFSocket.h
+//
+// Copyright © 2009–2013, Roy Ratcliffe, Pioneering Software, United Kingdom
+//
+// 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, EITHER
+// EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 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.
+//
+//------------------------------------------------------------------------------
+
+#import <Foundation/Foundation.h>
+
+@class CFSocket;
+@class CFStreamPair;
+
+@protocol CFSocketDelegate<NSObject>
+@optional
+
+- (void)socket:(CFSocket *)socket acceptNativeHandle:(NSSocketNativeHandle)nativeHandle;
+- (void)socket:(CFSocket *)socket acceptStreamPair:(CFStreamPair *)streamPair;
+
+@end
+
+/**
+ * Wraps Core Foundation's sockets in Objective-C clothing.
+ *
+ * Handles ARC memory management issues for a socket and implements a
+ * delegation protocol. As an object, you can sub-class the socket. It papers
+ * over the toll-free bridging requirements necessary for interactions between
+ * Core Foundation and Foundation. Start by initialising a socket, either an
+ * explicit IPv6 or IPv4 TCP socket, or whichever version of TCP socket the
+ * system makes available to you. Note that IPv4 clients can also access IPv6
+ * sockets.
+ *
+ * Note, you can have an object class called CFSocket; it does not clash with
+ * Apple's Core Foundation C-based socket functions, externals and constants
+ * because those exist in the C name space, while CFSocket here exists in the
+ * Objective-C name space. They do not collide.
+ */
+@interface CFSocket : NSObject
+{
+	CFSocketRef _socket;
+	CFRunLoopSourceRef _runLoopSource;
+}
+
+@property(weak, NS_NONATOMIC_IOSONLY) id<CFSocketDelegate> delegate;
+
+/**
+ * Designated initialiser.
+ *
+ * Initialisers also create the underlying Core Foundation socket. You
+ * cannot have a partially initialised Objective-C socket. When socket creation
+ * fails, initialisation fails also. All socket initialisers follow this
+ * pattern. Hence, you cannot initialise a socket with a `NULL` socket
+ * reference. In such cases, the initialiser answers `nil`.
+ *
+ * This approach creates a slight quandary. Creating a Core Foundation socket
+ * requires a socket context. The context needs to retain a bridging reference
+ * to `self`, the Objective-C object encapsulating the socket. Otherwise, the
+ * socket call-back function cannot springboard from C to Objective-C when
+ * call-backs trigger. When the initialiser returns successfully however, the
+ * answer overwrites `self`. What if `self` changes? If it changes to `nil`,
+ * no problem. But what if it changes to some other pointer address?
+ *
+ * TODO: Add more initialisers; specifically, socket signature initialisers.
+ *
+ * @param socket Reference to a CFSocket object.
+ */
+- (id)initWithSocketRef:(CFSocketRef)socket;
+
+- (id)initWithProtocolFamily:(int)family socketType:(int)type protocol:(int)protocol;
+- (id)initForTCPv6;
+- (id)initForTCPv4;
+
+/**
+ * Instantiates and creates a TCP socket using Internet Protocol version
+ * 6 if available, else tries version 4.
+ *
+ * Starts by creating a socket for Internet Protocol version 6. This
+ * also supports IPv4 clients via IPv4-mapped IPv6 addresses. Falls back to IPv4
+ * only when IPv6 fails.
+ *
+ * @result Answers a TCP socket, version 6 or 4; returns `nil` if the socket
+ * fails to create.
+ */
+- (id)initForTCP;
+
+/**
+ * Initialises using a native socket handle.
+ * @param nativeHandle Platform-specific native socket handle.
+ */
+- (id)initWithNativeHandle:(NSSocketNativeHandle)nativeHandle;
+
+/**
+ * Binds an address to a socket.
+ *
+ * Despite the innocuous-sounding method name, this method irreversibly
+ * binds the socket; assuming success. Do this early when constructing a
+ * socket. Be aware that accessing the address also binds the socket, if not
+ * already bound. You cannot therefore subsequently bind it. If you want to bind
+ * to a specific port, do so by setting the socket address _before_ asking for
+ * the address; that is, before using the getter method.
+ */
+- (BOOL)setAddress:(NSData *)addressData error:(NSError **)outError;
+
+- (BOOL)connectToAddress:(NSData *)addressData timeout:(NSTimeInterval)timeout error:(NSError **)outError;
+
+- (void)invalidate;
+- (BOOL)isValid;
+- (NSData *)address;
+- (NSData *)peerAddress;
+- (NSSocketNativeHandle)nativeHandle;
+- (BOOL)setReuseAddressOption:(BOOL)flag;
+
+/**
+ * Answers the socket address family.
+ *
+ * For Internet-based sockets, answers either `AF_INET6` or
+ * `AF_INET`. The latter for IP version 4 addresses. Note, protocol and address
+ * family are one and the same for Internet addresses. Protocol families are
+ * defined in terms of their address family; the `PF_` equals its corresponding
+ * `AF_` manifest constant.
+ *
+ * You can use this for polymorphic behaviour. If behaviour depends on a
+ * particular kind of socket, you can ask this method for the underlying address
+ * family and respond accordingly. This method differs from -address which binds
+ * the address first. The implementation here obtains the address family
+ * non-destructively; socket state remains unchanged.
+ *
+ * @result Answers the socket address family, or `AF_MAX` when an error
+ * occurs. On error, standard library `errno` value indicates the problem.
+ */
+- (int)addressFamily;
+
+/**
+ * Answers the port number.
+ *
+ * The answer depends on the socket's address family: version 4 or
+ * 6. Handles the network-to-host byte ordering. The resulting integer has
+ * correct ordering for the host machine.
+ */
+- (int)port;
+
+- (void)addToCurrentRunLoopForCommonModes;
+- (void)removeFromCurrentRunLoopForCommonModes;
+- (void)disableAcceptCallBack;
+- (void)enableAcceptCallBack;
+
+/**
+ * Handles the socket _accept_ call-back behaviour.
+ *
+ * Executes when Next Step meets Core Foundation. The argument specifies a
+ * native socket handle, an integer defining the Unix socket descriptor.
+ *
+ * Exists to allow for optional overriding. You do not need to deploy
+ * the delegate protocol if your sub-class handles "accept native handle" events
+ * directly; though delegation usually works best.
+ *
+ * @param nativeHandle Platform-specific native socket handle.
+ */
+- (void)acceptNativeHandle:(NSSocketNativeHandle)nativeHandle;
+
+@end
+
+extern NSString *const CFSocketErrorDomain;
+
+void __CFSocketCallOut(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info);
diff --git a/CFSockets/CFSocket.m b/CFSockets/CFSocket.m
new file mode 100755
index 0000000..4638696
--- /dev/null
+++ b/CFSockets/CFSocket.m
@@ -0,0 +1,270 @@
+// CFSockets CFSocket.m
+//
+// Copyright © 2009–2013, Roy Ratcliffe, Pioneering Software, United Kingdom
+//
+// 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, EITHER
+// EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 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.
+//
+//------------------------------------------------------------------------------
+
+#import "CFSocket.h"
+#import "CFStreamPair.h"
+
+// for setsockopt(2)
+#import <sys/socket.h>
+
+// for IPPROTO_TCP
+#import <netinet/in.h>
+
+@implementation CFSocket
+
+@synthesize delegate = _delegate;
+
+- (id)init
+{
+	return (self = nil);
+}
+
+// designated initialiser
+- (id)initWithSocketRef:(CFSocketRef)socket
+{
+	if ((self = [super init]))
+	{
+		if (socket)
+		{
+			_socket = socket;
+		}
+		else
+		{
+			self = nil;
+		}
+	}
+	return self;
+}
+
+- (id)initWithProtocolFamily:(int)family socketType:(int)type protocol:(int)protocol
+{
+	CFSocketContext context = { .info = (__bridge void *)self };
+	return [self initWithSocketRef:CFSocketCreate(kCFAllocatorDefault, family, type, protocol, kCFSocketAcceptCallBack, __CFSocketCallOut, &context)];
+}
+
+- (id)initForTCPv6
+{
+	return [self initWithProtocolFamily:PF_INET6 socketType:SOCK_STREAM protocol:IPPROTO_TCP];
+}
+
+- (id)initForTCPv4
+{
+	return [self initWithProtocolFamily:PF_INET socketType:SOCK_STREAM protocol:IPPROTO_TCP];
+}
+
+- (id)initForTCP
+{
+	self = [self initForTCPv6];
+	if (self == nil) self = [self initForTCPv4];
+	return self;
+}
+
+- (id)initWithNativeHandle:(NSSocketNativeHandle)nativeHandle
+{
+	CFSocketContext context = { .info = (__bridge void *)self };
+	return [self initWithSocketRef:CFSocketCreateWithNative(kCFAllocatorDefault, nativeHandle, kCFSocketAcceptCallBack, __CFSocketCallOut, &context)];
+}
+
+- (BOOL)setAddress:(NSData *)addressData error:(NSError **)outError
+{
+	CFSocketError error = CFSocketSetAddress(_socket, (__bridge CFDataRef)addressData);
+	BOOL success = (error == kCFSocketSuccess);
+	if (!success)
+	{
+		if (outError && *outError == nil)
+		{
+			*outError = [NSError errorWithDomain:CFSocketErrorDomain code:error userInfo:nil];
+		}
+	}
+	return success;
+}
+
+- (BOOL)connectToAddress:(NSData *)addressData timeout:(NSTimeInterval)timeout error:(NSError **)outError
+{
+	CFSocketError error = CFSocketConnectToAddress(_socket, (__bridge CFDataRef)addressData, timeout);
+	BOOL success = (error == kCFSocketSuccess);
+	if (!success)
+	{
+		if (outError && *outError == nil)
+		{
+			*outError = [NSError errorWithDomain:CFSocketErrorDomain code:error userInfo:nil];
+		}
+	}
+	return success;
+}
+
+- (void)invalidate
+{
+	// Never close the underlying native socket without first invalidating.
+	CFSocketInvalidate(_socket);
+}
+
+- (BOOL)isValid
+{
+	return CFSocketIsValid(_socket) != false;
+}
+
+- (NSData *)address
+{
+	return CFBridgingRelease(CFSocketCopyAddress(_socket));
+}
+
+- (NSData *)peerAddress
+{
+	return CFBridgingRelease(CFSocketCopyPeerAddress(_socket));
+}
+
+- (NSSocketNativeHandle)nativeHandle
+{
+	return CFSocketGetNative(_socket);
+}
+
+- (BOOL)setReuseAddressOption:(BOOL)flag
+{
+	int option = (flag == NO) ? 0 : 1;
+	return 0 == setsockopt([self nativeHandle], SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof(option));
+}
+
+- (int)addressFamily
+{
+	uint8_t sockaddr[SOCK_MAXADDRLEN];
+	socklen_t len = sizeof(sockaddr);
+	return 0 == getsockname([self nativeHandle], (struct sockaddr *)sockaddr, &len) && len >= offsetof(struct sockaddr, sa_data) ? ((struct sockaddr *)sockaddr)->sa_family : AF_MAX;
+}
+
+- (int)port
+{
+	int port;
+	switch ([self addressFamily])
+	{
+		case AF_INET:
+			port = ntohs(((struct sockaddr_in *)[[self address] bytes])->sin_port);
+			break;
+		case AF_INET6:
+			port = ntohs(((struct sockaddr_in6 *)[[self address] bytes])->sin6_port);
+			break;
+		default:
+			port = 0;
+	}
+	return port;
+}
+
+- (void)addToCurrentRunLoopForCommonModes
+{
+	// NSRunLoop is not toll-free bridged to CFRunLoop, even though their names
+	// might suggest that they are.
+	if (_runLoopSource == NULL)
+	{
+		_runLoopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _socket, 0);
+		CFRunLoopAddSource(CFRunLoopGetCurrent(), _runLoopSource, kCFRunLoopCommonModes);
+	}
+}
+
+- (void)removeFromCurrentRunLoopForCommonModes
+{
+	if (_runLoopSource)
+	{
+		CFRunLoopRemoveSource(CFRunLoopGetCurrent(), _runLoopSource, kCFRunLoopCommonModes);
+		CFRelease(_runLoopSource);
+		_runLoopSource = NULL;
+	}
+}
+
+- (void)disableAcceptCallBack
+{
+	CFSocketDisableCallBacks(_socket, kCFSocketAcceptCallBack);
+}
+
+- (void)enableAcceptCallBack
+{
+	// The Read, Accept and Data callbacks are mutually exclusive.
+	CFSocketEnableCallBacks(_socket, kCFSocketAcceptCallBack);
+}
+
+- (void)acceptNativeHandle:(NSSocketNativeHandle)nativeHandle
+{
+	id<CFSocketDelegate> delegate = [self delegate];
+	if (delegate)
+	{
+		if ([delegate respondsToSelector:@selector(socket:acceptNativeHandle:)])
+		{
+			[delegate socket:self acceptNativeHandle:nativeHandle];
+		}
+		else if ([delegate respondsToSelector:@selector(socket:acceptStreamPair:)])
+		{
+			CFStreamPair *streamPair = [[CFStreamPair alloc] initWithSocketNativeHandle:nativeHandle];
+			if (streamPair)
+			{
+				[delegate socket:self acceptStreamPair:streamPair];
+			}
+		}
+		else
+		{
+			close(nativeHandle);
+		}
+	}
+	else
+	{
+		close(nativeHandle);
+	}
+}
+
+- (void)dealloc
+{
+	// The de-allocator does not need to wonder if the underlying socket exists,
+	// or not. By contract, the socket must exist. This assumes, of course, that
+	// a failed initialisation sequence does not invoke the
+	// de-allocator. However, you cannot assume that. Assigning self to nil
+	// under ARC de-allocates the instance and invokes the -dealloc method.
+	[self removeFromCurrentRunLoopForCommonModes];
+	if (_socket)
+	{
+		CFRelease(_socket);
+		_socket = NULL;
+	}
+}
+
+@end
+
+NSString *const CFSocketErrorDomain = @"CFSocketErrorDomain";
+
+void __CFSocketCallOut(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)
+{
+	switch (type)
+	{
+		case kCFSocketAcceptCallBack:
+		{
+			// Next Step meets Core Foundation socket native handle type in the
+			// next statement. You can use them interchangeably. Apple
+			// type-define both as int. They are really Unix socket
+			// descriptors. The external interface uses the Next Step
+			// definition, since the Next Step foundation framework is the most
+			// immediate dependency.
+			[(__bridge CFSocket *)info acceptNativeHandle:*(CFSocketNativeHandle *)data];
+			break;
+		}
+		default:
+			;
+	}
+}
diff --git a/CFSockets/CFSocketAddressDataHelpers.h b/CFSockets/CFSocketAddressDataHelpers.h
new file mode 100755
index 0000000..44369fc
--- /dev/null
+++ b/CFSockets/CFSocketAddressDataHelpers.h
@@ -0,0 +1,35 @@
+/* CFSockets CFSocketAddressDataHelpers.h
+ *
+ * Copyright © 2012, 2013, Roy Ratcliffe, Pioneering Software, United Kingdom
+ *
+ * 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, EITHER
+ * EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 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.
+ *
+ ******************************************************************************/
+
+#import <Foundation/Foundation.h>
+
+#import <netinet/in.h>
+
+NSData *CFSocketAddressDataFromIPv6AddressWithPort(const struct in6_addr *addr, in_port_t port);
+NSData *CFSocketAddressDataFromAnyIPv6WithPort(in_port_t port);
+NSData *CFSocketAddressDataFromLoopBackIPv6WithPort(in_port_t port);
+
+NSData *CFSocketAddressDataFromIPv4AddressWithPort(in_addr_t addr, in_port_t port);
+NSData *CFSocketAddressDataFromAnyIPv4WithPort(in_port_t port);
+NSData *CFSocketAddressDataFromLoopBackIPv4WithPort(in_port_t port);
diff --git a/CFSockets/CFSocketAddressDataHelpers.m b/CFSockets/CFSocketAddressDataHelpers.m
new file mode 100644
index 0000000..a054c7d
--- /dev/null
+++ b/CFSockets/CFSocketAddressDataHelpers.m
@@ -0,0 +1,72 @@
+/* CFSockets CFSocketAddressDataHelpers.m
+ *
+ * Copyright © 2012, 2013, Roy Ratcliffe, Pioneering Software, United Kingdom
+ *
+ * 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, EITHER
+ * EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 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.
+ *
+ ******************************************************************************/
+
+#import "CFSocketAddressDataHelpers.h"
+#import "arpa/inet.h"
+
+NSData *CFSocketAddressDataFromIPv6AddressWithPort(const struct in6_addr *addr, in_port_t port)
+{
+	struct sockaddr_in6 sockaddr;
+	memset(&sockaddr, 0, sizeof(sockaddr));
+
+	sockaddr.sin6_len = sizeof(sockaddr);
+	sockaddr.sin6_family = AF_INET6;
+	sockaddr.sin6_port = htons(port);
+	memcpy(&sockaddr.sin6_addr, addr, sizeof(sockaddr.sin6_addr));
+
+	return [NSData dataWithBytes:&sockaddr length:sizeof(sockaddr)];
+}
+
+NSData *CFSocketAddressDataFromAnyIPv6WithPort(in_port_t port)
+{
+	return CFSocketAddressDataFromIPv6AddressWithPort(&in6addr_any, port);
+}
+
+NSData *CFSocketAddressDataFromLoopBackIPv6WithPort(in_port_t port)
+{
+	return CFSocketAddressDataFromIPv6AddressWithPort(&in6addr_loopback, port);
+}
+
+NSData *CFSocketAddressDataFromIPv4AddressWithPort(in_addr_t addr, in_port_t port)
+{
+	struct sockaddr_in sockaddr;
+	memset(&sockaddr, 0, sizeof(sockaddr));
+
+	sockaddr.sin_len = sizeof(sockaddr);
+	sockaddr.sin_family = AF_INET;
+	sockaddr.sin_port = htons(port);
+	sockaddr.sin_addr.s_addr = htonl(addr);
+
+	return [NSData dataWithBytes:&sockaddr length:sizeof(sockaddr)];
+}
+
+NSData *CFSocketAddressDataFromAnyIPv4WithPort(in_port_t port)
+{
+	return CFSocketAddressDataFromIPv4AddressWithPort(INADDR_ANY, port);
+}
+
+NSData *CFSocketAddressDataFromLoopBackIPv4WithPort(in_port_t port)
+{
+	return CFSocketAddressDataFromIPv4AddressWithPort(INADDR_LOOPBACK, port);
+}
diff --git a/CFSockets/CFSockets.h b/CFSockets/CFSockets.h
new file mode 100755
index 0000000..58aa56b
--- /dev/null
+++ b/CFSockets/CFSockets.h
@@ -0,0 +1,28 @@
+/* CFSockets CFSockets.h
+ *
+ * Copyright © 2012, 2013, Roy Ratcliffe, Pioneering Software, United Kingdom
+ *
+ * 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, EITHER
+ * EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 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.
+ *
+ ******************************************************************************/
+
+#import <CFSockets/CFSocket.h>
+#import <CFSockets/CFSocketAddressDataHelpers.h>
+#import <CFSockets/CFStreamPair.h>
+#import <CFSockets/Versioning.h>
diff --git a/CFSockets/CFStreamPair.h b/CFSockets/CFStreamPair.h
new file mode 100755
index 0000000..7d6e5ea
--- /dev/null
+++ b/CFSockets/CFStreamPair.h
@@ -0,0 +1,100 @@
+// CFSockets CFStreamPair.h
+//
+// Copyright © 2012, 2013, Roy Ratcliffe, Pioneering Software, United Kingdom
+//
+// 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, EITHER
+// EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 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.
+//
+//------------------------------------------------------------------------------
+
+#import <Foundation/Foundation.h>
+
+@class CFStreamPair;
+
+@protocol CFStreamPairDelegate<NSObject>
+@optional
+
+- (void)streamPair:(CFStreamPair *)streamPair hasBytesAvailable:(NSUInteger)bytesAvailable;
+
+- (void)streamPair:(CFStreamPair *)streamPair handleRequestEvent:(NSStreamEvent)eventCode;
+- (void)streamPair:(CFStreamPair *)streamPair handleResponseEvent:(NSStreamEvent)eventCode;
+
+@end
+
+/**
+ * Adds request-response semantics to a socket by encapsulating it with
+ * an input-output stream pair.
+ *
+ * For TCP connections, the request input stream corresponds to the
+ * request; _receive_ the request bytes from the request input, _send_ the
+ * response to the response output stream.
+ */
+@interface CFStreamPair : NSObject<NSStreamDelegate>
+
+@property(weak, NS_NONATOMIC_IOSONLY) id<CFStreamPairDelegate> delegate;
+@property(strong, NS_NONATOMIC_IOSONLY) NSInputStream *requestStream;
+@property(strong, NS_NONATOMIC_IOSONLY) NSOutputStream *responseStream;
+
+- (id)initWithRequestStream:(NSInputStream *)requestStream responseStream:(NSOutputStream *)responseStream;
+- (id)initWithSocketNativeHandle:(NSSocketNativeHandle)socketNativeHandle;
+
+/**
+ * Opens the request and response streams, scheduling them for service within
+ * the current run loop. You cannot reopen the streams.
+ *
+ * This method assumes that you have not already delegated, scheduled
+ * or opened the underlying request-response stream pair.
+ */
+- (void)open;
+
+/**
+ * Reverses the opening. Closes the request and response streams, removes them
+ * from the current run loop. This assumes that you send `-close` from the same
+ * thread as you sent the original `-open`.
+ */
+- (void)close;
+
+/**
+ * Destructively receives bytes from the request buffer.
+ */
+- (NSData *)receiveAvailableBytes;
+
+/**
+ * Special convenience method for receiving lines of text from the
+ * request stream based on a given string encoding.
+ *
+ * The result includes any line termination characters. There could be
+ * more than one termination character at the end of the line since some line
+ * termination sequences span multiple characters.
+ *
+ * @result Answers `nil` if the request buffer does not yet contain a complete
+ * line. Try again later.
+ */
+- (NSString *)receiveLineUsingEncoding:(NSStringEncoding)encoding;
+
+- (void)sendBytes:(NSData *)outputBytes;
+
+//-------------------------------------------------------------------- Overrides
+
+- (void)hasBytesAvailable;
+- (void)hasSpaceAvailable;
+- (void)sendBytes;
+- (void)handleRequestEvent:(NSStreamEvent)eventCode;
+- (void)handleResponseEvent:(NSStreamEvent)eventCode;
+
+@end
diff --git a/CFSockets/CFStreamPair.m b/CFSockets/CFStreamPair.m
new file mode 100644
index 0000000..2b6a122
--- /dev/null
+++ b/CFSockets/CFStreamPair.m
@@ -0,0 +1,338 @@
+// CFSockets CFStreamPair.m
+//
+// Copyright © 2012, 2013, Roy Ratcliffe, Pioneering Software, United Kingdom
+//
+// 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, EITHER
+// EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 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.
+//
+//------------------------------------------------------------------------------
+
+#import "CFStreamPair.h"
+
+static NSString *const NewLineCharacter = @"\n";
+
+@interface CFStreamPair()
+
+// You would not normally access the buffers directly. The following exposes the
+// buffer implementation, albeit away from the public header: just a pair of
+// mutable data objects.
+@property(strong, NS_NONATOMIC_IOSONLY) NSMutableData *requestBuffer;
+@property(strong, NS_NONATOMIC_IOSONLY) NSMutableData *responseBuffer;
+
+@end
+
+@implementation CFStreamPair
+
+@synthesize delegate = _delegate;
+
+// streams
+@synthesize requestStream = _requestStream;
+@synthesize responseStream = _responseStream;
+
+// buffers
+@synthesize requestBuffer = _requestBuffer;
+@synthesize responseBuffer = _responseBuffer;
+
+// designated initialiser
+- (id)init
+{
+	if ((self = [super init]))
+	{
+		// Sets up the request and response buffers at the outset. You can ask
+		// for available request bytes even before the request stream
+		// opens. Similarly, you can send response bytes even before the
+		// response opens. Hard to imagine exactly why however. Still, there is
+		// nothing to say that we can assume that the response stream will open
+		// before the request opens, or vice versa; indeed, the delegate may
+		// even respond by sending some bytes even before the response stream
+		// becomes ready. The buffer pair make such behaviour a valid pattern.
+		[self setRequestBuffer:[NSMutableData data]];
+		[self setResponseBuffer:[NSMutableData data]];
+	}
+	return self;
+}
+
+- (void)dealloc
+{
+	[self close];
+}
+
+// convenience initialiser
+- (id)initWithRequestStream:(NSInputStream *)requestStream responseStream:(NSOutputStream *)responseStream
+{
+	self = [self init];
+	if (self)
+	{
+		[self setRequestStream:requestStream];
+		[self setResponseStream:responseStream];
+	}
+	return self;
+}
+
+- (id)initWithSocketNativeHandle:(NSSocketNativeHandle)socketNativeHandle
+{
+	if ((self = [self init]))
+	{
+		CFReadStreamRef readStream = NULL;
+		CFWriteStreamRef writeStream = NULL;
+		CFStreamCreatePairWithSocket(kCFAllocatorDefault, socketNativeHandle, &readStream, &writeStream);
+		if (readStream && writeStream)
+		{
+			CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
+			CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
+			[self setRequestStream:CFBridgingRelease(readStream)];
+			[self setResponseStream:CFBridgingRelease(writeStream)];
+		}
+		else
+		{
+			if (readStream) CFRelease(readStream);
+			if (writeStream) CFRelease(writeStream);
+
+			// Something went wrong. Answer nil. Bear in mind however that this
+			// does not mean that the de-allocation method will not run: it
+			// will run.
+			self = nil;
+		}
+	}
+	return self;
+}
+
+- (void)open
+{
+	for (NSStream *stream in [NSArray arrayWithObjects:[self requestStream], [self responseStream], nil])
+	{
+		[stream setDelegate:self];
+
+		[stream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
+		[stream open];
+	}
+}
+
+- (void)close
+{
+	// Send -close first. Closing may trigger events. Let the stream emit all
+	// events until closing finishes. Might be wise to check the stream status
+	// first, before attempting to close the stream. The de-allocator invokes
+	// -close and therefore may send a double-close if the pair has already
+	// received an explicit -close message.
+	for (NSStream *stream in [NSArray arrayWithObjects:[self requestStream], [self responseStream], nil])
+	{
+		[stream close];
+		[stream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
+	}
+}
+
+- (NSData *)receiveAvailableBytes
+{
+	NSData *bytes = [[self requestBuffer] copy];
+	[[self requestBuffer] setLength:0];
+	return bytes;
+}
+
+- (NSString *)receiveLineUsingEncoding:(NSStringEncoding)encoding {
+    // The implementation first converts all the request bytes to a string. This
+    // could be risky for multi-byte characters. The implementation effectively
+    // assumes that multi-byte characters do not cross buffer boundaries.
+    //
+    // When the request range has length equal to zero, sending
+    // -lineRangeForRange: searches for the first line. The final bit is
+    // tricky. How to dissect the line from any remaining characters? Simply
+    // convert the remaining characters back to data using the given encoding.
+    NSString *result;
+
+    NSArray *requestsArray = [self requestsArrayFromData:self.requestBuffer withEncoding:encoding];
+    NSString *requestString = requestsArray.firstObject;
+
+    NSRange lineRange = [requestString lineRangeForRange:NSMakeRange(0, 0)];
+
+    if (lineRange.length) {
+        NSMutableArray *mutableRequestsArray = [requestsArray mutableCopy];
+        [mutableRequestsArray removeObjectAtIndex:0];
+
+        if (mutableRequestsArray.count) {
+            self.requestBuffer.data = [self dataFromRequestsArray:mutableRequestsArray withEncoding:encoding];
+        } else {
+            self.requestBuffer.length = 0;
+        }
+
+        result = [requestString substringToIndex:lineRange.length];
+    } else {
+        result = nil;
+    }
+
+    return result;
+}
+
+- (NSArray *)requestsArrayFromData:(NSData *)data withEncoding:(NSStringEncoding)encoding {
+    NSString *requestsString = [[NSString alloc] initWithData:data encoding:encoding];
+    NSMutableArray *requestsArray = [[requestsString componentsSeparatedByString:NewLineCharacter] mutableCopy];
+
+    for (NSInteger i = requestsArray.count - 1; i >= 0; i--) {
+        NSString *requestString = [((NSString *) requestsArray[i]) stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
+
+        if (requestString.length == 0) {
+            [requestsArray removeObjectAtIndex:i];
+        }
+    }
+
+    return requestsArray;
+}
+
+- (NSData *)dataFromRequestsArray:(NSArray *)requestsArray withEncoding:(NSStringEncoding)encoding {
+    NSString *requestBufferString = [requestsArray componentsJoinedByString:NewLineCharacter];
+
+    return [requestBufferString dataUsingEncoding:encoding];
+}
+
+- (void)sendBytes:(NSData *)responseBytes
+{
+	[[self responseBuffer] appendData:responseBytes];
+
+	// Trigger a "has space available" event if the response stream reports
+	// available space at this point.
+	if ([[self responseStream] hasSpaceAvailable])
+	{
+		[self hasSpaceAvailable];
+	}
+}
+
+#pragma mark -
+#pragma mark Overrides
+
+- (void)hasBytesAvailable
+{
+	if ([[self requestBuffer] length])
+	{
+		id delegate = [self delegate];
+		if (delegate && [delegate respondsToSelector:@selector(streamPair:hasBytesAvailable:)])
+		{
+			[delegate streamPair:self hasBytesAvailable:[[self requestBuffer] length]];
+		}
+	}
+}
+
+- (void)hasSpaceAvailable
+{
+	// Note that writing zero bytes to the response stream closes the
+	// connection. Therefore, avoid sending nothing unless you want to close.
+	if ([[self responseBuffer] length])
+	{
+		[self sendBytes];
+	}
+}
+
+- (void)sendBytes
+{
+	NSMutableData *responseBuffer = [self responseBuffer];
+	NSInteger bytesSent = [[self responseStream] write:[responseBuffer bytes] maxLength:[responseBuffer length]];
+	if (bytesSent > 0)
+	{
+		[responseBuffer replaceBytesInRange:NSMakeRange(0, bytesSent) withBytes:NULL length:0];
+	}
+}
+
+- (void)handleRequestEvent:(NSStreamEvent)eventCode
+{
+	switch (eventCode)
+	{
+		case NSStreamEventHasBytesAvailable:
+		{
+			uint8_t bytes[4096];
+			NSInteger bytesAvailable = [[self requestStream] read:bytes maxLength:sizeof(bytes)];
+			// Do not send a -read:maxLength message unless the stream reports
+			// that it has bytes available. Always send this message at least
+			// once when the bytes-available event fires, i.e. right now. The
+			// stream event indicates that available bytes have already been
+			// sensed. Avoid asking again.
+			//
+			// What happens however if more bytes arrive while reading, or the
+			// available bytes overflow the stack-based temporary buffer? In
+			// these cases, after reading, ask if more bytes exist. Issue
+			// another read if they do, and repeat while they do.
+			while (bytesAvailable > 0)
+			{
+				[[self requestBuffer] appendBytes:bytes length:bytesAvailable];
+				if ([[self requestStream] hasBytesAvailable])
+				{
+					bytesAvailable = [[self requestStream] read:bytes maxLength:sizeof(bytes)];
+				}
+				else
+				{
+					bytesAvailable = 0;
+				}
+			}
+			// Please note, the delegate can receive an has-bytes-available
+			// event immediately followed by an error event.
+			[self hasBytesAvailable];
+			if (bytesAvailable < 0)
+			{
+
+			}
+			break;
+		}
+		default:
+			;
+	}
+
+	id delegate = [self delegate];
+	if (delegate && [delegate respondsToSelector:@selector(streamPair:handleRequestEvent:)])
+	{
+		[delegate streamPair:self handleRequestEvent:eventCode];
+	}
+}
+
+- (void)handleResponseEvent:(NSStreamEvent)eventCode
+{
+	switch (eventCode)
+	{
+		case NSStreamEventHasSpaceAvailable:
+		{
+			[self hasSpaceAvailable];
+			break;
+		}
+		default:
+			;
+	}
+
+	id delegate = [self delegate];
+	if (delegate && [delegate respondsToSelector:@selector(streamPair:handleResponseEvent:)])
+	{
+		[delegate streamPair:self handleResponseEvent:eventCode];
+	}
+}
+
+#pragma mark -
+#pragma mark Stream Delegate
+
+- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode
+{
+	if (stream == [self requestStream])
+	{
+		[self handleRequestEvent:eventCode];
+	}
+	else if (stream == [self responseStream])
+	{
+		[self handleResponseEvent:eventCode];
+	}
+	else
+	{
+		;
+	}
+}
+
+@end
diff --git a/CFSockets/Versioning.h b/CFSockets/Versioning.h
new file mode 100755
index 0000000..8d3e74e
--- /dev/null
+++ b/CFSockets/Versioning.h
@@ -0,0 +1,36 @@
+/* CFSockets Versioning.h
+ *
+ * Copyright © 2012, 2013, Roy Ratcliffe, Pioneering Software, United Kingdom
+ *
+ * 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, EITHER
+ * EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 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.
+ *
+ ******************************************************************************/
+
+#import <Foundation/Foundation.h>
+
+extern const unsigned char kCFSocketsVersionString[];
+extern const double kCFSocketsVersionNumber;
+
+/**
+ * Answers the current Apple-generic versioning-formatted version string. The
+ * version string has been trimmed. It has no leading or trailing whitespace or
+ * newlines. Note that the raw C-based version string contrastingly has a single
+ * terminating newline character.
+ */
+NSString *CFSocketsVersionString(void);
diff --git a/CFSockets/Versioning.m b/CFSockets/Versioning.m
new file mode 100755
index 0000000..94846c8
--- /dev/null
+++ b/CFSockets/Versioning.m
@@ -0,0 +1,38 @@
+/* CFSockets Versioning.m
+ *
+ * Copyright © 2012, 2013, Roy Ratcliffe, Pioneering Software, United Kingdom
+ *
+ * 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, EITHER
+ * EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 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.
+ *
+ ******************************************************************************/
+
+#import "Versioning.h"
+
+NSString *CFSocketsVersionString()
+{
+	// The implementation assumes that the raw C-language version string
+	// terminates with null. It also trims assuming that the very last character
+	// is a terminating line feed. Also assumes UTF-8 encoding.
+	static NSString *__strong versionString;
+	if (versionString == nil)
+	{
+		versionString = [[NSString stringWithCString:(const char *)kCFSocketsVersionString encoding:NSUTF8StringEncoding] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+	}
+	return versionString;
+}
diff --git a/README.md b/README.md
new file mode 100755
index 0000000..1b59196
--- /dev/null
+++ b/README.md
@@ -0,0 +1,14 @@
+# Core Foundation Sockets
+
+[![Build Status](https://travis-ci.org/royratcliffe/CFSockets.png?branch=master)](https://travis-ci.org/royratcliffe/CFSockets)
+
+The project has four targets: a Cocoa framework with its unit test bundle, an
+iOS static library with its unit test bundle. The framework and the library
+share the same source files. The sources use Automatic Reference Counting (ARC).
+
+Why the plural project name? The project goes by the name 'Core Foundation
+Sockets' rather than 'Core Foundation Socket.' This avoids confusion with
+`CFSocket` which represents just one component of the CFSockets project. The
+project's monolithic header imports as `<CFSockets/CFSockets.h>`. The
+additional _s_ for the project prevents a clash with header
+`<CFSockets/CFSocket.h>` which only imports the `CFSocket` class.