Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -159,11 +159,12 @@ SRCS_APPLETALK = OFDDPSocket.m SRCS_IPX = OFIPXSocket.m \ OFSPXSocket.m \ OFSPXStreamSocket.m SRCS_SCTP = OFSCTPSocket.m -SRCS_UNIX_SOCKETS = OFUNIXDatagramSocket.m \ +SRCS_UNIX_SOCKETS = OFUNIXDatagramSocket.m \ + OFUNIXSequencedPacketSocket.m \ OFUNIXStreamSocket.m SRCS_SUBPROCESSES = OFSubprocess.m SRCS_THREADS = OFCondition.m \ OFMutex.m \ OFPlainCondition.m \ Index: src/OFSequencedPacketSocket.m ================================================================== --- src/OFSequencedPacketSocket.m +++ src/OFSequencedPacketSocket.m @@ -369,10 +369,15 @@ break; #ifdef OF_HAVE_IPV6 case AF_INET6: client->_remoteAddress.family = OFSocketAddressFamilyIPv6; break; +#endif +#ifdef OF_HAVE_UNIX_SOCKETS + case AF_UNIX: + client->_remoteAddress.family = OFSocketAddressFamilyUNIX; + break; #endif #ifdef OF_HAVE_IPX case AF_IPX: client->_remoteAddress.family = OFSocketAddressFamilyIPX; break; ADDED src/OFUNIXSequencedPacketSocket.h Index: src/OFUNIXSequencedPacketSocket.h ================================================================== --- /dev/null +++ src/OFUNIXSequencedPacketSocket.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2008-2024 Jonathan Schleifer + * + * All rights reserved. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3.0 only, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * version 3.0 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3.0 along with this program. If not, see + * . + */ + +#import "OFSequencedPacketSocket.h" + +OF_ASSUME_NONNULL_BEGIN + +/** + * @protocol OFUNIXSequencedPacketSocketDelegate \ + * OFUNIXSequencedPacketSocket.h ObjFW/ObjFW.h + * + * A delegate for OFUNIXSequencedPacketSocket. + */ +@protocol OFUNIXSequencedPacketSocketDelegate +@end + +/** + * @class OFUNIXSequencedPacketSocket \ + * OFUNIXSequencedPacketSocket.h ObjFW/ObjFW.h + * + * @brief A class which provides methods to create and use UNIX sequenced + * packet sockets. + * + * To connect to a server, create a socket and connect it. + * To create a server, create a socket, bind it and listen on it. + */ +@interface OFUNIXSequencedPacketSocket: OFSequencedPacketSocket +{ + OF_RESERVE_IVARS(OFUNIXSequencedPacketSocket, 4) +} + +/** + * @brief The delegate for asynchronous operations on the socket. + * + * @note The delegate is retained for as long as asynchronous operations are + * still ongoing. + */ +@property OF_NULLABLE_PROPERTY (assign, nonatomic) + id delegate; + +/** + * @brief Connects the OFUNIXSequencedPacketSocket to the specified path. + * + * @param path The path to connect to + * @throw OFConnectUNIXSocketFailedException Connecting failed + * @throw OFAlreadyOpenException The socket is already connected or bound + */ +- (void)connectToPath: (OFString *)path; + +/** + * @brief Binds the socket to the specified path. + * + * @param path The path to bind to + * @throw OFBindUNIXSocketFailedException Binding failed + * @throw OFAlreadyOpenException The socket is already connected or bound + */ +- (void)bindToPath: (OFString *)path; +@end + +OF_ASSUME_NONNULL_END ADDED src/OFUNIXSequencedPacketSocket.m Index: src/OFUNIXSequencedPacketSocket.m ================================================================== --- /dev/null +++ src/OFUNIXSequencedPacketSocket.m @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2008-2024 Jonathan Schleifer + * + * All rights reserved. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3.0 only, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * version 3.0 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3.0 along with this program. If not, see + * . + */ + +#include "config.h" + +#ifdef HAVE_FCNTL_H +# include +#endif + +#import "OFUNIXSequencedPacketSocket.h" +#import "OFSocket.h" +#import "OFSocket+Private.h" +#import "OFString.h" + +#import "OFAlreadyOpenException.h" +#import "OFBindUNIXSocketFailedException.h" +#import "OFConnectUNIXSocketFailedException.h" + +@implementation OFUNIXSequencedPacketSocket +@dynamic delegate; + +- (void)connectToPath: (OFString *)path +{ + OFSocketAddress address; +#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC) + int flags; +#endif + + if (_socket != OFInvalidSocketHandle) + @throw [OFAlreadyOpenException exceptionWithObject: self]; + + address = OFSocketAddressMakeUNIX(path); + + if ((_socket = socket(address.sockaddr.un.sun_family, + SOCK_SEQPACKET | SOCK_CLOEXEC, 0)) == OFInvalidSocketHandle) + @throw [OFConnectUNIXSocketFailedException + exceptionWithPath: path + socket: self + errNo: _OFSocketErrNo()]; + + _canBlock = true; + +#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC) + if ((flags = fcntl(_socket, F_GETFD, 0)) != -1) + fcntl(_socket, F_SETFD, flags | FD_CLOEXEC); +#endif + + if (connect(_socket, (struct sockaddr *)&address.sockaddr, + address.length) != 0) { + int errNo = _OFSocketErrNo(); + + closesocket(_socket); + _socket = OFInvalidSocketHandle; + + @throw [OFConnectUNIXSocketFailedException + exceptionWithPath: path + socket: self + errNo: errNo]; + } +} + +- (void)bindToPath: (OFString *)path +{ + OFSocketAddress address; +#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC) + int flags; +#endif + + if (_socket != OFInvalidSocketHandle) + @throw [OFAlreadyOpenException exceptionWithObject: self]; + + address = OFSocketAddressMakeUNIX(path); + + if ((_socket = socket(address.sockaddr.un.sun_family, + SOCK_SEQPACKET | SOCK_CLOEXEC, 0)) == OFInvalidSocketHandle) + @throw [OFBindUNIXSocketFailedException + exceptionWithPath: path + socket: self + errNo: _OFSocketErrNo()]; + + _canBlock = true; + +#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC) + if ((flags = fcntl(_socket, F_GETFD, 0)) != -1) + fcntl(_socket, F_SETFD, flags | FD_CLOEXEC); +#endif + + if (bind(_socket, (struct sockaddr *)&address.sockaddr, + address.length) != 0) { + int errNo = _OFSocketErrNo(); + + closesocket(_socket); + _socket = OFInvalidSocketHandle; + + @throw [OFBindUNIXSocketFailedException + exceptionWithPath: path + socket: self + errNo: errNo]; + } +} +@end Index: src/OFUNIXStreamSocket.h ================================================================== --- src/OFUNIXStreamSocket.h +++ src/OFUNIXStreamSocket.h @@ -52,20 +52,20 @@ */ @property OF_NULLABLE_PROPERTY (assign, nonatomic) id delegate; /** - * @brief Connects the OFUNIXStreamSocket to the specified destination. + * @brief Connects the OFUNIXStreamSocket to the specified path. * * @param path The path to connect to * @throw OFConnectUNIXSocketFailedException Connecting failed * @throw OFAlreadyOpenException The socket is already connected or bound */ - (void)connectToPath: (OFString *)path; /** - * @brief Binds the socket to the specified host and port. + * @brief Binds the socket to the specified path. * * @param path The path to bind to * @throw OFBindUNIXSocketFailedException Binding failed * @throw OFAlreadyOpenException The socket is already connected or bound */ Index: src/ObjFW.h ================================================================== --- src/ObjFW.h +++ src/ObjFW.h @@ -94,10 +94,11 @@ # ifdef OF_HAVE_SCTP # import "OFSCTPSocket.h" # endif # ifdef OF_HAVE_UNIX_SOCKETS # import "OFUNIXDatagramSocket.h" +# import "OFUNIXSequencedPacketSocket.h" # import "OFUNIXStreamSocket.h" # endif # ifdef OF_HAVE_IPX # import "OFIPXSocket.h" # import "OFSPXSocket.h" Index: tests/Makefile ================================================================== --- tests/Makefile +++ tests/Makefile @@ -99,11 +99,12 @@ SRCS_APPLETALK = OFDDPSocketTests.m SRCS_IPX = OFIPXSocketTests.m \ OFSPXSocketTests.m \ OFSPXStreamSocketTests.m SRCS_SCTP = OFSCTPSocketTests.m -SRCS_UNIX_SOCKETS = OFUNIXDatagramSocketTests.m \ +SRCS_UNIX_SOCKETS = OFUNIXDatagramSocketTests.m \ + OFUNIXSequencedPacketSocketTests.m \ OFUNIXStreamSocketTests.m SRCS_SUBPROCESSES = OFSubprocessTests.m SRCS_THREADS = OFThreadTests.m SRCS_WINDOWS = OFWindowsRegistryKeyTests.m ADDED tests/OFUNIXSequencedPacketSocketTests.m Index: tests/OFUNIXSequencedPacketSocketTests.m ================================================================== --- /dev/null +++ tests/OFUNIXSequencedPacketSocketTests.m @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2008-2024 Jonathan Schleifer + * + * All rights reserved. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3.0 only, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * version 3.0 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3.0 along with this program. If not, see + * . + */ + +#include "config.h" + +#include +#include + +#import "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFUNIXSequencedPacketSocketTests: OTTestCase +@end + +@implementation OFUNIXSequencedPacketSocketTests +- (void)testUNIXSequencedSocket +{ + OFString *path; + OFUNIXSequencedPacketSocket *sockClient, *sockServer, *sockAccepted; + char buffer[5]; + +#if defined(OF_HAVE_FILES) && !defined(OF_IOS) + path = [[OFSystemInfo temporaryDirectoryIRI] + IRIByAppendingPathComponent: [[OFUUID UUID] UUIDString]] + .fileSystemRepresentation; +#else + /* + * We can have sockets, including UNIX sockets, while file support is + * disabled. + * + * We also use this code path for iOS, as the temporaryDirectory:RI is + * too long on the iOS simulator. + */ + path = [OFString stringWithFormat: @"/tmp/%@", + [[OFUUID UUID] UUIDString]]; +#endif + + sockClient = [OFUNIXSequencedPacketSocket socket]; + sockServer = [OFUNIXSequencedPacketSocket socket]; + + @try { + [sockServer bindToPath: path]; + } @catch (OFBindSocketFailedException *e) { + switch (e.errNo) { + case EAFNOSUPPORT: + case EPERM: + OTSkip(@"UNIX sequenced packet sockets unsupported"); + default: + @throw e; + } + } + + @try { + [sockServer listen]; + + [sockClient connectToPath: path]; + + sockAccepted = [sockServer accept]; + [sockAccepted sendBuffer: "Hello" length: 5]; + + OTAssertEqual([sockClient receiveIntoBuffer: buffer + length: 5], 5); + OTAssertEqual(memcmp(buffer, "Hello", 5), 0); + + OTAssertEqual(OFSocketAddressUNIXPath( + sockAccepted.remoteAddress).length, 0); + } @finally { +#ifdef OF_HAVE_FILES + [[OFFileManager defaultManager] removeItemAtPath: path]; +#endif + } +} +@end