Author: zooey Date: 2010-04-12 20:39:34 +0200 (Mon, 12 Apr 2010) New Revision: 36192 Changeset: http://dev.haiku-os.org/changeset/36192/haiku Ticket: http://dev.haiku-os.org/ticket/5716 Added: haiku/trunk/src/tests/kits/net/ipv46_client.cpp haiku/trunk/src/tests/kits/net/ipv46_server.cpp Modified: haiku/trunk/headers/private/net/net_datalink.h haiku/trunk/src/add-ons/kernel/network/protocols/ipv4/ipv4_address.cpp haiku/trunk/src/add-ons/kernel/network/protocols/l2cap/l2cap_address.cpp haiku/trunk/src/add-ons/kernel/network/protocols/tcp/EndpointManager.cpp haiku/trunk/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp haiku/trunk/src/add-ons/kernel/network/protocols/udp/udp.cpp haiku/trunk/src/add-ons/kernel/network/protocols/unix/UnixAddress.cpp haiku/trunk/src/tests/kits/net/Jamfile Log: Applying patch by Atis Elsts: * fix connecting to INADDR_ANY work for tcp (effectively will connect to INADDR_LOOPBACK) * add same behaviour to udp * move some ipv4-specific code out of tcp into ipv4 address module * bind() and connect() now reject addresses from non-matching families * myself: minor cleanup in udp.cpp with respect to 80 chars limit Closes #5716 - many thanks! Modified: haiku/trunk/headers/private/net/net_datalink.h =================================================================== --- haiku/trunk/headers/private/net/net_datalink.h 2010-04-12 18:21:59 UTC (rev 36191) +++ haiku/trunk/headers/private/net/net_datalink.h 2010-04-12 18:39:34 UTC (rev 36192) @@ -113,6 +113,7 @@ 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 @@ bool (*matches_broadcast_address)(const sockaddr *address, const sockaddr *mask, const sockaddr *broadcastAddr); + + void (*get_loopback_address)(sockaddr *result); }; #endif // NET_DATALINK_H Modified: haiku/trunk/src/add-ons/kernel/network/protocols/ipv4/ipv4_address.cpp =================================================================== --- haiku/trunk/src/add-ons/kernel/network/protocols/ipv4/ipv4_address.cpp 2010-04-12 18:21:59 UTC (rev 36191) +++ haiku/trunk/src/add-ons/kernel/network/protocols/ipv4/ipv4_address.cpp 2010-04-12 18:39:34 UTC (rev 36192) @@ -107,7 +107,19 @@ && (!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,7 +489,17 @@ 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 = { { NULL, @@ -491,6 +513,7 @@ 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 @@ ipv4_update_to, ipv4_hash_address_pair, ipv4_checksum_address, - NULL // ipv4_matches_broadcast_address, + NULL, // ipv4_matches_broadcast_address, + ipv4_get_loopback_address }; Modified: haiku/trunk/src/add-ons/kernel/network/protocols/l2cap/l2cap_address.cpp =================================================================== --- haiku/trunk/src/add-ons/kernel/network/protocols/l2cap/l2cap_address.cpp 2010-04-12 18:21:59 UTC (rev 36191) +++ haiku/trunk/src/add-ons/kernel/network/protocols/l2cap/l2cap_address.cpp 2010-04-12 18:39:34 UTC (rev 36192) @@ -100,6 +100,18 @@ && (!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 @@ 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 @@ l2cap_update_to, l2cap_hash_address_pair, l2cap_checksum_address, - NULL // l2cap_matches_broadcast_address, + NULL, // l2cap_matches_broadcast_address, + NULL // get_loopback_address }; Modified: haiku/trunk/src/add-ons/kernel/network/protocols/tcp/EndpointManager.cpp =================================================================== --- haiku/trunk/src/add-ons/kernel/network/protocols/tcp/EndpointManager.cpp 2010-04-12 18:21:59 UTC (rev 36191) +++ haiku/trunk/src/add-ons/kernel/network/protocols/tcp/EndpointManager.cpp 2010-04-12 18:39:34 UTC (rev 36192) @@ -362,9 +362,9 @@ 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); Modified: haiku/trunk/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp =================================================================== --- haiku/trunk/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp 2010-04-12 18:21:59 UTC (rev 36191) +++ haiku/trunk/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp 2010-04-12 18:39:34 UTC (rev 36192) @@ -584,6 +584,9 @@ { 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 @@ } 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); Modified: haiku/trunk/src/add-ons/kernel/network/protocols/udp/udp.cpp =================================================================== --- haiku/trunk/src/add-ons/kernel/network/protocols/udp/udp.cpp 2010-04-12 18:21:59 UTC (rev 36191) +++ haiku/trunk/src/add-ons/kernel/network/protocols/udp/udp.cpp 2010-04-12 18:39:34 UTC (rev 36192) @@ -81,7 +81,8 @@ 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 @@ 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 @@ 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 @@ UdpDomainSupport::BindEndpoint(UdpEndpoint *endpoint, const sockaddr *address) { + if (!AddressModule()->is_same_family(address)) + return EAFNOSUPPORT; + MutexLocker _(fLock); if (endpoint->IsActive()) @@ -312,6 +318,19 @@ // 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 @@ 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; Modified: haiku/trunk/src/add-ons/kernel/network/protocols/unix/UnixAddress.cpp =================================================================== --- haiku/trunk/src/add-ons/kernel/network/protocols/unix/UnixAddress.cpp 2010-04-12 18:21:59 UTC (rev 36191) +++ haiku/trunk/src/add-ons/kernel/network/protocols/unix/UnixAddress.cpp 2010-04-12 18:39:34 UTC (rev 36192) @@ -112,6 +112,14 @@ && 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 @@ 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 @@ unix_update_to, unix_hash_address_pair, unix_checksum_address, - NULL // matches_broadcast_address + NULL, // matches_broadcast_address + NULL // get_loopback_address }; Modified: haiku/trunk/src/tests/kits/net/Jamfile =================================================================== --- haiku/trunk/src/tests/kits/net/Jamfile 2010-04-12 18:21:59 UTC (rev 36191) +++ haiku/trunk/src/tests/kits/net/Jamfile 2010-04-12 18:39:34 UTC (rev 36192) @@ -12,6 +12,9 @@ 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 Added: haiku/trunk/src/tests/kits/net/ipv46_client.cpp =================================================================== --- haiku/trunk/src/tests/kits/net/ipv46_client.cpp (rev 0) +++ haiku/trunk/src/tests/kits/net/ipv46_client.cpp 2010-04-12 18:39:34 UTC (rev 36192) @@ -0,0 +1,88 @@ +#include <unistd.h> +#include <memory.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> + +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; +} Added: haiku/trunk/src/tests/kits/net/ipv46_server.cpp =================================================================== --- haiku/trunk/src/tests/kits/net/ipv46_server.cpp (rev 0) +++ haiku/trunk/src/tests/kits/net/ipv46_server.cpp 2010-04-12 18:39:34 UTC (rev 36192) @@ -0,0 +1,126 @@ +#include <unistd.h> +#include <memory.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> + +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; + } +}