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:
@@ -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
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user