From 15a3a237583e69c6caa377986d80efac6daf4f59 Mon Sep 17 00:00:00 2001 From: Todd White Date: Wed, 22 Jul 2026 20:19:45 -0400 Subject: [PATCH] Register CFSocket and correct its address accessors CFInitialize did not call CFSocketInitialize, so the socket type was never registered. CFSocketGetTypeID returned zero and every attempt to create a socket failed. Add the missing registration. CFSocketCopyAddress and CFSocketCopyPeerAddress passed an uninitialised length to getsockname and getpeername. Set it to the size of the address buffer, hold the result in a sockaddr_storage, and cache the address only when the call succeeds. CFSocketCopyPeerAddress also read and stored the local _address field rather than _peerAddress, so it returned the local address; it now uses the peer field, which leaves an unconnected socket with no peer address. --- Source/CFRuntime.c | 2 + Source/CFSocket.c | 32 ++++++++-------- Tests/CFSocket/TestInfo | 0 Tests/CFSocket/basic.m | 78 +++++++++++++++++++++++++++++++++++++++ Tests/CFSocket/peeraddr.m | 48 ++++++++++++++++++++++++ 5 files changed, 144 insertions(+), 16 deletions(-) create mode 100644 Tests/CFSocket/TestInfo create mode 100755 Tests/CFSocket/basic.m create mode 100755 Tests/CFSocket/peeraddr.m diff --git a/Source/CFRuntime.c b/Source/CFRuntime.c index de03262..e656619 100644 --- a/Source/CFRuntime.c +++ b/Source/CFRuntime.c @@ -478,6 +478,7 @@ GS_PRIVATE void CFNumberInitialize (void); GS_PRIVATE void CFNumberFormatterInitialize (void); GS_PRIVATE void CFRunLoopInitialize (void); GS_PRIVATE void CFSetInitialize (void); +GS_PRIVATE void CFSocketInitialize (void); GS_PRIVATE void CFStreamInitialize (void); GS_PRIVATE void CFStringInitialize (void); GS_PRIVATE void CFConstantStringInitialize (void); @@ -530,6 +531,7 @@ CFInitialize (void) CFNumberInitialize (); CFNumberFormatterInitialize (); CFSetInitialize (); + CFSocketInitialize (); CFStreamInitialize (); CFStringInitialize (); CFConstantStringInitialize (); /* must be after CFStringIntialize () */ diff --git a/Source/CFSocket.c b/Source/CFSocket.c index ff83842..a5e5087 100644 --- a/Source/CFSocket.c +++ b/Source/CFSocket.c @@ -375,16 +375,16 @@ CFSocketCopyAddress (CFSocketRef s) GSMutexLock (&s->_lock); if (s->_address == NULL) { - struct sockaddr addr; - socklen_t addrlen; - getsockname (s->_socket, &addr, &addrlen); - s->_address = CFDataCreate (CFGetAllocator (s), (const UInt8*)&addr, - (CFIndex)addrlen); + struct sockaddr_storage addr; + socklen_t addrlen = sizeof (addr); + if (getsockname (s->_socket, (struct sockaddr*)&addr, &addrlen) == 0) + s->_address = CFDataCreate (CFGetAllocator (s), (const UInt8*)&addr, + (CFIndex)addrlen); } if (s->_address != NULL) ret = CFRetain (s->_address); GSMutexUnlock (&s->_lock); - + return ret; } @@ -392,20 +392,20 @@ CFDataRef CFSocketCopyPeerAddress (CFSocketRef s) { CFDataRef ret = NULL; - + GSMutexLock (&s->_lock); - if (s->_address == NULL) + if (s->_peerAddress == NULL) { - struct sockaddr addr; - socklen_t addrlen; - getpeername (s->_socket, &addr, &addrlen); - s->_address = CFDataCreate (CFGetAllocator (s), (const UInt8*)&addr, - (CFIndex)addrlen); + struct sockaddr_storage addr; + socklen_t addrlen = sizeof (addr); + if (getpeername (s->_socket, (struct sockaddr*)&addr, &addrlen) == 0) + s->_peerAddress = CFDataCreate (CFGetAllocator (s), + (const UInt8*)&addr, (CFIndex)addrlen); } - if (s->_address != NULL) - ret = CFRetain (s->_address); + if (s->_peerAddress != NULL) + ret = CFRetain (s->_peerAddress); GSMutexUnlock (&s->_lock); - + return ret; } diff --git a/Tests/CFSocket/TestInfo b/Tests/CFSocket/TestInfo new file mode 100644 index 0000000..e69de29 diff --git a/Tests/CFSocket/basic.m b/Tests/CFSocket/basic.m new file mode 100755 index 0000000..054e736 --- /dev/null +++ b/Tests/CFSocket/basic.m @@ -0,0 +1,78 @@ +#include "CoreFoundation/CFRunLoop.h" +#include "CoreFoundation/CFSocket.h" +#include "CoreFoundation/CFData.h" + +#include +#include +#include + +#include "../CFTesting.h" + +int +main (void) +{ + CFSocketRef s; + CFSocketContext ctx; + CFSocketContext got; + int marker = 42; + struct sockaddr_in sin; + CFDataRef addrData; + CFDataRef bound; + + PASS_CF(CFSocketGetTypeID () != 0, "CFSocketGetTypeID is non-zero."); + + memset (&ctx, 0, sizeof ctx); + ctx.info = ▮ + s = CFSocketCreate (NULL, PF_INET, SOCK_STREAM, IPPROTO_TCP, 0, NULL, &ctx); + PASS_CF(s != NULL, "A TCP socket is created."); + + PASS_CF(CFGetTypeID (s) == CFSocketGetTypeID (), + "The socket carries the socket type id."); + PASS_CF(CFSocketGetNative (s) >= 0, "The socket has a native handle."); + PASS_CF(CFSocketIsValid (s), "A fresh socket is valid."); + + CFSocketSetSocketFlags (s, kCFSocketCloseOnInvalidate + | kCFSocketAutomaticallyReenableReadCallBack); + PASS_CF(CFSocketGetSocketFlags (s) == (CFOptionFlags) + (kCFSocketCloseOnInvalidate + | kCFSocketAutomaticallyReenableReadCallBack), + "Socket flags round-trip through the getter."); + + memset (&got, 0, sizeof got); + CFSocketGetContext (s, &got); + PASS_CF(got.info == &marker, "The context info is returned."); + + /* Bind to an ephemeral loopback port and read the address back. */ + memset (&sin, 0, sizeof sin); + sin.sin_family = AF_INET; + sin.sin_port = 0; + sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + addrData = CFDataCreate (NULL, (const UInt8 *)&sin, sizeof sin); + PASS_CF(CFSocketSetAddress (s, addrData) == kCFSocketSuccess, + "Binding to a loopback address succeeds."); + CFRelease (addrData); + + bound = CFSocketCopyAddress (s); + PASS_CF(bound != NULL + && CFDataGetLength (bound) == (CFIndex)sizeof (struct sockaddr_in), + "The bound address has the length of a sockaddr_in."); + if (bound != NULL + && CFDataGetLength (bound) == (CFIndex)sizeof (struct sockaddr_in)) + { + const struct sockaddr_in *b = + (const struct sockaddr_in *)CFDataGetBytePtr (bound); + PASS_CF(b->sin_family == AF_INET + && b->sin_addr.s_addr == htonl (INADDR_LOOPBACK), + "The bound address is the loopback address."); + } + if (bound != NULL) + CFRelease (bound); + + CFSocketInvalidate (s); + PASS_CF(CFSocketIsValid (s) == false, + "An invalidated socket is no longer valid."); + + CFRelease (s); + + return 0; +} diff --git a/Tests/CFSocket/peeraddr.m b/Tests/CFSocket/peeraddr.m new file mode 100755 index 0000000..2836919 --- /dev/null +++ b/Tests/CFSocket/peeraddr.m @@ -0,0 +1,48 @@ +#include "CoreFoundation/CFRunLoop.h" +#include "CoreFoundation/CFSocket.h" +#include "CoreFoundation/CFData.h" + +#include +#include +#include + +#include "../CFTesting.h" + +int +main (void) +{ + CFSocketRef s; + struct sockaddr_in sin; + CFDataRef addrData; + CFDataRef local; + CFDataRef peer; + + s = CFSocketCreate (NULL, PF_INET, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL); + PASS_CF(s != NULL, "A TCP socket is created."); + + memset (&sin, 0, sizeof sin); + sin.sin_family = AF_INET; + sin.sin_port = 0; + sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + addrData = CFDataCreate (NULL, (const UInt8 *)&sin, sizeof sin); + CFSocketSetAddress (s, addrData); + CFRelease (addrData); + + /* The local address is available after binding. */ + local = CFSocketCopyAddress (s); + PASS_CF(local != NULL, "A bound socket has a local address."); + + /* The socket is never connected, so it has no peer address, even after + * the local address has been read. + */ + peer = CFSocketCopyPeerAddress (s); + PASS_CF(peer == NULL, + "An unconnected socket has no peer address."); + + if (local != NULL) CFRelease (local); + if (peer != NULL) CFRelease (peer); + CFSocketInvalidate (s); + CFRelease (s); + + return 0; +}