diff --git a/headers/private/net/net_datalink.h b/headers/private/net/net_datalink.h index b85108eab2..dec56d568e 100644 --- a/headers/private/net/net_datalink.h +++ b/headers/private/net/net_datalink.h @@ -113,6 +113,7 @@ struct net_address_module_info { bool (*equal_masked_addresses)(const sockaddr *a, const sockaddr *b, const sockaddr *mask); bool (*is_empty_address)(const sockaddr *address, bool checkPort); + bool (*is_same_family)(const sockaddr *address); int32 (*first_mask_bit)(const sockaddr *mask); @@ -142,6 +143,8 @@ struct net_address_module_info { bool (*matches_broadcast_address)(const sockaddr *address, const sockaddr *mask, const sockaddr *broadcastAddr); + + void (*get_loopback_address)(sockaddr *result); }; #endif // NET_DATALINK_H diff --git a/src/add-ons/kernel/network/protocols/ipv4/ipv4_address.cpp b/src/add-ons/kernel/network/protocols/ipv4/ipv4_address.cpp index 24da57890e..76fd3230c9 100644 --- a/src/add-ons/kernel/network/protocols/ipv4/ipv4_address.cpp +++ b/src/add-ons/kernel/network/protocols/ipv4/ipv4_address.cpp @@ -107,6 +107,18 @@ ipv4_is_empty_address(const sockaddr *address, bool checkPort) && (!checkPort || ((sockaddr_in *)address)->sin_port == 0); } +/*! Checks if the given \a address is an IPv4 address. + \return false if \a address is NULL, or with family different from AF_INET + true if it has AF_INET address family +*/ +static bool +ipv4_is_same_family(const sockaddr *address) +{ + if (address == NULL) + return false; + + return address->sa_family == AF_INET; +} /*! Compares the IP-addresses of the two given address structures \a a and \a b. \return true if IP-addresses of \a a and \a b are equal, false if not @@ -477,6 +489,16 @@ ipv4_checksum_address(struct Checksum *checksum, const sockaddr *address) return B_OK; } +static void +ipv4_get_loopback_address(sockaddr *result) +{ + sockaddr_in *resultIn = (sockaddr_in *)result; + memset(resultIn, 0, sizeof(resultIn)); + resultIn->sin_len = sizeof(sockaddr_in); + resultIn->sin_family = AF_INET; + resultIn->sin_addr.s_addr = htonl(INADDR_LOOPBACK); +} + net_address_module_info gIPv4AddressModule = { { @@ -491,6 +513,7 @@ net_address_module_info gIPv4AddressModule = { ipv4_equal_addresses_and_ports, ipv4_equal_masked_addresses, ipv4_is_empty_address, + ipv4_is_same_family, ipv4_first_mask_bit, ipv4_check_mask, ipv4_print_address, @@ -503,5 +526,6 @@ net_address_module_info gIPv4AddressModule = { ipv4_update_to, ipv4_hash_address_pair, ipv4_checksum_address, - NULL // ipv4_matches_broadcast_address, + NULL, // ipv4_matches_broadcast_address, + ipv4_get_loopback_address }; diff --git a/src/add-ons/kernel/network/protocols/l2cap/l2cap_address.cpp b/src/add-ons/kernel/network/protocols/l2cap/l2cap_address.cpp index a931660f0f..c78555e1c2 100644 --- a/src/add-ons/kernel/network/protocols/l2cap/l2cap_address.cpp +++ b/src/add-ons/kernel/network/protocols/l2cap/l2cap_address.cpp @@ -100,6 +100,18 @@ l2cap_is_empty_address(const sockaddr *address, bool checkPort) && (!checkPort || ((sockaddr_l2cap *)address)->l2cap_psm == 0) ); } +/*! Checks if the given \a address is L2CAP address. + \return false if \a address is NULL, or with family different from AF_BLUETOOTH + true if it has AF_BLUETOOTH address family +*/ +static bool +l2cap_is_same_family(const sockaddr *address) +{ + if (address == NULL) + return false; + + return address->sa_family == AF_BLUETOOTH; +} /*! Compares the IP-addresses of the two given address structures \a a and \a b. @@ -401,6 +413,7 @@ net_address_module_info gL2cap4AddressModule = { l2cap_equal_addresses_and_ports, l2cap_equal_masked_addresses, l2cap_is_empty_address, + l2cap_is_same_family, l2cap_first_mask_bit, l2cap_check_mask, l2cap_print_address, @@ -413,5 +426,6 @@ net_address_module_info gL2cap4AddressModule = { l2cap_update_to, l2cap_hash_address_pair, l2cap_checksum_address, - NULL // l2cap_matches_broadcast_address, + NULL, // l2cap_matches_broadcast_address, + NULL // get_loopback_address }; diff --git a/src/add-ons/kernel/network/protocols/tcp/EndpointManager.cpp b/src/add-ons/kernel/network/protocols/tcp/EndpointManager.cpp index b4af19c78e..1739b6323d 100644 --- a/src/add-ons/kernel/network/protocols/tcp/EndpointManager.cpp +++ b/src/add-ons/kernel/network/protocols/tcp/EndpointManager.cpp @@ -362,9 +362,9 @@ EndpointManager::FindConnection(sockaddr* local, sockaddr* peer) status_t EndpointManager::Bind(TCPEndpoint* endpoint, const sockaddr* address) { - // TODO: check the family: - // if (!AddressModule()->is_understandable(address)) - // return EAFNOSUPPORT; + // check the family + if (!AddressModule()->is_same_family(address)) + return EAFNOSUPPORT; WriteLocker locker(fLock); diff --git a/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp b/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp index 6d90d067eb..e9f04b468f 100644 --- a/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp +++ b/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp @@ -584,6 +584,9 @@ TCPEndpoint::Connect(const sockaddr* address) { TRACE("Connect() on address %s", PrintAddress(address)); + if (!AddressModule()->is_same_family(address)) + return EAFNOSUPPORT; + MutexLocker locker(fLock); if (gStackModule->is_restarted_syscall()) { @@ -604,14 +607,13 @@ TCPEndpoint::Connect(const sockaddr* address) } else if (fState != CLOSED) return EINPROGRESS; - // TODO: this is IPv4 specific, and doesn't belong here! // consider destination address INADDR_ANY as INADDR_LOOPBACK - sockaddr_in _address; - if (((sockaddr_in*)address)->sin_addr.s_addr == INADDR_ANY) { - memcpy(&_address, address, sizeof(sockaddr_in)); - _address.sin_len = sizeof(sockaddr_in); - _address.sin_addr.s_addr = INADDR_LOOPBACK; - address = (sockaddr*)&_address; + sockaddr_storage _address; + if (AddressModule()->is_empty_address(address, false)) { + AddressModule()->get_loopback_address((sockaddr *)&_address); + // for IPv4 and IPv6 the port is at the same offset + ((sockaddr_in &)_address).sin_port = ((sockaddr_in *)address)->sin_port; + address = (sockaddr *)&_address; } status_t status = _PrepareSendPath(address); diff --git a/src/add-ons/kernel/network/protocols/udp/udp.cpp b/src/add-ons/kernel/network/protocols/udp/udp.cpp index 034003fcb4..ebc0a15eb0 100644 --- a/src/add-ons/kernel/network/protocols/udp/udp.cpp +++ b/src/add-ons/kernel/network/protocols/udp/udp.cpp @@ -81,7 +81,8 @@ public: status_t Close(); status_t Free(); - status_t SendRoutedData(net_buffer *buffer, net_route *route); + status_t SendRoutedData(net_buffer *buffer, + net_route *route); status_t SendData(net_buffer *buffer); ssize_t BytesAvailable(); @@ -101,8 +102,9 @@ public: private: UdpDomainSupport *fManager; bool fActive; - // an active UdpEndpoint is part of the endpoint - // hash (and it is bound and optionally connected) + // an active UdpEndpoint is part of the + // endpoint hash (and it is bound and optionally + // connected) UdpEndpoint *fLink; }; @@ -127,12 +129,13 @@ struct UdpHashDefinition { size_t Hash(UdpEndpoint *endpoint) const { - return _Mix(endpoint->LocalAddress().HashPair(*endpoint->PeerAddress())); + return _Mix(endpoint->LocalAddress().HashPair( + *endpoint->PeerAddress())); } static size_t _Mix(size_t hash) { - // move the bits into the relevant range (as defined by kNumHashBuckets): + // move the bits into the relevant range (as defined by kNumHashBuckets) return (hash & 0x000007FF) ^ (hash & 0x003FF800) >> 11 ^ (hash & 0xFFC00000UL) >> 22; } @@ -287,6 +290,9 @@ status_t UdpDomainSupport::BindEndpoint(UdpEndpoint *endpoint, const sockaddr *address) { + if (!AddressModule()->is_same_family(address)) + return EAFNOSUPPORT; + MutexLocker _(fLock); if (endpoint->IsActive()) @@ -312,6 +318,19 @@ UdpDomainSupport::ConnectEndpoint(UdpEndpoint *endpoint, // so we reset the peer address: endpoint->PeerAddress().SetToEmpty(); } else { + if (!AddressModule()->is_same_family(address)) + return EAFNOSUPPORT; + + // consider destination address INADDR_ANY as INADDR_LOOPBACK + sockaddr_storage _address; + if (AddressModule()->is_empty_address(address, false)) { + AddressModule()->get_loopback_address((sockaddr *)&_address); + // for IPv4 and IPv6 the port is at the same offset + ((sockaddr_in&)_address).sin_port + = ((sockaddr_in *)address)->sin_port; + address = (sockaddr *)&_address; + } + status_t status = endpoint->PeerAddress().SetTo(address); if (status < B_OK) return status; @@ -398,7 +417,8 @@ UdpDomainSupport::_Bind(UdpEndpoint *endpoint, const sockaddr *address) if (otherEndpoint->LocalAddress().EqualPorts(address)) { // port is already bound, SO_REUSEADDR or SO_REUSEPORT is required: - if ((otherEndpoint->Socket()->options & (SO_REUSEADDR | SO_REUSEPORT)) == 0 + if ((otherEndpoint->Socket()->options + & (SO_REUSEADDR | SO_REUSEPORT)) == 0 || (socketOptions & (SO_REUSEADDR | SO_REUSEPORT)) == 0) return EADDRINUSE; diff --git a/src/add-ons/kernel/network/protocols/unix/UnixAddress.cpp b/src/add-ons/kernel/network/protocols/unix/UnixAddress.cpp index b11fb4ec83..299db4c051 100644 --- a/src/add-ons/kernel/network/protocols/unix/UnixAddress.cpp +++ b/src/add-ons/kernel/network/protocols/unix/UnixAddress.cpp @@ -112,6 +112,14 @@ unix_is_empty_address(const sockaddr *address, bool checkPort) && memcmp(address, &kEmptyAddress, kEmptyAddress.sun_len) == 0; } +static bool +unix_is_same_family(const sockaddr *address) +{ + if (address == NULL) + return false; + + return address->sa_family == AF_UNIX; +} static int32 unix_first_mask_bit(const sockaddr *mask) @@ -290,6 +298,7 @@ net_address_module_info gAddressModule = { unix_equal_addresses_and_ports, unix_equal_masked_addresses, unix_is_empty_address, + unix_is_same_family, unix_first_mask_bit, unix_check_mask, unix_print_address, @@ -302,5 +311,6 @@ net_address_module_info gAddressModule = { unix_update_to, unix_hash_address_pair, unix_checksum_address, - NULL // matches_broadcast_address + NULL, // matches_broadcast_address + NULL // get_loopback_address }; diff --git a/src/tests/kits/net/Jamfile b/src/tests/kits/net/Jamfile index 1e82918593..139ad1ccfd 100644 --- a/src/tests/kits/net/Jamfile +++ b/src/tests/kits/net/Jamfile @@ -12,6 +12,9 @@ SimpleTest udp_server : udp_server.c : $(TARGET_NETWORK_LIBS) ; SimpleTest tcp_server : tcp_server.c : $(TARGET_NETWORK_LIBS) ; SimpleTest tcp_client : tcp_client.c : $(TARGET_NETWORK_LIBS) ; +SimpleTest ipv46_server : ipv46_server.cpp : $(TARGET_NETWORK_LIBS) ; +SimpleTest ipv46_client : ipv46_client.cpp : $(TARGET_NETWORK_LIBS) ; + SimpleTest getpeername : getpeername.cpp : $(TARGET_NETWORK_LIBS) ; SimpleTest tcp_connection_test : tcp_connection_test.cpp diff --git a/src/tests/kits/net/ipv46_client.cpp b/src/tests/kits/net/ipv46_client.cpp new file mode 100644 index 0000000000..22a6ce829f --- /dev/null +++ b/src/tests/kits/net/ipv46_client.cpp @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include +#include +#include + +const unsigned short TEST_PORT = 40000; + +void usage() { + printf("client [tcp|udp] [4|6] [4|6]\n"); + exit(1); +} + +int main(int argc, char *argv[]) { + int socketType = SOCK_DGRAM; + int socketFamily1 = AF_INET; + int socketFamily2 = AF_INET; + + if (argc > 1) { + if (!strcmp(argv[1], "tcp")) { + socketType = SOCK_STREAM; + } else if (!strcmp(argv[1], "udp")) { + socketType = SOCK_DGRAM; + } else { + usage(); + } + } + if (argc > 2) { + switch (atoi(argv[2])) { + case 4: + socketFamily1 = AF_INET; + break; + case 6: + socketFamily1 = AF_INET6; + break; + default: + usage(); + } + } + if (argc > 3) { + switch (atoi(argv[3])) { + case 4: + socketFamily2 = AF_INET; + break; + case 6: + socketFamily2 = AF_INET6; + break; + default: + usage(); + } + } + + int fd = socket(socketFamily1, socketType, 0); + if (fd < 0) { + perror("socket"); + return -1; + } + + sockaddr_storage saddr; + memset(&saddr, 0, sizeof(saddr)); + saddr.ss_family = socketFamily2; + ((sockaddr_in *) &saddr)->sin_port = htons(TEST_PORT); + if (connect(fd, (sockaddr *) &saddr, socketFamily2 == AF_INET ? + sizeof(sockaddr_in) : sizeof(sockaddr_in6)) < 0) { + perror("connect"); + close(fd); + return -1; + } + + const char *buffer = "hello world"; + unsigned len = strlen(buffer); + int ret = send(fd, buffer, len, 0); + if (ret < len) { + if (ret < 0) { + perror("send"); + } else if (ret == 0) { + printf("no data sent!\n"); + } else { + printf("not all data sent!\n"); + } + } else { + printf("send(): success\n"); + } + close(fd); + return 0; +} diff --git a/src/tests/kits/net/ipv46_server.cpp b/src/tests/kits/net/ipv46_server.cpp new file mode 100644 index 0000000000..7ba70098b4 --- /dev/null +++ b/src/tests/kits/net/ipv46_server.cpp @@ -0,0 +1,126 @@ +#include +#include +#include +#include +#include +#include +#include + +const unsigned short TEST_PORT = 40000; + +void usage() { + printf("server [tcp|udp] [4|6] [local-address]\n"); + exit(1); +} + +void recvLoop(int fd) { + for (;;) { + char buffer[1000]; + int ret = recv(fd, buffer, sizeof(buffer) - 1, 0); + if (ret < 0) { + perror("recv"); + exit(-1); + } + if (ret == 0) { + printf("received EOF!\n"); + break; + } else { + buffer[ret] = 0; + printf("received %d bytes: \"%s\"\n", ret, buffer); + } + } +} + +int main(int argc, char *argv[]) { + int socketType = SOCK_DGRAM; + int socketFamily = AF_INET; + if (argc > 1) { + if (!strcmp(argv[1], "tcp")) { + socketType = SOCK_STREAM; + } else if (!strcmp(argv[1], "udp")) { + socketType = SOCK_DGRAM; + } else { + usage(); + } + } + if (argc > 2) { + switch (atoi(argv[2])) { + case 4: + socketFamily = AF_INET; + break; + case 6: + socketFamily = AF_INET6; + break; + default: + usage(); + } + } + + sockaddr_storage localAddress; + memset(&localAddress, 0, sizeof(localAddress)); + localAddress.ss_family = socketFamily; + ((sockaddr_in *) &localAddress)->sin_port = htons(TEST_PORT); + + if (argc > 3) { + do { + void *dstBuffer = &((sockaddr_in *) &localAddress)->sin_addr; + if (inet_pton(AF_INET, argv[3], dstBuffer) == 1) { + printf("using IPv4 local address\n"); + localAddress.ss_family = AF_INET; + break; + } + + dstBuffer = &((sockaddr_in6 *) &localAddress)->sin6_addr; + if (inet_pton(AF_INET6, argv[3], dstBuffer) == 1) { + printf("using IPv6 local address\n"); + localAddress.ss_family = AF_INET6; + break; + } + + usage(); + } while (false); + } + + int fd = socket(socketFamily, socketType, 0); + if (fd < 0) { + perror("socket"); + return -1; + } + + if (bind(fd, (sockaddr *)&localAddress, localAddress.ss_family == AF_INET ? + sizeof(sockaddr_in) : sizeof(sockaddr_in6)) < 0) { + perror("bind"); + return -1; + } + + switch (socketType) { + case SOCK_DGRAM: + for (;;) { + recvLoop(fd); + } + break; + case SOCK_STREAM: + if (listen(fd, 5) < 0) { + perror("listen"); + return 1; + } + for (;;) { + int clientfd = accept(fd, NULL, 0); + if (clientfd < 0) { + perror("accept"); + return 1; + } + printf("TCP server: got some client!\n"); + if (fork() != 0) { + // parent code + close(clientfd); + continue; + } + // child code + close(fd); + recvLoop(clientfd); + exit(0); + } + break; + } +}