kernel/libnetwork: add accept4() from POSIX.1/2024

* add SOCK_NONBLOCK and SOCK_CLOEXEC
* also extends the type parameter on socketpair() and socket()

Change-Id: I73570d5bfb57c2da00c1086149c9f07547ba61ce
Reviewed-on: https://review.haiku-os.org/c/haiku/+/8515
Tested-by: Commit checker robot <[email protected]>
Reviewed-by: waddlesplash <[email protected]>
This commit is contained in:
Jérôme Duval
2024-11-04 17:55:40 +00:00
committed by waddlesplash
parent 966076b273
commit 6beff0d163
10 changed files with 153 additions and 21 deletions
+4
View File
@@ -47,6 +47,9 @@ typedef uint8_t sa_family_t;
#define SOCK_SEQPACKET 5 #define SOCK_SEQPACKET 5
#define SOCK_MISC 255 #define SOCK_MISC 255
#define SOCK_NONBLOCK 0x00040000
#define SOCK_CLOEXEC 0x00080000
/* Socket options for SOL_SOCKET level */ /* Socket options for SOL_SOCKET level */
#define SOL_SOCKET -1 #define SOL_SOCKET -1
@@ -163,6 +166,7 @@ extern "C" {
#endif #endif
int accept(int socket, struct sockaddr *address, socklen_t *_addressLength); int accept(int socket, struct sockaddr *address, socklen_t *_addressLength);
int accept4(int socket, struct sockaddr *address, socklen_t *_addressLength, int flags);
int bind(int socket, const struct sockaddr *address, int bind(int socket, const struct sockaddr *address,
socklen_t addressLength); socklen_t addressLength);
int connect(int socket, const struct sockaddr *address, int connect(int socket, const struct sockaddr *address,
+1 -1
View File
@@ -264,7 +264,7 @@ status_t _user_connect(int socket, const struct sockaddr *address,
socklen_t addressLength); socklen_t addressLength);
status_t _user_listen(int socket, int backlog); status_t _user_listen(int socket, int backlog);
int _user_accept(int socket, struct sockaddr *address, int _user_accept(int socket, struct sockaddr *address,
socklen_t *_addressLength); socklen_t *_addressLength, int flags);
ssize_t _user_recv(int socket, void *data, size_t length, int flags); ssize_t _user_recv(int socket, void *data, size_t length, int flags);
ssize_t _user_recvfrom(int socket, void *data, size_t length, int flags, ssize_t _user_recvfrom(int socket, void *data, size_t length, int flags,
struct sockaddr *address, socklen_t *_addressLength); struct sockaddr *address, socklen_t *_addressLength);
+1 -1
View File
@@ -365,7 +365,7 @@ extern status_t _kern_connect(int socket, const struct sockaddr *address,
socklen_t addressLength); socklen_t addressLength);
extern status_t _kern_listen(int socket, int backlog); extern status_t _kern_listen(int socket, int backlog);
extern int _kern_accept(int socket, struct sockaddr *address, extern int _kern_accept(int socket, struct sockaddr *address,
socklen_t *_addressLength); socklen_t *_addressLength, int flags);
extern ssize_t _kern_recv(int socket, void *data, size_t length, extern ssize_t _kern_recv(int socket, void *data, size_t length,
int flags); int flags);
extern ssize_t _kern_recvfrom(int socket, void *data, size_t length, extern ssize_t _kern_recvfrom(int socket, void *data, size_t length,
+38 -1
View File
@@ -179,7 +179,7 @@ TypeHandlerImpl<const char*>::GetReturnValue(Context &context, uint64 value)
return read_string(context, (void *)value); return read_string(context, (void *)value);
} }
// #pragma mark - enums, flags // #pragma mark - enums, flags, enum_flags
EnumTypeHandler::EnumTypeHandler(const EnumMap &m) : fMap(m) {} EnumTypeHandler::EnumTypeHandler(const EnumMap &m) : fMap(m) {}
@@ -258,6 +258,43 @@ FlagsTypeHandler::RenderValue(Context &context, unsigned int value) const
return context.FormatUnsigned(value); return context.FormatUnsigned(value);
} }
EnumFlagsTypeHandler::EnumFlagsTypeHandler(const EnumMap &m, const FlagsTypeHandler::FlagsList &l)
:
EnumTypeHandler(m), fList(l) {}
string
EnumFlagsTypeHandler::RenderValue(Context &context, unsigned int value) const
{
if (context.GetContents(Context::ENUMERATIONS)) {
string rendered;
FlagsTypeHandler::FlagsList::const_reverse_iterator i = fList.rbegin();
for (; i != fList.rend(); i++) {
if (value == 0)
break;
if ((value & i->value) != i->value)
continue;
if (!rendered.empty())
rendered.insert(0, "|");
rendered.insert(0, i->name);
value &= ~(i->value);
}
EnumMap::const_iterator j = fMap.find(value);
if (j != fMap.end() && j->second != NULL) {
if (!rendered.empty())
rendered.insert(0, "|");
rendered.insert(0, j->second);
}
return rendered;
}
return context.FormatUnsigned(value);
}
TypeHandlerSelector::TypeHandlerSelector(const SelectMap &m, int sibling, TypeHandlerSelector::TypeHandlerSelector(const SelectMap &m, int sibling,
TypeHandler *def) TypeHandler *def)
: fMap(m), fSibling(sibling), fDefault(def) {} : fMap(m), fSibling(sibling), fDefault(def) {}
+15 -5
View File
@@ -35,7 +35,7 @@ public:
virtual string GetReturnValue(Context &, uint64 value) = 0; virtual string GetReturnValue(Context &, uint64 value) = 0;
}; };
class EnumTypeHandler : public TypeHandler { class EnumTypeHandler : virtual public TypeHandler {
public: public:
typedef std::map<int, const char *> EnumMap; typedef std::map<int, const char *> EnumMap;
@@ -44,13 +44,13 @@ public:
string GetParameterValue(Context &c, Parameter *, const void *); string GetParameterValue(Context &c, Parameter *, const void *);
string GetReturnValue(Context &, uint64 value); string GetReturnValue(Context &, uint64 value);
string RenderValue(Context &, unsigned int value) const; virtual string RenderValue(Context &, unsigned int value) const;
private: protected:
const EnumMap &fMap; const EnumMap &fMap;
}; };
class FlagsTypeHandler : public TypeHandler { class FlagsTypeHandler : virtual public TypeHandler {
public: public:
struct FlagInfo { struct FlagInfo {
unsigned int value; unsigned int value;
@@ -63,12 +63,22 @@ public:
string GetParameterValue(Context &c, Parameter *, const void *); string GetParameterValue(Context &c, Parameter *, const void *);
string GetReturnValue(Context &, uint64 value); string GetReturnValue(Context &, uint64 value);
string RenderValue(Context &, unsigned int value) const; virtual string RenderValue(Context &, unsigned int value) const;
private: private:
const FlagsList &fList; const FlagsList &fList;
}; };
class EnumFlagsTypeHandler : public EnumTypeHandler {
public:
EnumFlagsTypeHandler(const EnumMap &, const FlagsTypeHandler::FlagsList &);
string RenderValue(Context &, unsigned int value) const;
private:
const FlagsTypeHandler::FlagsList &fList;
};
// currently limited to select ints // currently limited to select ints
class TypeHandlerSelector : public TypeHandler { class TypeHandlerSelector : public TypeHandler {
public: public:
+17 -2
View File
@@ -79,10 +79,19 @@ static const enum_info kShutdownHow[] = {
}; };
static const FlagsTypeHandler::FlagInfo kSocketFlagInfos[] = {
FLAG_INFO_ENTRY(SOCK_NONBLOCK),
FLAG_INFO_ENTRY(SOCK_CLOEXEC),
{ 0, NULL }
};
static FlagsTypeHandler::FlagsList kRecvFlags; static FlagsTypeHandler::FlagsList kRecvFlags;
static EnumTypeHandler::EnumMap kSocketFamilyMap; static EnumTypeHandler::EnumMap kSocketFamilyMap;
static EnumTypeHandler::EnumMap kSocketTypeMap; static EnumTypeHandler::EnumMap kSocketTypeMap;
static EnumTypeHandler::EnumMap kShutdownHowMap; static EnumTypeHandler::EnumMap kShutdownHowMap;
static FlagsTypeHandler::FlagsList kSocketFlags;
void void
@@ -100,6 +109,9 @@ patch_network()
for (int i = 0; kShutdownHow[i].name != NULL; i++) { for (int i = 0; kShutdownHow[i].name != NULL; i++) {
kShutdownHowMap[kShutdownHow[i].index] = kShutdownHow[i].name; kShutdownHowMap[kShutdownHow[i].index] = kShutdownHow[i].name;
} }
for (int i = 0; kSocketFlagInfos[i].name != NULL; i++) {
kSocketFlags.push_back(kSocketFlagInfos[i]);
}
Syscall *recv = get_syscall("_kern_recv"); Syscall *recv = get_syscall("_kern_recv");
recv->GetParameter("flags")->SetHandler(new FlagsTypeHandler(kRecvFlags)); recv->GetParameter("flags")->SetHandler(new FlagsTypeHandler(kRecvFlags));
@@ -118,7 +130,7 @@ patch_network()
socket->GetParameter("family")->SetHandler( socket->GetParameter("family")->SetHandler(
new EnumTypeHandler(kSocketFamilyMap)); new EnumTypeHandler(kSocketFamilyMap));
socket->GetParameter("type")->SetHandler( socket->GetParameter("type")->SetHandler(
new EnumTypeHandler(kSocketTypeMap)); new EnumFlagsTypeHandler(kSocketTypeMap, kSocketFlags));
Syscall *shutdown = get_syscall("_kern_shutdown_socket"); Syscall *shutdown = get_syscall("_kern_shutdown_socket");
shutdown->GetParameter("how")->SetHandler( shutdown->GetParameter("how")->SetHandler(
@@ -130,5 +142,8 @@ patch_network()
socketPair->GetParameter("family")->SetHandler( socketPair->GetParameter("family")->SetHandler(
new EnumTypeHandler(kSocketFamilyMap)); new EnumTypeHandler(kSocketFamilyMap));
socketPair->GetParameter("type")->SetHandler( socketPair->GetParameter("type")->SetHandler(
new EnumTypeHandler(kSocketTypeMap)); new EnumFlagsTypeHandler(kSocketTypeMap, kSocketFlags));
Syscall *accept = get_syscall("_kern_accept");
accept->GetParameter("flags")->SetHandler(new FlagsTypeHandler(kSocketFlags));
} }
+37 -10
View File
@@ -358,7 +358,7 @@ get_socket_descriptor(int fd, bool kernel, file_descriptor*& descriptor)
static int static int
create_socket_fd(net_socket* socket, bool kernel) create_socket_fd(net_socket* socket, int flags, bool kernel)
{ {
// Get the socket's non-blocking flag, so we can set the respective // Get the socket's non-blocking flag, so we can set the respective
// open mode flag. // open mode flag.
@@ -368,6 +368,11 @@ create_socket_fd(net_socket* socket, bool kernel)
SO_NONBLOCK, &nonBlock, &nonBlockLen); SO_NONBLOCK, &nonBlock, &nonBlockLen);
if (error != B_OK) if (error != B_OK)
return error; return error;
int oflags = 0;
if ((flags & SOCK_CLOEXEC) != 0)
oflags |= O_CLOEXEC;
if ((flags & SOCK_NONBLOCK) != 0 || nonBlock)
oflags |= O_NONBLOCK;
// allocate a file descriptor // allocate a file descriptor
file_descriptor* descriptor = alloc_fd(); file_descriptor* descriptor = alloc_fd();
@@ -377,15 +382,20 @@ create_socket_fd(net_socket* socket, bool kernel)
// init it // init it
descriptor->ops = &sSocketFDOps; descriptor->ops = &sSocketFDOps;
descriptor->cookie = socket; descriptor->cookie = socket;
descriptor->open_mode = O_RDWR | (nonBlock ? O_NONBLOCK : 0); descriptor->open_mode = O_RDWR | oflags;
// publish it // publish it
int fd = new_fd(get_current_io_context(kernel), descriptor); io_context* context = get_current_io_context(kernel);
int fd = new_fd(context, descriptor);
if (fd < 0) { if (fd < 0) {
descriptor->ops = NULL; descriptor->ops = NULL;
put_fd(descriptor); put_fd(descriptor);
} }
mutex_lock(&context->io_mutex);
fd_set_close_on_exec(context, fd, (oflags & O_CLOEXEC) != 0);
mutex_unlock(&context->io_mutex);
return fd; return fd;
} }
@@ -399,6 +409,9 @@ common_socket(int family, int type, int protocol, bool kernel)
if (!get_stack_interface_module()) if (!get_stack_interface_module())
return B_UNSUPPORTED; return B_UNSUPPORTED;
int sflags = type & (SOCK_CLOEXEC | SOCK_NONBLOCK);
type &= ~(SOCK_CLOEXEC | SOCK_NONBLOCK);
// create the socket // create the socket
net_socket* socket; net_socket* socket;
status_t error = sStackInterface->open(family, type, protocol, &socket); status_t error = sStackInterface->open(family, type, protocol, &socket);
@@ -408,7 +421,7 @@ common_socket(int family, int type, int protocol, bool kernel)
} }
// allocate the FD // allocate the FD
int fd = create_socket_fd(socket, kernel); int fd = create_socket_fd(socket, sflags, kernel);
if (fd < 0) { if (fd < 0) {
sStackInterface->close(socket); sStackInterface->close(socket);
sStackInterface->free(socket); sStackInterface->free(socket);
@@ -467,13 +480,16 @@ common_listen(int fd, int backlog, bool kernel)
static int static int
common_accept(int fd, struct sockaddr *address, socklen_t *_addressLength, common_accept(int fd, struct sockaddr *address, socklen_t *_addressLength, int flags,
bool kernel) bool kernel)
{ {
file_descriptor* descriptor; file_descriptor* descriptor;
GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor); GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor);
FileDescriptorPutter _(descriptor); FileDescriptorPutter _(descriptor);
if ((flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) != 0)
RETURN_AND_SET_ERRNO(B_BAD_VALUE);
net_socket* acceptedSocket; net_socket* acceptedSocket;
status_t error = sStackInterface->accept(FD_SOCKET(descriptor), address, status_t error = sStackInterface->accept(FD_SOCKET(descriptor), address,
_addressLength, &acceptedSocket); _addressLength, &acceptedSocket);
@@ -481,7 +497,7 @@ common_accept(int fd, struct sockaddr *address, socklen_t *_addressLength,
return error; return error;
// allocate the FD // allocate the FD
int acceptedFD = create_socket_fd(acceptedSocket, kernel); int acceptedFD = create_socket_fd(acceptedSocket, flags, kernel);
if (acceptedFD < 0) { if (acceptedFD < 0) {
sStackInterface->close(acceptedSocket); sStackInterface->close(acceptedSocket);
sStackInterface->free(acceptedSocket); sStackInterface->free(acceptedSocket);
@@ -633,6 +649,9 @@ common_socketpair(int family, int type, int protocol, int fds[2], bool kernel)
if (!get_stack_interface_module()) if (!get_stack_interface_module())
return B_UNSUPPORTED; return B_UNSUPPORTED;
int sflags = type & (SOCK_CLOEXEC | SOCK_NONBLOCK);
type &= ~(SOCK_CLOEXEC | SOCK_NONBLOCK);
net_socket* sockets[2]; net_socket* sockets[2];
status_t error = sStackInterface->socketpair(family, type, protocol, status_t error = sStackInterface->socketpair(family, type, protocol,
sockets); sockets);
@@ -643,7 +662,7 @@ common_socketpair(int family, int type, int protocol, int fds[2], bool kernel)
// allocate the FDs // allocate the FDs
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
fds[i] = create_socket_fd(sockets[i], kernel); fds[i] = create_socket_fd(sockets[i], sflags, kernel);
if (fds[i] < 0) { if (fds[i] < 0) {
sStackInterface->close(sockets[i]); sStackInterface->close(sockets[i]);
sStackInterface->free(sockets[i]); sStackInterface->free(sockets[i]);
@@ -719,7 +738,15 @@ int
accept(int socket, struct sockaddr *address, socklen_t *_addressLength) accept(int socket, struct sockaddr *address, socklen_t *_addressLength)
{ {
SyscallFlagUnsetter _; SyscallFlagUnsetter _;
RETURN_AND_SET_ERRNO(common_accept(socket, address, _addressLength, true)); RETURN_AND_SET_ERRNO(common_accept(socket, address, _addressLength, 0, true));
}
int
accept4(int socket, struct sockaddr *address, socklen_t *_addressLength, int flags)
{
SyscallFlagUnsetter _;
RETURN_AND_SET_ERRNO(common_accept(socket, address, _addressLength, flags, true));
} }
@@ -907,7 +934,7 @@ _user_listen(int socket, int backlog)
int int
_user_accept(int socket, struct sockaddr *userAddress, _user_accept(int socket, struct sockaddr *userAddress,
socklen_t *_addressLength) socklen_t *_addressLength, int flags)
{ {
// check parameters // check parameters
socklen_t addressLength = 0; socklen_t addressLength = 0;
@@ -922,7 +949,7 @@ _user_accept(int socket, struct sockaddr *userAddress,
char address[MAX_SOCKET_ADDRESS_LENGTH]; char address[MAX_SOCKET_ADDRESS_LENGTH];
socklen_t userAddressBufferSize = addressLength; socklen_t userAddressBufferSize = addressLength;
result = common_accept(socket, result = common_accept(socket,
userAddress != NULL ? (sockaddr*)address : NULL, &addressLength, false); userAddress != NULL ? (sockaddr*)address : NULL, &addressLength, flags, false);
// copy address size and address back to userland // copy address size and address back to userland
if (copy_address_to_userland(address, addressLength, userAddress, if (copy_address_to_userland(address, addressLength, userAddress,
+8 -1
View File
@@ -191,6 +191,13 @@ listen(int socket, int backlog)
extern "C" int extern "C" int
accept(int socket, struct sockaddr *_address, socklen_t *_addressLength) accept(int socket, struct sockaddr *_address, socklen_t *_addressLength)
{
return accept4(socket, _address, _addressLength, 0);
}
extern "C" int
accept4(int socket, struct sockaddr *_address, socklen_t *_addressLength, int flags)
{ {
bool r5compatible = check_r5_compatibility(); bool r5compatible = check_r5_compatibility();
struct sockaddr haikuAddr; struct sockaddr haikuAddr;
@@ -206,7 +213,7 @@ accept(int socket, struct sockaddr *_address, socklen_t *_addressLength)
addressLength = _addressLength ? *_addressLength : 0; addressLength = _addressLength ? *_addressLength : 0;
} }
int acceptSocket = _kern_accept(socket, address, &addressLength); int acceptSocket = _kern_accept(socket, address, &addressLength, flags);
pthread_testcancel(); pthread_testcancel();
+3
View File
@@ -25,6 +25,9 @@ SimpleTest unix_send_test : unix_send_test.c : $(TARGET_NETWORK_LIBS) ;
SimpleTest tcp_connection_test : tcp_connection_test.cpp SimpleTest tcp_connection_test : tcp_connection_test.cpp
: $(TARGET_NETWORK_LIBS) ; : $(TARGET_NETWORK_LIBS) ;
SimpleTest test4 : test4.c
: $(TARGET_NETWORK_LIBS) ;
SubInclude HAIKU_TOP src tests system network icmp ; SubInclude HAIKU_TOP src tests system network icmp ;
SubInclude HAIKU_TOP src tests system network ipv6 ; SubInclude HAIKU_TOP src tests system network ipv6 ;
SubInclude HAIKU_TOP src tests system network multicast ; SubInclude HAIKU_TOP src tests system network multicast ;
+29
View File
@@ -0,0 +1,29 @@
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
int main(int argc, char **argv)
{
int sock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sock < 0) {
printf("Failed! Socket could not be created.\n");
return -1;
}
int flags = fcntl(sock, F_GETFD);
int ret = 0;
if ((flags & FD_CLOEXEC) == 0) {
printf("Failed! Descriptor flag not found.\n");
ret = -1;
}
close(sock);
printf("Test complete.\n");
return ret;
}