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!


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@36192 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Oliver Tappe
2010-04-12 18:39:34 +00:00
parent 5608589f03
commit 56f097ebd9
10 changed files with 309 additions and 19 deletions
+3
View File
@@ -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
@@ -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
};
@@ -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
};
@@ -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);
@@ -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);
@@ -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;
@@ -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
};
+3
View File
@@ -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
+88
View File
@@ -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;
}
+126
View File
@@ -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;
}
}