195 changes: 103 additions & 92 deletions src/socket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/

#include "socket.h"

#include "jmutexautolock.h"
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
// Without this some of the network functions are not found on mingw
Expand Down Expand Up @@ -78,117 +78,138 @@ void sockets_cleanup()
#endif
}

Address::Address()
Address::Address() : m_address(NULL)
{
m_address = 0;
m_port = 0;
assert(m_address==NULL);
}

Address::~Address() {
JMutexAutoLock context(m_lock);
// if(m_address != NULL) freeaddrinfo(m_address);
m_address = NULL;
}

Address::Address(unsigned int address, unsigned short port)
#define ResolveAssert(what,message) { int e = what; if(e != 0) throw ResolveError(message); }

Address::Address(const char* node, const char* service) : m_address(NULL)
{
m_address = address;
m_port = port;
JMutexAutoLock context(m_lock);
assert(m_address==NULL);
ResolveAssert(getaddrinfo(node,service,NULL,&m_address),node);
}

Address::Address(unsigned int a, unsigned int b,
unsigned int c, unsigned int d,
unsigned short port)
unsigned short port) : m_address(NULL)
{
m_address = (a<<24) | (b<<16) | ( c<<8) | d;
m_port = port;
assert(m_address==NULL);
setAddress(a,b,c,d,port);
}

bool Address::operator==(Address &address)
{
return (m_address == address.m_address
&& m_port == address.m_port);
return (serializeString() == address.serializeString());
}

bool Address::operator!=(Address &address)
{
return !(*this == address);
}

void Address::Resolve(const char *name)
{
struct addrinfo *resolved;
int e = getaddrinfo(name, NULL, NULL, &resolved);
if(e != 0)
throw ResolveError("");
/*
FIXME: This is an ugly hack; change the whole class
to store the address as sockaddr
*/
struct sockaddr_in *t = (struct sockaddr_in*)resolved->ai_addr;
m_address = ntohl(t->sin_addr.s_addr);
freeaddrinfo(resolved);
}

std::string Address::serializeString() const
{
unsigned int a, b, c, d;
a = (m_address & 0xFF000000)>>24;
b = (m_address & 0x00FF0000)>>16;
c = (m_address & 0x0000FF00)>>8;
d = (m_address & 0x000000FF);
return itos(a)+"."+itos(b)+"."+itos(c)+"."+itos(d);
assert(m_address);
char node[0x100];
char service[0x10];
int ret = getnameinfo(m_address->ai_addr,m_address->ai_addrlen,
node,0x100,
service,0x10,
NI_NUMERICHOST | NI_NUMERICSERV );
if(ret!=0) throw ResolveError("Could not get the (readable) name of an address! plz debug");
return std::string(node)+","+service;
}

unsigned int Address::getAddress() const
void Address::setAddress(const char* node, const char* service)
{
return m_address;
}

unsigned short Address::getPort() const
{
return m_port;
}

void Address::setAddress(unsigned int address)
{
m_address = address;
JMutexAutoLock context(m_lock);
assert(m_address == NULL);
ResolveAssert(getaddrinfo(node,service,NULL,&m_address),node);
}

void Address::setAddress(unsigned int a, unsigned int b,
unsigned int c, unsigned int d)
unsigned int c, unsigned int d,
unsigned short port)
{
m_address = (a<<24) | (b<<16) | ( c<<8) | d;
}

void Address::setPort(unsigned short port)
{
m_port = port;
JMutexAutoLock context(m_lock);
assert(m_address==NULL);
char node[0x100];
char service[0x10];
struct sockaddr_in fakeaddr;
memset(&fakeaddr,0,sizeof(fakeaddr));
fakeaddr.sin_family = AF_INET;
fakeaddr.sin_addr.s_addr = htonl((a<<24) | (b<<16) | ( c<<8) | d);
fakeaddr.sin_port = htons(port);
assert(0==getnameinfo((struct sockaddr*)&fakeaddr,sizeof(fakeaddr),
node,0x100,
service,0x10,
NI_NUMERICHOST | NI_NUMERICSERV));
struct addrinfo hints = {};
hints.ai_flags = AI_NUMERICHOST;
hints.ai_socktype = SOCK_DGRAM;
ResolveAssert(getaddrinfo(node,service,&hints,&m_address),node);
assert(m_address);
}

