#pragma mark Includes
#include <CFNetwork/CFServerPriv.h>
#include <CoreFoundation/CFRuntime.h>
#include <CFNetwork/CFNetwork.h>
#include <assert.h>
#if defined(__WIN32__)
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#endif
#if 0
#pragma mark -
#pragma mark Constant Strings
#endif
#ifdef __CONSTANT_CFSTRINGS__
#define _kCFServerNULL CFSTR("0x0")
#define _kCFServerPtrFormat CFSTR("<0x%x>")
#define _kCFServerDescribeFormat CFSTR("<Server 0x%x>{sockets=[%@, %@], service=%@, info=%@}")
#define _kCFServerEmptyString CFSTR("")
#else
static CONST_STRING_DECL(_kCFServerNULL, "0x0")
static CONST_STRING_DECL(_kCFServerPtrFormat, "<0x%x>")
static CONST_STRING_DECL(_kCFServerDescribeFormat, "<Server 0x%x>{sockets=[%@, %@], service=%@, info=%@}")
static CONST_STRING_DECL(_kCFServerEmptyString, "")
#endif
#pragma mark -
#pragma mark Type Declarations
typedef struct {
CFRuntimeBase _base;
CFSocketRef _sockets[2];
CFStringRef _name; CFStringRef _type; UInt32 _port; CFNetServiceRef _service;
_CFServerCallBack _callback; _CFServerContext _ctxt; } Server;
#pragma mark -
#pragma mark Constant Definitions
#pragma mark -
#pragma mark Static Function Declarations
static void _ServerRelease(_CFServerRef server);
CFStringRef _ServerCopyDescription(_CFServerRef server);
static void _ServerReleaseSocket(Server* server);
static void _ServerHandleAccept(Server* server, CFSocketNativeHandle nativeSocket);
static void _SocketCallBack(CFSocketRef sock, CFSocketCallBackType type, CFDataRef address, const void *data, Server* server);
#if defined(__MACH__)
static void _ServerReleaseNetService(Server* server);
static Boolean _ServerCreateAndRegisterNetService(Server* server);
static void _ServerHandleNetServiceError(Server* server, CFStreamError* error);
static void _NetServiceCallBack(CFNetServiceRef service, CFStreamError* error, Server* server);
#endif
#pragma mark -
#pragma mark Static Variable Definitions
static CFTypeID _ServerTypeId = _kCFRuntimeNotATypeID;
#pragma mark -
#pragma mark Extern Function Definitions (API)
CFTypeID
_CFServerGetTypeID(void) {
if (_ServerTypeId == _kCFRuntimeNotATypeID) {
static const CFRuntimeClass ServerClass = {
0, "_CFServer", NULL, NULL, (void(*)(CFTypeRef))_ServerRelease, NULL, NULL, NULL,
(CFStringRef(*)(CFTypeRef))_ServerCopyDescription };
_ServerTypeId = _CFRuntimeRegisterClass(&ServerClass);
}
return _ServerTypeId;
}
_CFServerRef
_CFServerCreate(CFAllocatorRef alloc, _CFServerCallBack callback, _CFServerContext* context) {
Server* server = NULL;
do {
int yes = 1;
CFSocketContext socketCtxt = {0,
NULL,
(const void*(*)(const void*))&CFRetain,
(void(*)(const void*))&CFRelease,
(CFStringRef(*)(const void *))&CFCopyDescription};
CFTypeID id = _CFServerGetTypeID();
if (id != _kCFRuntimeNotATypeID) {
server = (Server*)_CFRuntimeCreateInstance(alloc,
id,
sizeof(Server) - sizeof(CFRuntimeBase),
NULL);
}
if (server == NULL)
break;
server->_name = NULL;
server->_type = NULL;
server->_port = 0;
server->_service = NULL;
memset(&server->_callback, 0, sizeof(server->_callback));
memset(&server->_ctxt, 0, sizeof(server->_ctxt));
socketCtxt.info = server;
server->_sockets[0] = CFSocketCreate(alloc,
PF_INET,
SOCK_STREAM,
IPPROTO_TCP,
kCFSocketAcceptCallBack,
(CFSocketCallBack)&_SocketCallBack,
&socketCtxt);
if (server->_sockets[0] == NULL)
break;
server->_sockets[1] = CFSocketCreate(alloc,
PF_INET6,
SOCK_STREAM,
IPPROTO_TCP,
kCFSocketAcceptCallBack,
(CFSocketCallBack)&_SocketCallBack,
&socketCtxt);
if (server->_sockets[1] == NULL)
break;
setsockopt(CFSocketGetNative(server->_sockets[0]), SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(yes));
setsockopt(CFSocketGetNative(server->_sockets[1]), SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(yes));
server->_callback = callback;
memcpy(&(server->_ctxt), context, sizeof(server->_ctxt));
if (server->_ctxt.info && server->_ctxt.retain)
server->_ctxt.info = (void*)server->_ctxt.retain(server->_ctxt.info);
return (_CFServerRef)server;
} while (0);
if (server) {
_CFServerInvalidate((_CFServerRef)server);
CFRelease((_CFServerRef)server);
}
return NULL;
}
UInt32
_CFServerGetPort(_CFServerRef server) {
return ((Server*)server)->_port & 0x0000FFFF;
}
void
_ServerRelease(_CFServerRef server) {
_CFServerInvalidate(server);
}
CFStringRef
_ServerCopyDescription(_CFServerRef server) {
Server* s = (Server*)server;
CFAllocatorRef alloc = CFGetAllocator(server);
CFTypeRef socket4, socket6, service;
CFStringRef info, result;
socket4 = socket6 = service = _kCFServerNULL;
if (s->_sockets[0] != NULL)
socket4 = (CFTypeRef)(s->_sockets[0]);
if (s->_sockets[1] != NULL)
socket6 = (CFTypeRef)(s->_sockets[1]);
if (s->_service != NULL)
service = (CFTypeRef)(s->_service);
if (s->_ctxt.copyDescription)
info = s->_ctxt.copyDescription(s->_ctxt.info);
else
info = CFStringCreateWithFormat(alloc, NULL, _kCFServerPtrFormat, (UInt32)(s->_ctxt.info));
result = CFStringCreateWithFormat(alloc,
NULL,
_kCFServerDescribeFormat,
(UInt32)server,
socket4,
socket6,
service,
info);
CFRelease(info);
return result;
}
Boolean
_CFServerStart(_CFServerRef server, CFStringRef name, CFStringRef type, UInt32 port) {
Server* s = (Server*)server;
CFDataRef address = NULL;
do {
unsigned i;
CFRunLoopRef rl = CFRunLoopGetCurrent();
CFAllocatorRef alloc = CFGetAllocator(server);
struct sockaddr_in addr4;
struct sockaddr_in6 addr6;
if ((port & 0xFFFF0000U) != 0)
break;
if (name == NULL)
name = _kCFServerEmptyString;
for (i = 0; i < (sizeof(s->_sockets) / sizeof(s->_sockets[0])); i++) {
CFRunLoopSourceRef src = CFSocketCreateRunLoopSource(alloc, s->_sockets[i], 0);
if (src == NULL)
break;
CFRunLoopAddSource(rl, src, kCFRunLoopCommonModes);
CFRelease(src);
}
memset(&addr4, 0, sizeof(addr4));
#if !defined(__WIN32__)
addr4.sin_len = sizeof(addr4);
#endif
addr4.sin_family = AF_INET;
addr4.sin_port = htons((UInt16)port);
addr4.sin_addr.s_addr = htonl(INADDR_ANY);
address = CFDataCreateWithBytesNoCopy(alloc, (const UInt8*)&addr4, sizeof(addr4), kCFAllocatorNull);
if (address == NULL)
break;
if (CFSocketSetAddress(s->_sockets[0], address) != kCFSocketSuccess)
break;
CFRelease(address);
address = CFSocketCopyAddress(s->_sockets[0]);
memcpy(&addr4, CFDataGetBytePtr(address), CFDataGetLength(address));
port = ntohs(addr4.sin_port);
CFRelease(address);
memset(&addr6, 0, sizeof(addr6));
addr6.sin6_family = AF_INET6;
#ifndef __WIN32__
addr6.sin6_port = htons((UInt16)port);
addr6.sin6_len = sizeof(addr6);
memcpy(&(addr6.sin6_addr), &in6addr_any, sizeof(addr6.sin6_addr));
#else
#ifndef __MINGW32__
IN6ADDR_SETANY(addr6);
addr6.sin6_port = htons((UInt16)port);
#else
addr6.sin6_port = htons((UInt16)port);
struct sockaddr_in6 in6addr_any = IN6ADDR_ANY_INIT;
memcpy(&(addr6.sin6_addr), &in6addr_any, sizeof(addr6.sin6_addr));
#endif
#endif
address = CFDataCreateWithBytesNoCopy(alloc, (const UInt8*)&addr6, sizeof(addr6), kCFAllocatorNull);
if (CFSocketSetAddress(s->_sockets[1], address) != kCFSocketSuccess)
break;
s->_name = CFRetain(name);
s->_type = type ? CFRetain(type) : NULL;
s->_port = port;
#if defined(__MACH__)
if (type && !_ServerCreateAndRegisterNetService(s))
break;
#endif
CFRelease(address);
return TRUE;
} while (0);
if (address)
CFRelease(address);
_ServerReleaseSocket(s);
return FALSE;
}
void
_CFServerInvalidate(_CFServerRef server) {
Server* s = (Server*)server;
if (s->_ctxt.info && s->_ctxt.release)
s->_ctxt.release(s->_ctxt.info);
memset(&(s->_ctxt), 0, sizeof(s->_ctxt));
s->_callback = NULL;
#if defined(__MACH__)
_ServerReleaseNetService(s);
#endif
if (s->_name) {
CFRelease(s->_name);
s->_name = NULL;
}
if (s->_type) {
CFRelease(s->_type);
s->_type = NULL;
}
_ServerReleaseSocket(s);
}
#pragma mark -
#pragma mark Static Function Definitions
#if defined(__MACH__)
void
_ServerReleaseNetService(Server* server) {
if (server->_service != NULL) {
CFNetServiceUnscheduleFromRunLoop(server->_service, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
CFNetServiceSetClient(server->_service, NULL, NULL);
CFNetServiceCancel(server->_service);
CFRelease(server->_service);
server->_service = NULL;
}
}
#endif
void
_ServerReleaseSocket(Server* server) {
unsigned i;
for (i = 0; i < (sizeof(server->_sockets) / sizeof(server->_sockets[0])); i++) {
if (server->_sockets[i] != NULL) {
CFSocketInvalidate(server->_sockets[i]);
CFRelease(server->_sockets[i]);
server->_sockets[i] = NULL;
}
}
}
#if defined(__MACH__)
Boolean
_ServerCreateAndRegisterNetService(Server* server) {
do {
UInt32 port = server->_port;
Boolean didSet, didRegister;
CFNetServiceClientContext netSvcCtxt = {0,
server,
(CFAllocatorRetainCallBack)&CFRetain,
(CFAllocatorReleaseCallBack)&CFRelease,
(CFAllocatorCopyDescriptionCallBack)&CFCopyDescription};
if (port == 0) {
CFDataRef addr = CFSocketCopyAddress(server->_sockets[0]);
struct sockaddr_in* nativeAddr = (struct sockaddr_in*)CFDataGetBytePtr(addr);
CFRelease(addr);
port = ntohs(nativeAddr->sin_port);
}
server->_service = CFNetServiceCreate(CFGetAllocator((_CFServerRef)server),
_kCFServerEmptyString,
server->_type,
server->_name,
port);
if (server->_service == NULL)
break;
didSet = CFNetServiceSetClient(server->_service,
(CFNetServiceClientCallBack)&_NetServiceCallBack,
&netSvcCtxt);
if (!didSet)
break;
CFNetServiceScheduleWithRunLoop(server->_service, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
didRegister = CFNetServiceRegisterWithOptions(server->_service, 0, NULL);
if (!didRegister)
break;
return TRUE;
} while (0);
_ServerReleaseNetService(server);
return FALSE;
}
#endif
void
_ServerHandleAccept(Server* server, CFSocketNativeHandle nativeSocket) {
if (server->_callback != NULL) {
CFStreamError error = {0, 0};
server->_callback((_CFServerRef)server, nativeSocket, &error, server->_ctxt.info);
}
}
#if defined(__MACH__)
void
_ServerHandleNetServiceError(Server* server, CFStreamError* error) {
_ServerReleaseNetService(server);
if (error->error != 0) {
_ServerReleaseSocket(server);
if (server->_callback != NULL)
server->_callback((_CFServerRef)server, (CFSocketNativeHandle)(-1), error, server->_ctxt.info);
}
}
#endif
void
_SocketCallBack(CFSocketRef sock, CFSocketCallBackType type, CFDataRef address, const void *data, Server* server) {
assert((sock == server->_sockets[0]) || (sock == server->_sockets[1]));
if (type == kCFSocketAcceptCallBack) {
assert((data != NULL) && (*((CFSocketNativeHandle*)data) != -1));
_ServerHandleAccept(server, *((CFSocketNativeHandle*)data));
}
}
#if defined(__MACH__)
void
_NetServiceCallBack(CFNetServiceRef service, CFStreamError* error, Server* server) {
assert(service == server->_service);
if (error->error)
_ServerHandleNetServiceError(server, error);
}
#endif