void Address::print(std::ostream *s) const
{
(*s)<<((m_address>>24)&0xff)<<"."
<<((m_address>>16)&0xff)<<"."
<<((m_address>>8)&0xff)<<"."
<<((m_address>>0)&0xff)<<":"
<<m_port;
(*s)<<serializeString();
}

void Address::print() const
{
print(&dstream);
}

UDPSocket::UDPSocket()
UDPSocket::UDPSocket(): m_handle(-1) {}
void UDPSocket::Bind(const char* service)
{
if(g_sockets_initialized == false)
std::cerr << "Bind " << service << std::endl;
if(g_sockets_initialized == false)
throw SocketException("Sockets not initialized");

m_handle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);


struct addrinfo *address = NULL;
struct addrinfo hints = {};
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
hints.ai_family = AF_UNSPEC;
hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST | AI_NUMERICSERV | AI_V4MAPPED;

ResolveAssert(getaddrinfo(NULL,service,&hints,&address),"ready to listen");

m_handle = socket(address->ai_family,address->ai_socktype,address->ai_protocol);

if(DP)
dstream<<DPS<<"UDPSocket("<<(int)m_handle<<")::UDPSocket()"<<std::endl;

if(m_handle <= 0)
{
throw SocketException("Failed to create socket");
}

struct addrinfo* cur = address;
while(cur) {
if(bind(m_handle, cur->ai_addr, cur->ai_addrlen) < 0)
{
#ifndef DISABLE_ERRNO
dstream<<(int)m_handle<<": Bind failed: "<<strerror(errno)<<cur->ai_protocol<<std::endl;
#endif
throw SocketException("Failed to bind socket");
}
cur = cur->ai_next;
break;
}
/*#ifdef _WIN32
DWORD nonblocking = 0;
if(ioctlsocket(m_handle, FIONBIO, &nonblocking) != 0)
Expand All @@ -203,39 +224,29 @@ UDPSocket::UDPSocket()
}
#endif*/

setTimeoutMs(0);

setTimeoutMs(0);
freeaddrinfo(address);
std::cerr << "BEEP" << std::endl;
}

UDPSocket::~UDPSocket()
{
if(DP)
void UDPSocket::Close() {
if(m_handle < 0) return;
if(DP)
dstream<<DPS<<"UDPSocket("<<(int)m_handle<<")::~UDPSocket()"<<std::endl;

#ifdef _WIN32
closesocket(m_handle);
#else
close(m_handle);
#endif
m_handle = -1;
}

void UDPSocket::Bind(unsigned short port)
{
if(DP)
dstream<<DPS<<"UDPSocket("<<(int)m_handle
<<")::Bind(): port="<<port<<std::endl;

sockaddr_in address;
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(port);

if(bind(m_handle, (const sockaddr*)&address, sizeof(sockaddr_in)) < 0)
{
#ifndef DISABLE_ERRNO
dstream<<(int)m_handle<<": Bind failed: "<<strerror(errno)<<std::endl;
#endif
throw SocketException("Failed to bind socket");
}
UDPSocket::~UDPSocket()
{
Close();
}

void UDPSocket::Send(const Address & destination, const void * data, int size)
Expand Down Expand Up @@ -274,13 +285,8 @@ void UDPSocket::Send(const Address & destination, const void * data, int size)
if(dumping_packet)
return;

sockaddr_in address;
address.sin_family = AF_INET;
address.sin_addr.s_addr = htonl(destination.getAddress());
address.sin_port = htons(destination.getPort());

int sent = sendto(m_handle, (const char*)data, size,
0, (sockaddr*)&address, sizeof(sockaddr_in));
0, destination.getAddress(), destination.getLength());

if(sent != size)
{
Expand All @@ -295,7 +301,8 @@ int UDPSocket::Receive(Address & sender, void * data, int size)
return -1;
}

sockaddr_in address;
struct sockaddr_storage address;
memset(&address,0,sizeof(address));
socklen_t address_len = sizeof(address);

int received = recvfrom(m_handle, (char*)data,
Expand All @@ -304,10 +311,14 @@ int UDPSocket::Receive(Address & sender, void * data, int size)
if(received < 0)
return -1;

unsigned int address_ip = ntohl(address.sin_addr.s_addr);
unsigned int address_port = ntohs(address.sin_port);
char node[0x100];
char service[0x10];
assert(0==getnameinfo((struct sockaddr*)&address,address_len,
node,0x100,
service,0x10,
NI_NUMERICHOST|NI_NUMERICSERV));

sender = Address(address_ip, address_port);
sender.setCPlusPlusSucks(Address(node,service));

if(DP){
//dstream<<DPS<<"UDPSocket("<<(int)m_handle<<")::Receive(): sender=";
Expand Down
31 changes: 21 additions & 10 deletions src/socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,

#include <ostream>
#include "exceptions.h"
#include "jmutex.h"

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
// ...because the private keyword is useless!

extern bool socket_enable_debug_output;

Expand Down Expand Up @@ -59,34 +65,39 @@ class Address
{
public:
Address();
Address(unsigned int address, unsigned short port);
~Address();
Address(const char* node, const char* service);
// XXX: this hack is only used in test.cpp
Address(unsigned int a, unsigned int b,
unsigned int c, unsigned int d,
unsigned short port);
bool operator==(Address &address);
bool operator!=(Address &address);
void setCPlusPlusSucks(Address address) {
*this = address;
}
void Resolve(const char *name);
unsigned int getAddress() const;
unsigned short getPort() const;
void setAddress(unsigned int address);
struct sockaddr* getAddress() const { return m_address->ai_addr; }
socklen_t getLength() const { return m_address->ai_addrlen; }
void setAddress(const char* node, const char* service);
void setAddress(unsigned int a, unsigned int b,
unsigned int c, unsigned int d);
void setPort(unsigned short port);
unsigned int c, unsigned int d,
unsigned short port);
void print(std::ostream *s) const;
void print() const;
std::string serializeString() const;
private:
unsigned int m_address;
unsigned short m_port;
struct addrinfo* m_address;
JMutex m_lock;
};

class UDPSocket
{
public:
UDPSocket();
~UDPSocket();
void Bind(unsigned short port);
//void Close();
void Bind(const char* service = NULL);
void Close();
//bool IsOpen();
void Send(const Address & destination, const void * data, int size);
// Returns -1 if there is no data
Expand Down
15 changes: 9 additions & 6 deletions src/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ struct TestCompress: public TestBase
<<os_decompressed.str().size()<<std::endl;
std::string str_decompressed = os_decompressed.str();
UTEST(str_decompressed.size() == data_in.size(), "Output size not"
" equal (output: %i, input: %i)",
" equal (output: %li, input: %li)",
str_decompressed.size(), data_in.size());
for(u32 i=0; i<size && i<str_decompressed.size(); i++){
UTEST(str_decompressed[i] == data_in[i],
Expand Down Expand Up @@ -1291,12 +1291,13 @@ struct TestSocket: public TestBase
{
void Run()
{
const int port = 30003;
const char* service = "30003";
UDPSocket socket;
socket.Bind(port);
socket.Bind(service);

const char sendbuffer[] = "hello world!";
socket.Send(Address(127,0,0,1,port), sendbuffer, sizeof(sendbuffer));
Address receiver("127.0.0.1",service);
socket.Send(receiver, sendbuffer, sizeof(sendbuffer));

sleep_ms(50);

Expand All @@ -1309,9 +1310,11 @@ struct TestSocket: public TestBase
if(bytes_read < 0)
break;
}
socket.Close();
//FIXME: This fails on some systems
UASSERT(strncmp(sendbuffer, rcvbuffer, sizeof(sendbuffer))==0);
UASSERT(sender.getAddress() == Address(127,0,0,1, 0).getAddress());
Address expected(127,0,0,1,30003);
UASSERT(sender == expected);
}
};

Expand Down Expand Up @@ -1412,7 +1415,7 @@ struct TestConnection: public TestBase

infostream<<"** Creating server Connection"<<std::endl;
con::Connection server(proto_id, 512, 5.0, &hand_server);
server.Serve(30001);
server.Serve("30001");

infostream<<"** Creating client Connection"<<std::endl;
con::Connection client(proto_id, 512, 5.0, &hand_client);
Expand Down