Initialize with OpenTTD 1.9.3
This commit is contained in:
437
src/network/core/address.cpp
Normal file
437
src/network/core/address.cpp
Normal file
@@ -0,0 +1,437 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file core/address.cpp Implementation of the address. */
|
||||
|
||||
#include "../../stdafx.h"
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
#include "address.h"
|
||||
#include "../../debug.h"
|
||||
|
||||
#include "../../safeguards.h"
|
||||
|
||||
/**
|
||||
* Get the hostname; in case it wasn't given the
|
||||
* IPv4 dotted representation is given.
|
||||
* @return the hostname
|
||||
*/
|
||||
const char *NetworkAddress::GetHostname()
|
||||
{
|
||||
if (StrEmpty(this->hostname) && this->address.ss_family != AF_UNSPEC) {
|
||||
assert(this->address_length != 0);
|
||||
getnameinfo((struct sockaddr *)&this->address, this->address_length, this->hostname, sizeof(this->hostname), NULL, 0, NI_NUMERICHOST);
|
||||
}
|
||||
return this->hostname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the port.
|
||||
* @return the port.
|
||||
*/
|
||||
uint16 NetworkAddress::GetPort() const
|
||||
{
|
||||
switch (this->address.ss_family) {
|
||||
case AF_UNSPEC:
|
||||
case AF_INET:
|
||||
return ntohs(((const struct sockaddr_in *)&this->address)->sin_port);
|
||||
|
||||
case AF_INET6:
|
||||
return ntohs(((const struct sockaddr_in6 *)&this->address)->sin6_port);
|
||||
|
||||
default:
|
||||
NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the port.
|
||||
* @param port set the port number.
|
||||
*/
|
||||
void NetworkAddress::SetPort(uint16 port)
|
||||
{
|
||||
switch (this->address.ss_family) {
|
||||
case AF_UNSPEC:
|
||||
case AF_INET:
|
||||
((struct sockaddr_in*)&this->address)->sin_port = htons(port);
|
||||
break;
|
||||
|
||||
case AF_INET6:
|
||||
((struct sockaddr_in6*)&this->address)->sin6_port = htons(port);
|
||||
break;
|
||||
|
||||
default:
|
||||
NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the address as a string, e.g. 127.0.0.1:12345.
|
||||
* @param buffer the buffer to write to
|
||||
* @param last the last element in the buffer
|
||||
* @param with_family whether to add the family (e.g. IPvX).
|
||||
*/
|
||||
void NetworkAddress::GetAddressAsString(char *buffer, const char *last, bool with_family)
|
||||
{
|
||||
if (this->GetAddress()->ss_family == AF_INET6) buffer = strecpy(buffer, "[", last);
|
||||
buffer = strecpy(buffer, this->GetHostname(), last);
|
||||
if (this->GetAddress()->ss_family == AF_INET6) buffer = strecpy(buffer, "]", last);
|
||||
buffer += seprintf(buffer, last, ":%d", this->GetPort());
|
||||
|
||||
if (with_family) {
|
||||
char family;
|
||||
switch (this->address.ss_family) {
|
||||
case AF_INET: family = '4'; break;
|
||||
case AF_INET6: family = '6'; break;
|
||||
default: family = '?'; break;
|
||||
}
|
||||
seprintf(buffer, last, " (IPv%c)", family);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the address as a string, e.g. 127.0.0.1:12345.
|
||||
* @param with_family whether to add the family (e.g. IPvX).
|
||||
* @return the address
|
||||
* @note NOT thread safe
|
||||
*/
|
||||
const char *NetworkAddress::GetAddressAsString(bool with_family)
|
||||
{
|
||||
/* 6 = for the : and 5 for the decimal port number */
|
||||
static char buf[NETWORK_HOSTNAME_LENGTH + 6 + 7];
|
||||
this->GetAddressAsString(buf, lastof(buf), with_family);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to resolve without opening a socket.
|
||||
* @param runp information about the socket to try not
|
||||
* @return the opened socket or INVALID_SOCKET
|
||||
*/
|
||||
static SOCKET ResolveLoopProc(addrinfo *runp)
|
||||
{
|
||||
/* We just want the first 'entry', so return a valid socket. */
|
||||
return !INVALID_SOCKET;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the address in its internal representation.
|
||||
* @return the address
|
||||
*/
|
||||
const sockaddr_storage *NetworkAddress::GetAddress()
|
||||
{
|
||||
if (!this->IsResolved()) {
|
||||
/* Here we try to resolve a network address. We use SOCK_STREAM as
|
||||
* socket type because some stupid OSes, like Solaris, cannot be
|
||||
* bothered to implement the specifications and allow '0' as value
|
||||
* that means "don't care whether it is SOCK_STREAM or SOCK_DGRAM".
|
||||
*/
|
||||
this->Resolve(this->address.ss_family, SOCK_STREAM, AI_ADDRCONFIG, NULL, ResolveLoopProc);
|
||||
this->resolved = true;
|
||||
}
|
||||
return &this->address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks of this address is of the given family.
|
||||
* @param family the family to check against
|
||||
* @return true if it is of the given family
|
||||
*/
|
||||
bool NetworkAddress::IsFamily(int family)
|
||||
{
|
||||
if (!this->IsResolved()) {
|
||||
this->Resolve(family, SOCK_STREAM, AI_ADDRCONFIG, NULL, ResolveLoopProc);
|
||||
}
|
||||
return this->address.ss_family == family;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether this IP address is contained by the given netmask.
|
||||
* @param netmask the netmask in CIDR notation to test against.
|
||||
* @note netmask without /n assumes all bits need to match.
|
||||
* @return true if this IP is within the netmask.
|
||||
*/
|
||||
bool NetworkAddress::IsInNetmask(char *netmask)
|
||||
{
|
||||
/* Resolve it if we didn't do it already */
|
||||
if (!this->IsResolved()) this->GetAddress();
|
||||
|
||||
int cidr = this->address.ss_family == AF_INET ? 32 : 128;
|
||||
|
||||
NetworkAddress mask_address;
|
||||
|
||||
/* Check for CIDR separator */
|
||||
char *chr_cidr = strchr(netmask, '/');
|
||||
if (chr_cidr != NULL) {
|
||||
int tmp_cidr = atoi(chr_cidr + 1);
|
||||
|
||||
/* Invalid CIDR, treat as single host */
|
||||
if (tmp_cidr > 0 || tmp_cidr < cidr) cidr = tmp_cidr;
|
||||
|
||||
/* Remove and then replace the / so that NetworkAddress works on the IP portion */
|
||||
*chr_cidr = '\0';
|
||||
mask_address = NetworkAddress(netmask, 0, this->address.ss_family);
|
||||
*chr_cidr = '/';
|
||||
} else {
|
||||
mask_address = NetworkAddress(netmask, 0, this->address.ss_family);
|
||||
}
|
||||
|
||||
if (mask_address.GetAddressLength() == 0) return false;
|
||||
|
||||
uint32 *ip;
|
||||
uint32 *mask;
|
||||
switch (this->address.ss_family) {
|
||||
case AF_INET:
|
||||
ip = (uint32*)&((struct sockaddr_in*)&this->address)->sin_addr.s_addr;
|
||||
mask = (uint32*)&((struct sockaddr_in*)&mask_address.address)->sin_addr.s_addr;
|
||||
break;
|
||||
|
||||
case AF_INET6:
|
||||
ip = (uint32*)&((struct sockaddr_in6*)&this->address)->sin6_addr;
|
||||
mask = (uint32*)&((struct sockaddr_in6*)&mask_address.address)->sin6_addr;
|
||||
break;
|
||||
|
||||
default:
|
||||
NOT_REACHED();
|
||||
}
|
||||
|
||||
while (cidr > 0) {
|
||||
uint32 msk = cidr >= 32 ? (uint32)-1 : htonl(-(1 << (32 - cidr)));
|
||||
if ((*mask++ & msk) != (*ip++ & msk)) return false;
|
||||
|
||||
cidr -= 32;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve this address into a socket
|
||||
* @param family the type of 'protocol' (IPv4, IPv6)
|
||||
* @param socktype the type of socket (TCP, UDP, etc)
|
||||
* @param flags the flags to send to getaddrinfo
|
||||
* @param sockets the list of sockets to add the sockets to
|
||||
* @param func the inner working while looping over the address info
|
||||
* @return the resolved socket or INVALID_SOCKET.
|
||||
*/
|
||||
SOCKET NetworkAddress::Resolve(int family, int socktype, int flags, SocketList *sockets, LoopProc func)
|
||||
{
|
||||
struct addrinfo *ai;
|
||||
struct addrinfo hints;
|
||||
memset(&hints, 0, sizeof (hints));
|
||||
hints.ai_family = family;
|
||||
hints.ai_flags = flags;
|
||||
hints.ai_socktype = socktype;
|
||||
|
||||
/* The port needs to be a string. Six is enough to contain all characters + '\0'. */
|
||||
char port_name[6];
|
||||
seprintf(port_name, lastof(port_name), "%u", this->GetPort());
|
||||
|
||||
bool reset_hostname = false;
|
||||
/* Setting both hostname to NULL and port to 0 is not allowed.
|
||||
* As port 0 means bind to any port, the other must mean that
|
||||
* we want to bind to 'all' IPs. */
|
||||
if (StrEmpty(this->hostname) && this->address_length == 0 && this->GetPort() == 0) {
|
||||
reset_hostname = true;
|
||||
int fam = this->address.ss_family;
|
||||
if (fam == AF_UNSPEC) fam = family;
|
||||
strecpy(this->hostname, fam == AF_INET ? "0.0.0.0" : "::", lastof(this->hostname));
|
||||
}
|
||||
|
||||
int e = getaddrinfo(StrEmpty(this->hostname) ? NULL : this->hostname, port_name, &hints, &ai);
|
||||
|
||||
if (reset_hostname) strecpy(this->hostname, "", lastof(this->hostname));
|
||||
|
||||
if (e != 0) {
|
||||
if (func != ResolveLoopProc) {
|
||||
DEBUG(net, 0, "getaddrinfo for hostname \"%s\", port %s, address family %s and socket type %s failed: %s",
|
||||
this->hostname, port_name, AddressFamilyAsString(family), SocketTypeAsString(socktype), FS2OTTD(gai_strerror(e)));
|
||||
}
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
SOCKET sock = INVALID_SOCKET;
|
||||
for (struct addrinfo *runp = ai; runp != NULL; runp = runp->ai_next) {
|
||||
/* When we are binding to multiple sockets, make sure we do not
|
||||
* connect to one with exactly the same address twice. That's
|
||||
* of course totally unneeded ;) */
|
||||
if (sockets != NULL) {
|
||||
NetworkAddress address(runp->ai_addr, (int)runp->ai_addrlen);
|
||||
if (sockets->Contains(address)) continue;
|
||||
}
|
||||
sock = func(runp);
|
||||
if (sock == INVALID_SOCKET) continue;
|
||||
|
||||
if (sockets == NULL) {
|
||||
this->address_length = (int)runp->ai_addrlen;
|
||||
assert(sizeof(this->address) >= runp->ai_addrlen);
|
||||
memcpy(&this->address, runp->ai_addr, runp->ai_addrlen);
|
||||
break;
|
||||
}
|
||||
|
||||
NetworkAddress addr(runp->ai_addr, (int)runp->ai_addrlen);
|
||||
(*sockets)[addr] = sock;
|
||||
sock = INVALID_SOCKET;
|
||||
}
|
||||
freeaddrinfo (ai);
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to resolve a connected socket.
|
||||
* @param runp information about the socket to try not
|
||||
* @return the opened socket or INVALID_SOCKET
|
||||
*/
|
||||
static SOCKET ConnectLoopProc(addrinfo *runp)
|
||||
{
|
||||
const char *type = NetworkAddress::SocketTypeAsString(runp->ai_socktype);
|
||||
const char *family = NetworkAddress::AddressFamilyAsString(runp->ai_family);
|
||||
const char *address = NetworkAddress(runp->ai_addr, (int)runp->ai_addrlen).GetAddressAsString();
|
||||
|
||||
SOCKET sock = socket(runp->ai_family, runp->ai_socktype, runp->ai_protocol);
|
||||
if (sock == INVALID_SOCKET) {
|
||||
DEBUG(net, 1, "[%s] could not create %s socket: %s", type, family, strerror(errno));
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
if (!SetNoDelay(sock)) DEBUG(net, 1, "[%s] setting TCP_NODELAY failed", type);
|
||||
|
||||
if (connect(sock, runp->ai_addr, (int)runp->ai_addrlen) != 0) {
|
||||
DEBUG(net, 1, "[%s] could not connect %s socket: %s", type, family, strerror(errno));
|
||||
closesocket(sock);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
/* Connection succeeded */
|
||||
if (!SetNonBlocking(sock)) DEBUG(net, 0, "[%s] setting non-blocking mode failed", type);
|
||||
|
||||
DEBUG(net, 1, "[%s] connected to %s", type, address);
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the given address.
|
||||
* @return the connected socket or INVALID_SOCKET.
|
||||
*/
|
||||
SOCKET NetworkAddress::Connect()
|
||||
{
|
||||
DEBUG(net, 1, "Connecting to %s", this->GetAddressAsString());
|
||||
|
||||
return this->Resolve(AF_UNSPEC, SOCK_STREAM, AI_ADDRCONFIG, NULL, ConnectLoopProc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to resolve a listening.
|
||||
* @param runp information about the socket to try not
|
||||
* @return the opened socket or INVALID_SOCKET
|
||||
*/
|
||||
static SOCKET ListenLoopProc(addrinfo *runp)
|
||||
{
|
||||
const char *type = NetworkAddress::SocketTypeAsString(runp->ai_socktype);
|
||||
const char *family = NetworkAddress::AddressFamilyAsString(runp->ai_family);
|
||||
const char *address = NetworkAddress(runp->ai_addr, (int)runp->ai_addrlen).GetAddressAsString();
|
||||
|
||||
SOCKET sock = socket(runp->ai_family, runp->ai_socktype, runp->ai_protocol);
|
||||
if (sock == INVALID_SOCKET) {
|
||||
DEBUG(net, 0, "[%s] could not create %s socket on port %s: %s", type, family, address, strerror(errno));
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
if (runp->ai_socktype == SOCK_STREAM && !SetNoDelay(sock)) {
|
||||
DEBUG(net, 3, "[%s] setting TCP_NODELAY failed for port %s", type, address);
|
||||
}
|
||||
|
||||
int on = 1;
|
||||
/* The (const char*) cast is needed for windows!! */
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)) == -1) {
|
||||
DEBUG(net, 3, "[%s] could not set reusable %s sockets for port %s: %s", type, family, address, strerror(errno));
|
||||
}
|
||||
|
||||
#ifndef __OS2__
|
||||
if (runp->ai_family == AF_INET6 &&
|
||||
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&on, sizeof(on)) == -1) {
|
||||
DEBUG(net, 3, "[%s] could not disable IPv4 over IPv6 on port %s: %s", type, address, strerror(errno));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (bind(sock, runp->ai_addr, (int)runp->ai_addrlen) != 0) {
|
||||
DEBUG(net, 1, "[%s] could not bind on %s port %s: %s", type, family, address, strerror(errno));
|
||||
closesocket(sock);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
if (runp->ai_socktype != SOCK_DGRAM && listen(sock, 1) != 0) {
|
||||
DEBUG(net, 1, "[%s] could not listen at %s port %s: %s", type, family, address, strerror(errno));
|
||||
closesocket(sock);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
/* Connection succeeded */
|
||||
if (!SetNonBlocking(sock)) DEBUG(net, 0, "[%s] setting non-blocking mode failed for %s port %s", type, family, address);
|
||||
|
||||
DEBUG(net, 1, "[%s] listening on %s port %s", type, family, address);
|
||||
return sock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the given socket listen.
|
||||
* @param socktype the type of socket (TCP, UDP, etc)
|
||||
* @param sockets the list of sockets to add the sockets to
|
||||
*/
|
||||
void NetworkAddress::Listen(int socktype, SocketList *sockets)
|
||||
{
|
||||
assert(sockets != NULL);
|
||||
|
||||
/* Setting both hostname to NULL and port to 0 is not allowed.
|
||||
* As port 0 means bind to any port, the other must mean that
|
||||
* we want to bind to 'all' IPs. */
|
||||
if (this->address_length == 0 && this->address.ss_family == AF_UNSPEC &&
|
||||
StrEmpty(this->hostname) && this->GetPort() == 0) {
|
||||
this->Resolve(AF_INET, socktype, AI_ADDRCONFIG | AI_PASSIVE, sockets, ListenLoopProc);
|
||||
this->Resolve(AF_INET6, socktype, AI_ADDRCONFIG | AI_PASSIVE, sockets, ListenLoopProc);
|
||||
} else {
|
||||
this->Resolve(AF_UNSPEC, socktype, AI_ADDRCONFIG | AI_PASSIVE, sockets, ListenLoopProc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the socket type into a string
|
||||
* @param socktype the socket type to convert
|
||||
* @return the string representation
|
||||
* @note only works for SOCK_STREAM and SOCK_DGRAM
|
||||
*/
|
||||
/* static */ const char *NetworkAddress::SocketTypeAsString(int socktype)
|
||||
{
|
||||
switch (socktype) {
|
||||
case SOCK_STREAM: return "tcp";
|
||||
case SOCK_DGRAM: return "udp";
|
||||
default: return "unsupported";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the address family into a string
|
||||
* @param family the family to convert
|
||||
* @return the string representation
|
||||
* @note only works for AF_INET, AF_INET6 and AF_UNSPEC
|
||||
*/
|
||||
/* static */ const char *NetworkAddress::AddressFamilyAsString(int family)
|
||||
{
|
||||
switch (family) {
|
||||
case AF_UNSPEC: return "either IPv4 or IPv6";
|
||||
case AF_INET: return "IPv4";
|
||||
case AF_INET6: return "IPv6";
|
||||
default: return "unsupported";
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
196
src/network/core/address.h
Normal file
196
src/network/core/address.h
Normal file
@@ -0,0 +1,196 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file core/address.h Wrapper for network addresses. */
|
||||
|
||||
#ifndef NETWORK_CORE_ADDRESS_H
|
||||
#define NETWORK_CORE_ADDRESS_H
|
||||
|
||||
#include "os_abstraction.h"
|
||||
#include "config.h"
|
||||
#include "../../string_func.h"
|
||||
#include "../../core/smallmap_type.hpp"
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
class NetworkAddress;
|
||||
typedef SmallVector<NetworkAddress, 4> NetworkAddressList; ///< Type for a list of addresses.
|
||||
typedef SmallMap<NetworkAddress, SOCKET, 4> SocketList; ///< Type for a mapping between address and socket.
|
||||
|
||||
/**
|
||||
* Wrapper for (un)resolved network addresses; there's no reason to transform
|
||||
* a numeric IP to a string and then back again to pass it to functions. It
|
||||
* furthermore allows easier delaying of the hostname lookup.
|
||||
*/
|
||||
class NetworkAddress {
|
||||
private:
|
||||
char hostname[NETWORK_HOSTNAME_LENGTH]; ///< The hostname
|
||||
int address_length; ///< The length of the resolved address
|
||||
sockaddr_storage address; ///< The resolved address
|
||||
bool resolved; ///< Whether the address has been (tried to be) resolved
|
||||
|
||||
/**
|
||||
* Helper function to resolve something to a socket.
|
||||
* @param runp information about the socket to try not
|
||||
* @return the opened socket or INVALID_SOCKET
|
||||
*/
|
||||
typedef SOCKET (*LoopProc)(addrinfo *runp);
|
||||
|
||||
SOCKET Resolve(int family, int socktype, int flags, SocketList *sockets, LoopProc func);
|
||||
public:
|
||||
/**
|
||||
* Create a network address based on a resolved IP and port.
|
||||
* @param address The IP address with port.
|
||||
* @param address_length The length of the address.
|
||||
*/
|
||||
NetworkAddress(struct sockaddr_storage &address, int address_length) :
|
||||
address_length(address_length),
|
||||
address(address),
|
||||
resolved(address_length != 0)
|
||||
{
|
||||
*this->hostname = '\0';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a network address based on a resolved IP and port.
|
||||
* @param address The IP address with port.
|
||||
* @param address_length The length of the address.
|
||||
*/
|
||||
NetworkAddress(sockaddr *address, int address_length) :
|
||||
address_length(address_length),
|
||||
resolved(address_length != 0)
|
||||
{
|
||||
*this->hostname = '\0';
|
||||
memset(&this->address, 0, sizeof(this->address));
|
||||
memcpy(&this->address, address, address_length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a network address based on a unresolved host and port
|
||||
* @param hostname the unresolved hostname
|
||||
* @param port the port
|
||||
* @param family the address family
|
||||
*/
|
||||
NetworkAddress(const char *hostname = "", uint16 port = 0, int family = AF_UNSPEC) :
|
||||
address_length(0),
|
||||
resolved(false)
|
||||
{
|
||||
/* Also handle IPv6 bracket enclosed hostnames */
|
||||
if (StrEmpty(hostname)) hostname = "";
|
||||
if (*hostname == '[') hostname++;
|
||||
strecpy(this->hostname, StrEmpty(hostname) ? "" : hostname, lastof(this->hostname));
|
||||
char *tmp = strrchr(this->hostname, ']');
|
||||
if (tmp != NULL) *tmp = '\0';
|
||||
|
||||
memset(&this->address, 0, sizeof(this->address));
|
||||
this->address.ss_family = family;
|
||||
this->SetPort(port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a clone of another address
|
||||
* @param address the address to clone
|
||||
*/
|
||||
NetworkAddress(const NetworkAddress &address)
|
||||
{
|
||||
memcpy(this, &address, sizeof(*this));
|
||||
}
|
||||
|
||||
const char *GetHostname();
|
||||
void GetAddressAsString(char *buffer, const char *last, bool with_family = true);
|
||||
const char *GetAddressAsString(bool with_family = true);
|
||||
const sockaddr_storage *GetAddress();
|
||||
|
||||
/**
|
||||
* Get the (valid) length of the address.
|
||||
* @return the length
|
||||
*/
|
||||
int GetAddressLength()
|
||||
{
|
||||
/* Resolve it if we didn't do it already */
|
||||
if (!this->IsResolved()) this->GetAddress();
|
||||
return this->address_length;
|
||||
}
|
||||
|
||||
uint16 GetPort() const;
|
||||
void SetPort(uint16 port);
|
||||
|
||||
/**
|
||||
* Check whether the IP address has been resolved already
|
||||
* @return true iff the port has been resolved
|
||||
*/
|
||||
bool IsResolved() const
|
||||
{
|
||||
return this->resolved;
|
||||
}
|
||||
|
||||
bool IsFamily(int family);
|
||||
bool IsInNetmask(char *netmask);
|
||||
|
||||
/**
|
||||
* Compare the address of this class with the address of another.
|
||||
* @param address the other address.
|
||||
* @return < 0 if address is less, 0 if equal and > 0 if address is more
|
||||
*/
|
||||
int CompareTo(NetworkAddress &address)
|
||||
{
|
||||
int r = this->GetAddressLength() - address.GetAddressLength();
|
||||
if (r == 0) r = this->address.ss_family - address.address.ss_family;
|
||||
if (r == 0) r = memcmp(&this->address, &address.address, this->address_length);
|
||||
if (r == 0) r = this->GetPort() - address.GetPort();
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare the address of this class with the address of another.
|
||||
* @param address the other address.
|
||||
* @return true if both match.
|
||||
*/
|
||||
bool operator == (NetworkAddress &address)
|
||||
{
|
||||
return this->CompareTo(address) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare the address of this class with the address of another.
|
||||
* @param address the other address.
|
||||
* @return true if both match.
|
||||
*/
|
||||
bool operator == (NetworkAddress &address) const
|
||||
{
|
||||
return const_cast<NetworkAddress*>(this)->CompareTo(address) == 0;
|
||||
}
|
||||
/**
|
||||
* Compare the address of this class with the address of another.
|
||||
* @param address the other address.
|
||||
* @return true if both do not match.
|
||||
*/
|
||||
bool operator != (NetworkAddress address) const
|
||||
{
|
||||
return const_cast<NetworkAddress*>(this)->CompareTo(address) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare the address of this class with the address of another.
|
||||
* @param address the other address.
|
||||
*/
|
||||
bool operator < (NetworkAddress &address)
|
||||
{
|
||||
return this->CompareTo(address) < 0;
|
||||
}
|
||||
|
||||
SOCKET Connect();
|
||||
void Listen(int socktype, SocketList *sockets);
|
||||
|
||||
static const char *SocketTypeAsString(int socktype);
|
||||
static const char *AddressFamilyAsString(int family);
|
||||
};
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
#endif /* NETWORK_CORE_ADDRESS_H */
|
||||
74
src/network/core/config.h
Normal file
74
src/network/core/config.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file config.h Configuration options of the network stuff. It is used even when compiling without network support.
|
||||
*/
|
||||
|
||||
#ifndef NETWORK_CORE_CONFIG_H
|
||||
#define NETWORK_CORE_CONFIG_H
|
||||
|
||||
/** DNS hostname of the masterserver */
|
||||
static const char * const NETWORK_MASTER_SERVER_HOST = "master.openttd.org";
|
||||
/** DNS hostname of the content server */
|
||||
static const char * const NETWORK_CONTENT_SERVER_HOST = "content.openttd.org";
|
||||
/** DNS hostname of the HTTP-content mirror server */
|
||||
static const char * const NETWORK_CONTENT_MIRROR_HOST = "binaries.openttd.org";
|
||||
/** URL of the HTTP mirror system */
|
||||
static const char * const NETWORK_CONTENT_MIRROR_URL = "/bananas";
|
||||
/** Message sent to the masterserver to 'identify' this client as OpenTTD */
|
||||
static const char * const NETWORK_MASTER_SERVER_WELCOME_MESSAGE = "OpenTTDRegister";
|
||||
|
||||
static const uint16 NETWORK_MASTER_SERVER_PORT = 3978; ///< The default port of the master server (UDP)
|
||||
static const uint16 NETWORK_CONTENT_SERVER_PORT = 3978; ///< The default port of the content server (TCP)
|
||||
static const uint16 NETWORK_CONTENT_MIRROR_PORT = 80; ///< The default port of the content mirror (TCP)
|
||||
static const uint16 NETWORK_DEFAULT_PORT = 3979; ///< The default port of the game server (TCP & UDP)
|
||||
static const uint16 NETWORK_ADMIN_PORT = 3977; ///< The default port for admin network
|
||||
static const uint16 NETWORK_DEFAULT_DEBUGLOG_PORT = 3982; ///< The default port debug-log is sent to (TCP)
|
||||
|
||||
static const uint16 SEND_MTU = 1460; ///< Number of bytes we can pack in a single packet
|
||||
|
||||
static const byte NETWORK_GAME_ADMIN_VERSION = 1; ///< What version of the admin network do we use?
|
||||
static const byte NETWORK_GAME_INFO_VERSION = 4; ///< What version of game-info do we use?
|
||||
static const byte NETWORK_COMPANY_INFO_VERSION = 6; ///< What version of company info is this?
|
||||
static const byte NETWORK_MASTER_SERVER_VERSION = 2; ///< What version of master-server-protocol do we use?
|
||||
|
||||
static const uint NETWORK_NAME_LENGTH = 80; ///< The maximum length of the server name and map name, in bytes including '\0'
|
||||
static const uint NETWORK_COMPANY_NAME_LENGTH = 128; ///< The maximum length of the company name, in bytes including '\0'
|
||||
static const uint NETWORK_HOSTNAME_LENGTH = 80; ///< The maximum length of the host name, in bytes including '\0'
|
||||
static const uint NETWORK_SERVER_ID_LENGTH = 33; ///< The maximum length of the network id of the servers, in bytes including '\0'
|
||||
static const uint NETWORK_REVISION_LENGTH = 33; ///< The maximum length of the revision, in bytes including '\0'
|
||||
static const uint NETWORK_PASSWORD_LENGTH = 33; ///< The maximum length of the password, in bytes including '\0' (must be >= NETWORK_SERVER_ID_LENGTH)
|
||||
static const uint NETWORK_CLIENTS_LENGTH = 200; ///< The maximum length for the list of clients that controls a company, in bytes including '\0'
|
||||
static const uint NETWORK_CLIENT_NAME_LENGTH = 25; ///< The maximum length of a client's name, in bytes including '\0'
|
||||
static const uint NETWORK_RCONCOMMAND_LENGTH = 500; ///< The maximum length of a rconsole command, in bytes including '\0'
|
||||
static const uint NETWORK_GAMESCRIPT_JSON_LENGTH = SEND_MTU - 3; ///< The maximum length of a gamescript json string, in bytes including '\0'. Must not be longer than SEND_MTU including header (3 bytes)
|
||||
static const uint NETWORK_CHAT_LENGTH = 900; ///< The maximum length of a chat message, in bytes including '\0'
|
||||
|
||||
static const uint NETWORK_GRF_NAME_LENGTH = 80; ///< Maximum length of the name of a GRF
|
||||
|
||||
/**
|
||||
* Maximum number of GRFs that can be sent.
|
||||
* This limit is reached when PACKET_UDP_SERVER_RESPONSE reaches the maximum size of SEND_MTU bytes.
|
||||
*/
|
||||
static const uint NETWORK_MAX_GRF_COUNT = 62;
|
||||
|
||||
static const uint NETWORK_NUM_LANGUAGES = 36; ///< Number of known languages (to the network protocol) + 1 for 'any'.
|
||||
|
||||
/**
|
||||
* The number of landscapes in OpenTTD.
|
||||
* This number must be equal to NUM_LANDSCAPE, but as this number is used
|
||||
* within the network code and that the network code is shared with the
|
||||
* masterserver/updater, it has to be declared in here too. In network.cpp
|
||||
* there is a compile assertion to check that this NUM_LANDSCAPE is equal
|
||||
* to NETWORK_NUM_LANDSCAPES.
|
||||
*/
|
||||
static const uint NETWORK_NUM_LANDSCAPES = 4;
|
||||
|
||||
#endif /* NETWORK_CORE_CONFIG_H */
|
||||
131
src/network/core/core.cpp
Normal file
131
src/network/core/core.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file core.cpp Functions used to initialize/shut down the core network
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
#include "../../stdafx.h"
|
||||
#include "../../debug.h"
|
||||
#include "os_abstraction.h"
|
||||
#include "packet.h"
|
||||
|
||||
#include "../../safeguards.h"
|
||||
|
||||
|
||||
#ifdef __MORPHOS__
|
||||
/* the library base is required here */
|
||||
struct Library *SocketBase = NULL;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Initializes the network core (as that is needed for some platforms
|
||||
* @return true if the core has been initialized, false otherwise
|
||||
*/
|
||||
bool NetworkCoreInitialize()
|
||||
{
|
||||
#if defined(__MORPHOS__) || defined(__AMIGA__)
|
||||
/*
|
||||
* IMPORTANT NOTE: SocketBase needs to be initialized before we use _any_
|
||||
* network related function, else: crash.
|
||||
*/
|
||||
DEBUG(net, 3, "[core] loading bsd socket library");
|
||||
SocketBase = OpenLibrary("bsdsocket.library", 4);
|
||||
if (SocketBase == NULL) {
|
||||
DEBUG(net, 0, "[core] can't open bsdsocket.library version 4, network unavailable");
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(__AMIGA__)
|
||||
/* for usleep() implementation (only required for legacy AmigaOS builds) */
|
||||
TimerPort = CreateMsgPort();
|
||||
if (TimerPort != NULL) {
|
||||
TimerRequest = (struct timerequest*)CreateIORequest(TimerPort, sizeof(struct timerequest);
|
||||
if (TimerRequest != NULL) {
|
||||
if (OpenDevice("timer.device", UNIT_MICROHZ, (struct IORequest*)TimerRequest, 0) == 0) {
|
||||
TimerBase = TimerRequest->tr_node.io_Device;
|
||||
if (TimerBase == NULL) {
|
||||
/* free resources... */
|
||||
DEBUG(net, 0, "[core] can't initialize timer, network unavailable");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* __AMIGA__ */
|
||||
#endif /* __MORPHOS__ / __AMIGA__ */
|
||||
|
||||
/* Let's load the network in windows */
|
||||
#ifdef _WIN32
|
||||
{
|
||||
WSADATA wsa;
|
||||
DEBUG(net, 3, "[core] loading windows socket library");
|
||||
if (WSAStartup(MAKEWORD(2, 0), &wsa) != 0) {
|
||||
DEBUG(net, 0, "[core] WSAStartup failed, network unavailable");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif /* _WIN32 */
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts down the network core (as that is needed for some platforms
|
||||
*/
|
||||
void NetworkCoreShutdown()
|
||||
{
|
||||
#if defined(__MORPHOS__) || defined(__AMIGA__)
|
||||
/* free allocated resources */
|
||||
#if defined(__AMIGA__)
|
||||
if (TimerBase != NULL) CloseDevice((struct IORequest*)TimerRequest); // XXX This smells wrong
|
||||
if (TimerRequest != NULL) DeleteIORequest(TimerRequest);
|
||||
if (TimerPort != NULL) DeleteMsgPort(TimerPort);
|
||||
#endif
|
||||
|
||||
if (SocketBase != NULL) CloseLibrary(SocketBase);
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
WSACleanup();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the GRFIdentifier (GRF ID and MD5 checksum) to the packet
|
||||
* @param p the packet to write the data to
|
||||
* @param grf the GRFIdentifier to serialize
|
||||
*/
|
||||
void NetworkSocketHandler::SendGRFIdentifier(Packet *p, const GRFIdentifier *grf)
|
||||
{
|
||||
uint j;
|
||||
p->Send_uint32(grf->grfid);
|
||||
for (j = 0; j < sizeof(grf->md5sum); j++) {
|
||||
p->Send_uint8 (grf->md5sum[j]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes the GRFIdentifier (GRF ID and MD5 checksum) from the packet
|
||||
* @param p the packet to read the data from
|
||||
* @param grf the GRFIdentifier to deserialize
|
||||
*/
|
||||
void NetworkSocketHandler::ReceiveGRFIdentifier(Packet *p, GRFIdentifier *grf)
|
||||
{
|
||||
uint j;
|
||||
grf->grfid = p->Recv_uint32();
|
||||
for (j = 0; j < sizeof(grf->md5sum); j++) {
|
||||
grf->md5sum[j] = p->Recv_uint8();
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
85
src/network/core/core.h
Normal file
85
src/network/core/core.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file core.h Base for all network types (UDP and TCP)
|
||||
*/
|
||||
|
||||
#ifndef NETWORK_CORE_CORE_H
|
||||
#define NETWORK_CORE_CORE_H
|
||||
|
||||
#include "../../newgrf_config.h"
|
||||
#include "config.h"
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
bool NetworkCoreInitialize();
|
||||
void NetworkCoreShutdown();
|
||||
|
||||
/** Status of a network client; reasons why a client has quit */
|
||||
enum NetworkRecvStatus {
|
||||
NETWORK_RECV_STATUS_OKAY, ///< Everything is okay
|
||||
NETWORK_RECV_STATUS_DESYNC, ///< A desync did occur
|
||||
NETWORK_RECV_STATUS_NEWGRF_MISMATCH, ///< We did not have the required NewGRFs
|
||||
NETWORK_RECV_STATUS_SAVEGAME, ///< Something went wrong (down)loading the savegame
|
||||
NETWORK_RECV_STATUS_CONN_LOST, ///< The connection is 'just' lost
|
||||
NETWORK_RECV_STATUS_MALFORMED_PACKET, ///< We apparently send a malformed packet
|
||||
NETWORK_RECV_STATUS_SERVER_ERROR, ///< The server told us we made an error
|
||||
NETWORK_RECV_STATUS_SERVER_FULL, ///< The server is full
|
||||
NETWORK_RECV_STATUS_SERVER_BANNED, ///< The server has banned us
|
||||
NETWORK_RECV_STATUS_CLOSE_QUERY, ///< Done querying the server
|
||||
};
|
||||
|
||||
/** Forward declaration due to circular dependencies */
|
||||
struct Packet;
|
||||
|
||||
/**
|
||||
* SocketHandler for all network sockets in OpenTTD.
|
||||
*/
|
||||
class NetworkSocketHandler {
|
||||
bool has_quit; ///< Whether the current client has quit/send a bad packet
|
||||
public:
|
||||
/** Create a new unbound socket */
|
||||
NetworkSocketHandler() { this->has_quit = false; }
|
||||
|
||||
/** Close the socket when destructing the socket handler */
|
||||
virtual ~NetworkSocketHandler() { this->Close(); }
|
||||
|
||||
/** Really close the socket */
|
||||
virtual void Close() {}
|
||||
|
||||
/**
|
||||
* Close the current connection; for TCP this will be mostly equivalent
|
||||
* to Close(), but for UDP it just means the packet has to be dropped.
|
||||
* @param error Whether we quit under an error condition or not.
|
||||
* @return new status of the connection.
|
||||
*/
|
||||
virtual NetworkRecvStatus CloseConnection(bool error = true) { this->has_quit = true; return NETWORK_RECV_STATUS_OKAY; }
|
||||
|
||||
/**
|
||||
* Whether the current client connected to the socket has quit.
|
||||
* In the case of UDP, for example, once a client quits (send bad
|
||||
* data), the socket in not closed; only the packet is dropped.
|
||||
* @return true when the current client has quit, false otherwise
|
||||
*/
|
||||
bool HasClientQuit() const { return this->has_quit; }
|
||||
|
||||
/**
|
||||
* Reopen the socket so we can send/receive stuff again.
|
||||
*/
|
||||
void Reopen() { this->has_quit = false; }
|
||||
|
||||
void SendGRFIdentifier(Packet *p, const GRFIdentifier *grf);
|
||||
void ReceiveGRFIdentifier(Packet *p, GRFIdentifier *grf);
|
||||
void SendCompanyInformation(Packet *p, const struct Company *c, const struct NetworkCompanyStats *stats, uint max_len = NETWORK_COMPANY_NAME_LENGTH);
|
||||
};
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
|
||||
#endif /* NETWORK_CORE_CORE_H */
|
||||
63
src/network/core/game.h
Normal file
63
src/network/core/game.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file game.h Information about a game that is sent between a
|
||||
* game server, game client and masterserver.
|
||||
*/
|
||||
|
||||
#ifndef NETWORK_CORE_GAME_H
|
||||
#define NETWORK_CORE_GAME_H
|
||||
|
||||
#include "config.h"
|
||||
#include "../../newgrf_config.h"
|
||||
#include "../../date_type.h"
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
/**
|
||||
* The game information that is not generated on-the-fly and has to
|
||||
* be sent to the clients.
|
||||
*/
|
||||
struct NetworkServerGameInfo {
|
||||
char map_name[NETWORK_NAME_LENGTH]; ///< Map which is played ["random" for a randomized map]
|
||||
byte clients_on; ///< Current count of clients on server
|
||||
};
|
||||
|
||||
/**
|
||||
* The game information that is sent from the server to the clients.
|
||||
*/
|
||||
struct NetworkGameInfo : NetworkServerGameInfo {
|
||||
GRFConfig *grfconfig; ///< List of NewGRF files used
|
||||
Date start_date; ///< When the game started
|
||||
Date game_date; ///< Current date
|
||||
uint16 map_width; ///< Map width
|
||||
uint16 map_height; ///< Map height
|
||||
char server_name[NETWORK_NAME_LENGTH]; ///< Server name
|
||||
char hostname[NETWORK_HOSTNAME_LENGTH]; ///< Hostname of the server (if any)
|
||||
char server_revision[NETWORK_REVISION_LENGTH]; ///< The version number the server is using (e.g.: 'r304' or 0.5.0)
|
||||
bool dedicated; ///< Is this a dedicated server?
|
||||
bool version_compatible; ///< Can we connect to this server or not? (based on server_revision)
|
||||
bool compatible; ///< Can we connect to this server or not? (based on server_revision _and_ grf_match
|
||||
bool use_password; ///< Is this server passworded?
|
||||
byte game_info_version; ///< Version of the game info
|
||||
byte server_lang; ///< Language of the server (we should make a nice table for this)
|
||||
byte clients_max; ///< Max clients allowed on server
|
||||
byte companies_on; ///< How many started companies do we have
|
||||
byte companies_max; ///< Max companies allowed on server
|
||||
byte spectators_on; ///< How many spectators do we have?
|
||||
byte spectators_max; ///< Max spectators allowed on server
|
||||
byte map_set; ///< Graphical set
|
||||
};
|
||||
|
||||
const char * GetNetworkRevisionString();
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
|
||||
#endif /* NETWORK_CORE_GAME_H */
|
||||
211
src/network/core/host.cpp
Normal file
211
src/network/core/host.cpp
Normal file
@@ -0,0 +1,211 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file host.cpp Functions related to getting host specific data (IPs). */
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
#include "../../stdafx.h"
|
||||
#include "../../debug.h"
|
||||
#include "address.h"
|
||||
|
||||
#include "../../safeguards.h"
|
||||
|
||||
/**
|
||||
* Internal implementation for finding the broadcast IPs.
|
||||
* This function is implemented multiple times for multiple targets.
|
||||
* @param broadcast the list of broadcasts to write into.
|
||||
*/
|
||||
static void NetworkFindBroadcastIPsInternal(NetworkAddressList *broadcast);
|
||||
|
||||
#if defined(BEOS_NET_SERVER) || defined(__HAIKU__) /* doesn't have neither getifaddrs or net/if.h */
|
||||
/* Based on Andrew Bachmann's netstat+.c. Big thanks to him! */
|
||||
extern "C" int _netstat(int fd, char **output, int verbose);
|
||||
|
||||
int seek_past_header(char **pos, const char *header)
|
||||
{
|
||||
char *new_pos = strstr(*pos, header);
|
||||
if (new_pos == 0) {
|
||||
return B_ERROR;
|
||||
}
|
||||
*pos += strlen(header) + new_pos - *pos + 1;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
static void NetworkFindBroadcastIPsInternal(NetworkAddressList *broadcast) // BEOS implementation
|
||||
{
|
||||
int sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
|
||||
if (sock < 0) {
|
||||
DEBUG(net, 0, "[core] error creating socket");
|
||||
return;
|
||||
}
|
||||
|
||||
char *output_pointer = NULL;
|
||||
int output_length = _netstat(sock, &output_pointer, 1);
|
||||
if (output_length < 0) {
|
||||
DEBUG(net, 0, "[core] error running _netstat");
|
||||
return;
|
||||
}
|
||||
|
||||
char **output = &output_pointer;
|
||||
if (seek_past_header(output, "IP Interfaces:") == B_OK) {
|
||||
for (;;) {
|
||||
uint32 n;
|
||||
int fields, read;
|
||||
uint8 i1, i2, i3, i4, j1, j2, j3, j4;
|
||||
uint32 ip;
|
||||
uint32 netmask;
|
||||
|
||||
fields = sscanf(*output, "%u: %hhu.%hhu.%hhu.%hhu, netmask %hhu.%hhu.%hhu.%hhu%n",
|
||||
&n, &i1, &i2, &i3, &i4, &j1, &j2, &j3, &j4, &read);
|
||||
read += 1;
|
||||
if (fields != 9) {
|
||||
break;
|
||||
}
|
||||
|
||||
ip = (uint32)i1 << 24 | (uint32)i2 << 16 | (uint32)i3 << 8 | (uint32)i4;
|
||||
netmask = (uint32)j1 << 24 | (uint32)j2 << 16 | (uint32)j3 << 8 | (uint32)j4;
|
||||
|
||||
if (ip != INADDR_LOOPBACK && ip != INADDR_ANY) {
|
||||
sockaddr_storage address;
|
||||
memset(&address, 0, sizeof(address));
|
||||
((sockaddr_in*)&address)->sin_addr.s_addr = htonl(ip | ~netmask);
|
||||
NetworkAddress addr(address, sizeof(sockaddr));
|
||||
if (!broadcast->Contains(addr)) *broadcast->Append() = addr;
|
||||
}
|
||||
if (read < 0) {
|
||||
break;
|
||||
}
|
||||
*output += read;
|
||||
}
|
||||
closesocket(sock);
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(HAVE_GETIFADDRS)
|
||||
static void NetworkFindBroadcastIPsInternal(NetworkAddressList *broadcast) // GETIFADDRS implementation
|
||||
{
|
||||
struct ifaddrs *ifap, *ifa;
|
||||
|
||||
if (getifaddrs(&ifap) != 0) return;
|
||||
|
||||
for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
|
||||
if (!(ifa->ifa_flags & IFF_BROADCAST)) continue;
|
||||
if (ifa->ifa_broadaddr == NULL) continue;
|
||||
if (ifa->ifa_broadaddr->sa_family != AF_INET) continue;
|
||||
|
||||
NetworkAddress addr(ifa->ifa_broadaddr, sizeof(sockaddr));
|
||||
if (!broadcast->Contains(addr)) *broadcast->Append() = addr;
|
||||
}
|
||||
freeifaddrs(ifap);
|
||||
}
|
||||
|
||||
#elif defined(_WIN32)
|
||||
static void NetworkFindBroadcastIPsInternal(NetworkAddressList *broadcast) // Win32 implementation
|
||||
{
|
||||
SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sock == INVALID_SOCKET) return;
|
||||
|
||||
DWORD len = 0;
|
||||
int num = 2;
|
||||
INTERFACE_INFO *ifo = CallocT<INTERFACE_INFO>(num);
|
||||
|
||||
for (;;) {
|
||||
if (WSAIoctl(sock, SIO_GET_INTERFACE_LIST, NULL, 0, ifo, num * sizeof(*ifo), &len, NULL, NULL) == 0) break;
|
||||
free(ifo);
|
||||
if (WSAGetLastError() != WSAEFAULT) {
|
||||
closesocket(sock);
|
||||
return;
|
||||
}
|
||||
num *= 2;
|
||||
ifo = CallocT<INTERFACE_INFO>(num);
|
||||
}
|
||||
|
||||
for (uint j = 0; j < len / sizeof(*ifo); j++) {
|
||||
if (ifo[j].iiFlags & IFF_LOOPBACK) continue;
|
||||
if (!(ifo[j].iiFlags & IFF_BROADCAST)) continue;
|
||||
|
||||
sockaddr_storage address;
|
||||
memset(&address, 0, sizeof(address));
|
||||
/* iiBroadcast is unusable, because it always seems to be set to 255.255.255.255. */
|
||||
memcpy(&address, &ifo[j].iiAddress.Address, sizeof(sockaddr));
|
||||
((sockaddr_in*)&address)->sin_addr.s_addr = ifo[j].iiAddress.AddressIn.sin_addr.s_addr | ~ifo[j].iiNetmask.AddressIn.sin_addr.s_addr;
|
||||
NetworkAddress addr(address, sizeof(sockaddr));
|
||||
if (!broadcast->Contains(addr)) *broadcast->Append() = addr;
|
||||
}
|
||||
|
||||
free(ifo);
|
||||
closesocket(sock);
|
||||
}
|
||||
|
||||
#else /* not HAVE_GETIFADDRS */
|
||||
|
||||
#include "../../string_func.h"
|
||||
|
||||
static void NetworkFindBroadcastIPsInternal(NetworkAddressList *broadcast) // !GETIFADDRS implementation
|
||||
{
|
||||
SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sock == INVALID_SOCKET) return;
|
||||
|
||||
char buf[4 * 1024]; // Arbitrary buffer size
|
||||
struct ifconf ifconf;
|
||||
|
||||
ifconf.ifc_len = sizeof(buf);
|
||||
ifconf.ifc_buf = buf;
|
||||
if (ioctl(sock, SIOCGIFCONF, &ifconf) == -1) {
|
||||
closesocket(sock);
|
||||
return;
|
||||
}
|
||||
|
||||
const char *buf_end = buf + ifconf.ifc_len;
|
||||
for (const char *p = buf; p < buf_end;) {
|
||||
const struct ifreq *req = (const struct ifreq*)p;
|
||||
|
||||
if (req->ifr_addr.sa_family == AF_INET) {
|
||||
struct ifreq r;
|
||||
|
||||
strecpy(r.ifr_name, req->ifr_name, lastof(r.ifr_name));
|
||||
if (ioctl(sock, SIOCGIFFLAGS, &r) != -1 &&
|
||||
(r.ifr_flags & IFF_BROADCAST) &&
|
||||
ioctl(sock, SIOCGIFBRDADDR, &r) != -1) {
|
||||
NetworkAddress addr(&r.ifr_broadaddr, sizeof(sockaddr));
|
||||
if (!broadcast->Contains(addr)) *broadcast->Append() = addr;
|
||||
}
|
||||
}
|
||||
|
||||
p += sizeof(struct ifreq);
|
||||
#if defined(AF_LINK) && !defined(SUNOS)
|
||||
p += req->ifr_addr.sa_len - sizeof(struct sockaddr);
|
||||
#endif
|
||||
}
|
||||
|
||||
closesocket(sock);
|
||||
}
|
||||
#endif /* all NetworkFindBroadcastIPsInternals */
|
||||
|
||||
/**
|
||||
* Find the IPv4 broadcast addresses; IPv6 uses a completely different
|
||||
* strategy for broadcasting.
|
||||
* @param broadcast the list of broadcasts to write into.
|
||||
*/
|
||||
void NetworkFindBroadcastIPs(NetworkAddressList *broadcast)
|
||||
{
|
||||
NetworkFindBroadcastIPsInternal(broadcast);
|
||||
|
||||
/* Now display to the debug all the detected ips */
|
||||
DEBUG(net, 3, "Detected broadcast addresses:");
|
||||
int i = 0;
|
||||
for (NetworkAddress *addr = broadcast->Begin(); addr != broadcast->End(); addr++) {
|
||||
addr->SetPort(NETWORK_DEFAULT_PORT);
|
||||
DEBUG(net, 3, "%d) %s", i++, addr->GetHostname());
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
21
src/network/core/host.h
Normal file
21
src/network/core/host.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file host.h Resolving of hostnames/IPs
|
||||
*/
|
||||
|
||||
#ifndef NETWORK_CORE_HOST_H
|
||||
#define NETWORK_CORE_HOST_H
|
||||
|
||||
#include "address.h"
|
||||
|
||||
void NetworkFindBroadcastIPs(NetworkAddressList *broadcast);
|
||||
|
||||
#endif /* NETWORK_CORE_HOST_H */
|
||||
242
src/network/core/os_abstraction.h
Normal file
242
src/network/core/os_abstraction.h
Normal file
@@ -0,0 +1,242 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file os_abstraction.h Network stuff has many things that needs to be
|
||||
* included and/or implemented by default.
|
||||
* All those things are in this file.
|
||||
*/
|
||||
|
||||
#ifndef NETWORK_CORE_OS_ABSTRACTION_H
|
||||
#define NETWORK_CORE_OS_ABSTRACTION_H
|
||||
|
||||
/* Include standard stuff per OS */
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
/* Windows stuff */
|
||||
#if defined(_WIN32)
|
||||
#include <errno.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <windows.h>
|
||||
|
||||
#define GET_LAST_ERROR() WSAGetLastError()
|
||||
#undef EWOULDBLOCK
|
||||
#define EWOULDBLOCK WSAEWOULDBLOCK
|
||||
/* Windows has some different names for some types */
|
||||
typedef unsigned long in_addr_t;
|
||||
|
||||
#if !(defined(__MINGW32__) || defined(__CYGWIN__))
|
||||
/* Windows has some different names for some types */
|
||||
typedef SSIZE_T ssize_t;
|
||||
typedef int socklen_t;
|
||||
# define IPPROTO_IPV6 41
|
||||
#endif /* !(__MINGW32__ && __CYGWIN__) */
|
||||
#endif /* _WIN32 */
|
||||
|
||||
/* UNIX stuff */
|
||||
#if defined(UNIX) && !defined(__OS2__)
|
||||
# if defined(OPENBSD) || defined(__NetBSD__)
|
||||
# define AI_ADDRCONFIG 0
|
||||
# endif
|
||||
# define SOCKET int
|
||||
# define INVALID_SOCKET -1
|
||||
# if !defined(__MORPHOS__) && !defined(__AMIGA__)
|
||||
# define ioctlsocket ioctl
|
||||
# if !defined(BEOS_NET_SERVER)
|
||||
# define closesocket close
|
||||
# endif
|
||||
# define GET_LAST_ERROR() (errno)
|
||||
# endif
|
||||
/* Need this for FIONREAD on solaris */
|
||||
# define BSD_COMP
|
||||
|
||||
/* Includes needed for UNIX-like systems */
|
||||
# include <unistd.h>
|
||||
# include <sys/ioctl.h>
|
||||
# if defined(__BEOS__) && defined(BEOS_NET_SERVER)
|
||||
# include <be/net/socket.h>
|
||||
# include <be/kernel/OS.h> /* snooze() */
|
||||
# include <be/net/netdb.h>
|
||||
typedef unsigned long in_addr_t;
|
||||
# define INADDR_NONE INADDR_BROADCAST
|
||||
# else
|
||||
# include <sys/socket.h>
|
||||
# include <netinet/in.h>
|
||||
# include <netinet/tcp.h>
|
||||
# include <arpa/inet.h>
|
||||
# include <net/if.h>
|
||||
/* According to glibc/NEWS, <ifaddrs.h> appeared in glibc-2.3. */
|
||||
# if !defined(__sgi__) && !defined(SUNOS) && !defined(__MORPHOS__) && !defined(__BEOS__) && !defined(__HAIKU__) && !defined(__INNOTEK_LIBC__) \
|
||||
&& !(defined(__GLIBC__) && (__GLIBC__ <= 2) && (__GLIBC_MINOR__ <= 2)) && !defined(__dietlibc__) && !defined(HPUX)
|
||||
/* If for any reason ifaddrs.h does not exist on your system, comment out
|
||||
* the following two lines and an alternative way will be used to fetch
|
||||
* the list of IPs from the system. */
|
||||
# include <ifaddrs.h>
|
||||
# define HAVE_GETIFADDRS
|
||||
# endif
|
||||
# if !defined(INADDR_NONE)
|
||||
# define INADDR_NONE 0xffffffff
|
||||
# endif
|
||||
# if defined(__BEOS__) && !defined(BEOS_NET_SERVER)
|
||||
/* needed on Zeta */
|
||||
# include <sys/sockio.h>
|
||||
# endif
|
||||
# endif /* BEOS_NET_SERVER */
|
||||
|
||||
# if !defined(__BEOS__) && defined(__GLIBC__) && (__GLIBC__ <= 2) && (__GLIBC_MINOR__ <= 1)
|
||||
typedef uint32_t in_addr_t;
|
||||
# endif
|
||||
|
||||
# include <errno.h>
|
||||
# include <sys/time.h>
|
||||
# include <netdb.h>
|
||||
#endif /* UNIX */
|
||||
|
||||
#ifdef __BEOS__
|
||||
typedef int socklen_t;
|
||||
#endif
|
||||
|
||||
#ifdef __HAIKU__
|
||||
#define IPV6_V6ONLY 27
|
||||
#endif
|
||||
|
||||
/* OS/2 stuff */
|
||||
#if defined(__OS2__)
|
||||
# define SOCKET int
|
||||
# define INVALID_SOCKET -1
|
||||
# define ioctlsocket ioctl
|
||||
# define closesocket close
|
||||
# define GET_LAST_ERROR() (sock_errno())
|
||||
|
||||
/* Includes needed for OS/2 systems */
|
||||
# include <types.h>
|
||||
# include <unistd.h>
|
||||
# include <sys/ioctl.h>
|
||||
# include <sys/socket.h>
|
||||
# include <netinet/in.h>
|
||||
# include <netinet/tcp.h>
|
||||
# include <arpa/inet.h>
|
||||
# include <net/if.h>
|
||||
# include <errno.h>
|
||||
# include <sys/time.h>
|
||||
# include <netdb.h>
|
||||
# include <nerrno.h>
|
||||
# define INADDR_NONE 0xffffffff
|
||||
# include "../../3rdparty/os2/getaddrinfo.h"
|
||||
# include "../../3rdparty/os2/getnameinfo.h"
|
||||
|
||||
#define IPV6_V6ONLY 27
|
||||
|
||||
/*
|
||||
* IPv6 address
|
||||
*/
|
||||
struct in6_addr {
|
||||
union {
|
||||
uint8_t __u6_addr8[16];
|
||||
uint16_t __u6_addr16[8];
|
||||
uint32_t __u6_addr32[4];
|
||||
} __u6_addr; /* 128-bit IP6 address */
|
||||
};
|
||||
|
||||
#define s6_addr __u6_addr.__u6_addr8
|
||||
|
||||
struct sockaddr_in6 {
|
||||
uint8_t sin6_len; /* length of this struct */
|
||||
sa_family_t sin6_family; /* AF_INET6 */
|
||||
in_port_t sin6_port; /* Transport layer port # */
|
||||
uint32_t sin6_flowinfo; /* IP6 flow information */
|
||||
struct in6_addr sin6_addr; /* IP6 address */
|
||||
uint32_t sin6_scope_id; /* scope zone index */
|
||||
};
|
||||
|
||||
typedef int socklen_t;
|
||||
#if !defined(__INNOTEK_LIBC__)
|
||||
typedef unsigned long in_addr_t;
|
||||
#endif /* __INNOTEK_LIBC__ */
|
||||
|
||||
#endif /* OS/2 */
|
||||
|
||||
/* MorphOS and Amiga stuff */
|
||||
#if defined(__MORPHOS__) || defined(__AMIGA__)
|
||||
# include <exec/types.h>
|
||||
# include <proto/exec.h> /* required for Open/CloseLibrary() */
|
||||
/* MorphOS defines his network functions with UBYTE arrays while we
|
||||
* use char arrays. This gives tons of unneeded warnings */
|
||||
# define UBYTE char
|
||||
# if defined(__MORPHOS__)
|
||||
# include <sys/filio.h> /* FIO* defines */
|
||||
# include <sys/sockio.h> /* SIO* defines */
|
||||
# include <netinet/in.h>
|
||||
# else /* __AMIGA__ */
|
||||
# include <proto/socket.h>
|
||||
# endif
|
||||
|
||||
/* Make the names compatible */
|
||||
# define closesocket(s) CloseSocket(s)
|
||||
# define GET_LAST_ERROR() Errno()
|
||||
# define ioctlsocket(s, request, status) IoctlSocket((LONG)s, (ULONG)request, (char*)status)
|
||||
# define ioctl ioctlsocket
|
||||
|
||||
typedef unsigned int in_addr_t;
|
||||
typedef long socklen_t;
|
||||
extern struct Library *SocketBase;
|
||||
|
||||
# ifdef __AMIGA__
|
||||
/* for usleep() implementation */
|
||||
extern struct Device *TimerBase;
|
||||
extern struct MsgPort *TimerPort;
|
||||
extern struct timerequest *TimerRequest;
|
||||
# endif
|
||||
#endif /* __MORPHOS__ || __AMIGA__ */
|
||||
|
||||
/**
|
||||
* Try to set the socket into non-blocking mode.
|
||||
* @param d The socket to set the non-blocking more for.
|
||||
* @return True if setting the non-blocking mode succeeded, otherwise false.
|
||||
*/
|
||||
static inline bool SetNonBlocking(SOCKET d)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
u_long nonblocking = 1;
|
||||
#else
|
||||
int nonblocking = 1;
|
||||
#endif
|
||||
#if (defined(__BEOS__) && defined(BEOS_NET_SERVER))
|
||||
return setsockopt(d, SOL_SOCKET, SO_NONBLOCK, &nonblocking, sizeof(nonblocking)) == 0;
|
||||
#else
|
||||
return ioctlsocket(d, FIONBIO, &nonblocking) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to set the socket to not delay sending.
|
||||
* @param d The socket to disable the delaying for.
|
||||
* @return True if disabling the delaying succeeded, otherwise false.
|
||||
*/
|
||||
static inline bool SetNoDelay(SOCKET d)
|
||||
{
|
||||
/* XXX should this be done at all? */
|
||||
#if !defined(BEOS_NET_SERVER) /* not implemented on BeOS net_server */
|
||||
int b = 1;
|
||||
/* The (const char*) cast is needed for windows */
|
||||
return setsockopt(d, IPPROTO_TCP, TCP_NODELAY, (const char*)&b, sizeof(b)) == 0;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Make sure these structures have the size we expect them to be */
|
||||
assert_compile(sizeof(in_addr) == 4); ///< IPv4 addresses should be 4 bytes.
|
||||
assert_compile(sizeof(in6_addr) == 16); ///< IPv6 addresses should be 16 bytes.
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
|
||||
#endif /* NETWORK_CORE_OS_ABSTRACTION_H */
|
||||
314
src/network/core/packet.cpp
Normal file
314
src/network/core/packet.cpp
Normal file
@@ -0,0 +1,314 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file packet.cpp Basic functions to create, fill and read packets.
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
#include "../../stdafx.h"
|
||||
#include "../../string_func.h"
|
||||
|
||||
#include "packet.h"
|
||||
|
||||
#include "../../safeguards.h"
|
||||
|
||||
/**
|
||||
* Create a packet that is used to read from a network socket
|
||||
* @param cs the socket handler associated with the socket we are reading from
|
||||
*/
|
||||
Packet::Packet(NetworkSocketHandler *cs)
|
||||
{
|
||||
assert(cs != NULL);
|
||||
|
||||
this->cs = cs;
|
||||
this->next = NULL;
|
||||
this->pos = 0; // We start reading from here
|
||||
this->size = 0;
|
||||
this->buffer = MallocT<byte>(SEND_MTU);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet to send
|
||||
* @param type of the packet to send
|
||||
*/
|
||||
Packet::Packet(PacketType type)
|
||||
{
|
||||
this->cs = NULL;
|
||||
this->next = NULL;
|
||||
|
||||
/* Skip the size so we can write that in before sending the packet */
|
||||
this->pos = 0;
|
||||
this->size = sizeof(PacketSize);
|
||||
this->buffer = MallocT<byte>(SEND_MTU);
|
||||
this->buffer[this->size++] = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free the buffer of this packet.
|
||||
*/
|
||||
Packet::~Packet()
|
||||
{
|
||||
free(this->buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the packet size from the raw packet from packet->size
|
||||
*/
|
||||
void Packet::PrepareToSend()
|
||||
{
|
||||
assert(this->cs == NULL && this->next == NULL);
|
||||
|
||||
this->buffer[0] = GB(this->size, 0, 8);
|
||||
this->buffer[1] = GB(this->size, 8, 8);
|
||||
|
||||
this->pos = 0; // We start reading from here
|
||||
}
|
||||
|
||||
/*
|
||||
* The next couple of functions make sure we can send
|
||||
* uint8, uint16, uint32 and uint64 endian-safe
|
||||
* over the network. The least significant bytes are
|
||||
* sent first.
|
||||
*
|
||||
* So 0x01234567 would be sent as 67 45 23 01.
|
||||
*
|
||||
* A bool is sent as a uint8 where zero means false
|
||||
* and non-zero means true.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Package a boolean in the packet.
|
||||
* @param data The data to send.
|
||||
*/
|
||||
void Packet::Send_bool(bool data)
|
||||
{
|
||||
this->Send_uint8(data ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Package a 8 bits integer in the packet.
|
||||
* @param data The data to send.
|
||||
*/
|
||||
void Packet::Send_uint8(uint8 data)
|
||||
{
|
||||
assert(this->size < SEND_MTU - sizeof(data));
|
||||
this->buffer[this->size++] = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Package a 16 bits integer in the packet.
|
||||
* @param data The data to send.
|
||||
*/
|
||||
void Packet::Send_uint16(uint16 data)
|
||||
{
|
||||
assert(this->size < SEND_MTU - sizeof(data));
|
||||
this->buffer[this->size++] = GB(data, 0, 8);
|
||||
this->buffer[this->size++] = GB(data, 8, 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Package a 32 bits integer in the packet.
|
||||
* @param data The data to send.
|
||||
*/
|
||||
void Packet::Send_uint32(uint32 data)
|
||||
{
|
||||
assert(this->size < SEND_MTU - sizeof(data));
|
||||
this->buffer[this->size++] = GB(data, 0, 8);
|
||||
this->buffer[this->size++] = GB(data, 8, 8);
|
||||
this->buffer[this->size++] = GB(data, 16, 8);
|
||||
this->buffer[this->size++] = GB(data, 24, 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Package a 64 bits integer in the packet.
|
||||
* @param data The data to send.
|
||||
*/
|
||||
void Packet::Send_uint64(uint64 data)
|
||||
{
|
||||
assert(this->size < SEND_MTU - sizeof(data));
|
||||
this->buffer[this->size++] = GB(data, 0, 8);
|
||||
this->buffer[this->size++] = GB(data, 8, 8);
|
||||
this->buffer[this->size++] = GB(data, 16, 8);
|
||||
this->buffer[this->size++] = GB(data, 24, 8);
|
||||
this->buffer[this->size++] = GB(data, 32, 8);
|
||||
this->buffer[this->size++] = GB(data, 40, 8);
|
||||
this->buffer[this->size++] = GB(data, 48, 8);
|
||||
this->buffer[this->size++] = GB(data, 56, 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a string over the network. It sends out
|
||||
* the string + '\0'. No size-byte or something.
|
||||
* @param data The string to send
|
||||
*/
|
||||
void Packet::Send_string(const char *data)
|
||||
{
|
||||
assert(data != NULL);
|
||||
/* The <= *is* valid due to the fact that we are comparing sizes and not the index. */
|
||||
assert(this->size + strlen(data) + 1 <= SEND_MTU);
|
||||
while ((this->buffer[this->size++] = *data++) != '\0') {}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Receiving commands
|
||||
* Again, the next couple of functions are endian-safe
|
||||
* see the comment before Send_bool for more info.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Is it safe to read from the packet, i.e. didn't we run over the buffer ?
|
||||
* @param bytes_to_read The amount of bytes we want to try to read.
|
||||
* @return True if that is safe, otherwise false.
|
||||
*/
|
||||
bool Packet::CanReadFromPacket(uint bytes_to_read)
|
||||
{
|
||||
/* Don't allow reading from a quit client/client who send bad data */
|
||||
if (this->cs->HasClientQuit()) return false;
|
||||
|
||||
/* Check if variable is within packet-size */
|
||||
if (this->pos + bytes_to_read > this->size) {
|
||||
this->cs->NetworkSocketHandler::CloseConnection();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the packet size from the raw packet and stores it in the packet->size
|
||||
*/
|
||||
void Packet::ReadRawPacketSize()
|
||||
{
|
||||
assert(this->cs != NULL && this->next == NULL);
|
||||
this->size = (PacketSize)this->buffer[0];
|
||||
this->size += (PacketSize)this->buffer[1] << 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the packet so it can be read
|
||||
*/
|
||||
void Packet::PrepareToRead()
|
||||
{
|
||||
this->ReadRawPacketSize();
|
||||
|
||||
/* Put the position on the right place */
|
||||
this->pos = sizeof(PacketSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a boolean from the packet.
|
||||
* @return The read data.
|
||||
*/
|
||||
bool Packet::Recv_bool()
|
||||
{
|
||||
return this->Recv_uint8() != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a 8 bits integer from the packet.
|
||||
* @return The read data.
|
||||
*/
|
||||
uint8 Packet::Recv_uint8()
|
||||
{
|
||||
uint8 n;
|
||||
|
||||
if (!this->CanReadFromPacket(sizeof(n))) return 0;
|
||||
|
||||
n = this->buffer[this->pos++];
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a 16 bits integer from the packet.
|
||||
* @return The read data.
|
||||
*/
|
||||
uint16 Packet::Recv_uint16()
|
||||
{
|
||||
uint16 n;
|
||||
|
||||
if (!this->CanReadFromPacket(sizeof(n))) return 0;
|
||||
|
||||
n = (uint16)this->buffer[this->pos++];
|
||||
n += (uint16)this->buffer[this->pos++] << 8;
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a 32 bits integer from the packet.
|
||||
* @return The read data.
|
||||
*/
|
||||
uint32 Packet::Recv_uint32()
|
||||
{
|
||||
uint32 n;
|
||||
|
||||
if (!this->CanReadFromPacket(sizeof(n))) return 0;
|
||||
|
||||
n = (uint32)this->buffer[this->pos++];
|
||||
n += (uint32)this->buffer[this->pos++] << 8;
|
||||
n += (uint32)this->buffer[this->pos++] << 16;
|
||||
n += (uint32)this->buffer[this->pos++] << 24;
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a 64 bits integer from the packet.
|
||||
* @return The read data.
|
||||
*/
|
||||
uint64 Packet::Recv_uint64()
|
||||
{
|
||||
uint64 n;
|
||||
|
||||
if (!this->CanReadFromPacket(sizeof(n))) return 0;
|
||||
|
||||
n = (uint64)this->buffer[this->pos++];
|
||||
n += (uint64)this->buffer[this->pos++] << 8;
|
||||
n += (uint64)this->buffer[this->pos++] << 16;
|
||||
n += (uint64)this->buffer[this->pos++] << 24;
|
||||
n += (uint64)this->buffer[this->pos++] << 32;
|
||||
n += (uint64)this->buffer[this->pos++] << 40;
|
||||
n += (uint64)this->buffer[this->pos++] << 48;
|
||||
n += (uint64)this->buffer[this->pos++] << 56;
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a string till it finds a '\0' in the stream.
|
||||
* @param buffer The buffer to put the data into.
|
||||
* @param size The size of the buffer.
|
||||
* @param settings The string validation settings.
|
||||
*/
|
||||
void Packet::Recv_string(char *buffer, size_t size, StringValidationSettings settings)
|
||||
{
|
||||
PacketSize pos;
|
||||
char *bufp = buffer;
|
||||
const char *last = buffer + size - 1;
|
||||
|
||||
/* Don't allow reading from a closed socket */
|
||||
if (cs->HasClientQuit()) return;
|
||||
|
||||
pos = this->pos;
|
||||
while (--size > 0 && pos < this->size && (*buffer++ = this->buffer[pos++]) != '\0') {}
|
||||
|
||||
if (size == 0 || pos == this->size) {
|
||||
*buffer = '\0';
|
||||
/* If size was sooner to zero then the string in the stream
|
||||
* skip till the \0, so than packet can be read out correctly for the rest */
|
||||
while (pos < this->size && this->buffer[pos] != '\0') pos++;
|
||||
pos++;
|
||||
}
|
||||
this->pos = pos;
|
||||
|
||||
str_validate(bufp, last, settings);
|
||||
}
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
92
src/network/core/packet.h
Normal file
92
src/network/core/packet.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file packet.h Basic functions to create, fill and read packets.
|
||||
*/
|
||||
|
||||
#ifndef NETWORK_CORE_PACKET_H
|
||||
#define NETWORK_CORE_PACKET_H
|
||||
|
||||
#include "config.h"
|
||||
#include "core.h"
|
||||
#include "../../string_type.h"
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
typedef uint16 PacketSize; ///< Size of the whole packet.
|
||||
typedef uint8 PacketType; ///< Identifier for the packet
|
||||
|
||||
/**
|
||||
* Internal entity of a packet. As everything is sent as a packet,
|
||||
* all network communication will need to call the functions that
|
||||
* populate the packet.
|
||||
* Every packet can be at most SEND_MTU bytes. Overflowing this
|
||||
* limit will give an assertion when sending (i.e. writing) the
|
||||
* packet. Reading past the size of the packet when receiving
|
||||
* will return all 0 values and "" in case of the string.
|
||||
*
|
||||
* --- Points of attention ---
|
||||
* - all > 1 byte integral values are written in little endian,
|
||||
* unless specified otherwise.
|
||||
* Thus, 0x01234567 would be sent as {0x67, 0x45, 0x23, 0x01}.
|
||||
* - all sent strings are of variable length and terminated by a '\0'.
|
||||
* Thus, the length of the strings is not sent.
|
||||
* - years that are leap years in the 'days since X' to 'date' calculations:
|
||||
* (year % 4 == 0) and ((year % 100 != 0) or (year % 400 == 0))
|
||||
*/
|
||||
struct Packet {
|
||||
/** The next packet. Used for queueing packets before sending. */
|
||||
Packet *next;
|
||||
/**
|
||||
* The size of the whole packet for received packets. For packets
|
||||
* that will be sent, the value is filled in just before the
|
||||
* actual transmission.
|
||||
*/
|
||||
PacketSize size;
|
||||
/** The current read/write position in the packet */
|
||||
PacketSize pos;
|
||||
/** The buffer of this packet, of basically variable length up to SEND_MTU. */
|
||||
byte *buffer;
|
||||
|
||||
private:
|
||||
/** Socket we're associated with. */
|
||||
NetworkSocketHandler *cs;
|
||||
|
||||
public:
|
||||
Packet(NetworkSocketHandler *cs);
|
||||
Packet(PacketType type);
|
||||
~Packet();
|
||||
|
||||
/* Sending/writing of packets */
|
||||
void PrepareToSend();
|
||||
|
||||
void Send_bool (bool data);
|
||||
void Send_uint8 (uint8 data);
|
||||
void Send_uint16(uint16 data);
|
||||
void Send_uint32(uint32 data);
|
||||
void Send_uint64(uint64 data);
|
||||
void Send_string(const char *data);
|
||||
|
||||
/* Reading/receiving of packets */
|
||||
void ReadRawPacketSize();
|
||||
void PrepareToRead();
|
||||
|
||||
bool CanReadFromPacket (uint bytes_to_read);
|
||||
bool Recv_bool ();
|
||||
uint8 Recv_uint8 ();
|
||||
uint16 Recv_uint16();
|
||||
uint32 Recv_uint32();
|
||||
uint64 Recv_uint64();
|
||||
void Recv_string(char *buffer, size_t size, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK);
|
||||
};
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
|
||||
#endif /* NETWORK_CORE_PACKET_H */
|
||||
251
src/network/core/tcp.cpp
Normal file
251
src/network/core/tcp.cpp
Normal file
@@ -0,0 +1,251 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file tcp.cpp Basic functions to receive and send TCP packets.
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
#include "../../stdafx.h"
|
||||
#include "../../debug.h"
|
||||
|
||||
#include "tcp.h"
|
||||
|
||||
#include "../../safeguards.h"
|
||||
|
||||
/**
|
||||
* Construct a socket handler for a TCP connection.
|
||||
* @param s The just opened TCP connection.
|
||||
*/
|
||||
NetworkTCPSocketHandler::NetworkTCPSocketHandler(SOCKET s) :
|
||||
NetworkSocketHandler(),
|
||||
packet_queue(NULL), packet_recv(NULL),
|
||||
sock(s), writable(false)
|
||||
{
|
||||
}
|
||||
|
||||
NetworkTCPSocketHandler::~NetworkTCPSocketHandler()
|
||||
{
|
||||
this->CloseConnection();
|
||||
|
||||
if (this->sock != INVALID_SOCKET) closesocket(this->sock);
|
||||
this->sock = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
NetworkRecvStatus NetworkTCPSocketHandler::CloseConnection(bool error)
|
||||
{
|
||||
this->writable = false;
|
||||
NetworkSocketHandler::CloseConnection(error);
|
||||
|
||||
/* Free all pending and partially received packets */
|
||||
while (this->packet_queue != NULL) {
|
||||
Packet *p = this->packet_queue->next;
|
||||
delete this->packet_queue;
|
||||
this->packet_queue = p;
|
||||
}
|
||||
delete this->packet_recv;
|
||||
this->packet_recv = NULL;
|
||||
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function puts the packet in the send-queue and it is send as
|
||||
* soon as possible. This is the next tick, or maybe one tick later
|
||||
* if the OS-network-buffer is full)
|
||||
* @param packet the packet to send
|
||||
*/
|
||||
void NetworkTCPSocketHandler::SendPacket(Packet *packet)
|
||||
{
|
||||
Packet *p;
|
||||
assert(packet != NULL);
|
||||
|
||||
packet->PrepareToSend();
|
||||
|
||||
/* Reallocate the packet as in 99+% of the times we send at most 25 bytes and
|
||||
* keeping the other 1400+ bytes wastes memory, especially when someone tries
|
||||
* to do a denial of service attack! */
|
||||
packet->buffer = ReallocT(packet->buffer, packet->size);
|
||||
|
||||
/* Locate last packet buffered for the client */
|
||||
p = this->packet_queue;
|
||||
if (p == NULL) {
|
||||
/* No packets yet */
|
||||
this->packet_queue = packet;
|
||||
} else {
|
||||
/* Skip to the last packet */
|
||||
while (p->next != NULL) p = p->next;
|
||||
p->next = packet;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends all the buffered packets out for this client. It stops when:
|
||||
* 1) all packets are send (queue is empty)
|
||||
* 2) the OS reports back that it can not send any more
|
||||
* data right now (full network-buffer, it happens ;))
|
||||
* 3) sending took too long
|
||||
* @param closing_down Whether we are closing down the connection.
|
||||
* @return \c true if a (part of a) packet could be sent and
|
||||
* the connection is not closed yet.
|
||||
*/
|
||||
SendPacketsState NetworkTCPSocketHandler::SendPackets(bool closing_down)
|
||||
{
|
||||
ssize_t res;
|
||||
Packet *p;
|
||||
|
||||
/* We can not write to this socket!! */
|
||||
if (!this->writable) return SPS_NONE_SENT;
|
||||
if (!this->IsConnected()) return SPS_CLOSED;
|
||||
|
||||
p = this->packet_queue;
|
||||
while (p != NULL) {
|
||||
res = send(this->sock, (const char*)p->buffer + p->pos, p->size - p->pos, 0);
|
||||
if (res == -1) {
|
||||
int err = GET_LAST_ERROR();
|
||||
if (err != EWOULDBLOCK) {
|
||||
/* Something went wrong.. close client! */
|
||||
if (!closing_down) {
|
||||
DEBUG(net, 0, "send failed with error %d", err);
|
||||
this->CloseConnection();
|
||||
}
|
||||
return SPS_CLOSED;
|
||||
}
|
||||
return SPS_PARTLY_SENT;
|
||||
}
|
||||
if (res == 0) {
|
||||
/* Client/server has left us :( */
|
||||
if (!closing_down) this->CloseConnection();
|
||||
return SPS_CLOSED;
|
||||
}
|
||||
|
||||
p->pos += res;
|
||||
|
||||
/* Is this packet sent? */
|
||||
if (p->pos == p->size) {
|
||||
/* Go to the next packet */
|
||||
this->packet_queue = p->next;
|
||||
delete p;
|
||||
p = this->packet_queue;
|
||||
} else {
|
||||
return SPS_PARTLY_SENT;
|
||||
}
|
||||
}
|
||||
|
||||
return SPS_ALL_SENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives a packet for the given client
|
||||
* @return The received packet (or NULL when it didn't receive one)
|
||||
*/
|
||||
Packet *NetworkTCPSocketHandler::ReceivePacket()
|
||||
{
|
||||
ssize_t res;
|
||||
|
||||
if (!this->IsConnected()) return NULL;
|
||||
|
||||
if (this->packet_recv == NULL) {
|
||||
this->packet_recv = new Packet(this);
|
||||
}
|
||||
|
||||
Packet *p = this->packet_recv;
|
||||
|
||||
/* Read packet size */
|
||||
if (p->pos < sizeof(PacketSize)) {
|
||||
while (p->pos < sizeof(PacketSize)) {
|
||||
/* Read the size of the packet */
|
||||
res = recv(this->sock, (char*)p->buffer + p->pos, sizeof(PacketSize) - p->pos, 0);
|
||||
if (res == -1) {
|
||||
int err = GET_LAST_ERROR();
|
||||
if (err != EWOULDBLOCK) {
|
||||
/* Something went wrong... (104 is connection reset by peer) */
|
||||
if (err != 104) DEBUG(net, 0, "recv failed with error %d", err);
|
||||
this->CloseConnection();
|
||||
return NULL;
|
||||
}
|
||||
/* Connection would block, so stop for now */
|
||||
return NULL;
|
||||
}
|
||||
if (res == 0) {
|
||||
/* Client/server has left */
|
||||
this->CloseConnection();
|
||||
return NULL;
|
||||
}
|
||||
p->pos += res;
|
||||
}
|
||||
|
||||
/* Read the packet size from the received packet */
|
||||
p->ReadRawPacketSize();
|
||||
|
||||
if (p->size > SEND_MTU) {
|
||||
this->CloseConnection();
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read rest of packet */
|
||||
while (p->pos < p->size) {
|
||||
res = recv(this->sock, (char*)p->buffer + p->pos, p->size - p->pos, 0);
|
||||
if (res == -1) {
|
||||
int err = GET_LAST_ERROR();
|
||||
if (err != EWOULDBLOCK) {
|
||||
/* Something went wrong... (104 is connection reset by peer) */
|
||||
if (err != 104) DEBUG(net, 0, "recv failed with error %d", err);
|
||||
this->CloseConnection();
|
||||
return NULL;
|
||||
}
|
||||
/* Connection would block */
|
||||
return NULL;
|
||||
}
|
||||
if (res == 0) {
|
||||
/* Client/server has left */
|
||||
this->CloseConnection();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p->pos += res;
|
||||
}
|
||||
|
||||
/* Prepare for receiving a new packet */
|
||||
this->packet_recv = NULL;
|
||||
|
||||
p->PrepareToRead();
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this socket can send or receive something.
|
||||
* @return \c true when there is something to receive.
|
||||
* @note Sets #writable if more data can be sent.
|
||||
*/
|
||||
bool NetworkTCPSocketHandler::CanSendReceive()
|
||||
{
|
||||
fd_set read_fd, write_fd;
|
||||
struct timeval tv;
|
||||
|
||||
FD_ZERO(&read_fd);
|
||||
FD_ZERO(&write_fd);
|
||||
|
||||
FD_SET(this->sock, &read_fd);
|
||||
FD_SET(this->sock, &write_fd);
|
||||
|
||||
tv.tv_sec = tv.tv_usec = 0; // don't block at all.
|
||||
#if !defined(__MORPHOS__) && !defined(__AMIGA__)
|
||||
if (select(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv) < 0) return false;
|
||||
#else
|
||||
if (WaitSelect(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv, NULL) < 0) return false;
|
||||
#endif
|
||||
|
||||
this->writable = !!FD_ISSET(this->sock, &write_fd);
|
||||
return FD_ISSET(this->sock, &read_fd) != 0;
|
||||
}
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
104
src/network/core/tcp.h
Normal file
104
src/network/core/tcp.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file tcp.h Basic functions to receive and send TCP packets.
|
||||
*/
|
||||
|
||||
#ifndef NETWORK_CORE_TCP_H
|
||||
#define NETWORK_CORE_TCP_H
|
||||
|
||||
#include "address.h"
|
||||
#include "packet.h"
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
/** The states of sending the packets. */
|
||||
enum SendPacketsState {
|
||||
SPS_CLOSED, ///< The connection got closed.
|
||||
SPS_NONE_SENT, ///< The buffer is still full, so no (parts of) packets could be sent.
|
||||
SPS_PARTLY_SENT, ///< The packets are partly sent; there are more packets to be sent in the queue.
|
||||
SPS_ALL_SENT, ///< All packets in the queue are sent.
|
||||
};
|
||||
|
||||
/** Base socket handler for all TCP sockets */
|
||||
class NetworkTCPSocketHandler : public NetworkSocketHandler {
|
||||
private:
|
||||
Packet *packet_queue; ///< Packets that are awaiting delivery
|
||||
Packet *packet_recv; ///< Partially received packet
|
||||
public:
|
||||
SOCKET sock; ///< The socket currently connected to
|
||||
bool writable; ///< Can we write to this socket?
|
||||
|
||||
/**
|
||||
* Whether this socket is currently bound to a socket.
|
||||
* @return true when the socket is bound, false otherwise
|
||||
*/
|
||||
bool IsConnected() const { return this->sock != INVALID_SOCKET; }
|
||||
|
||||
virtual NetworkRecvStatus CloseConnection(bool error = true);
|
||||
virtual void SendPacket(Packet *packet);
|
||||
SendPacketsState SendPackets(bool closing_down = false);
|
||||
|
||||
virtual Packet *ReceivePacket();
|
||||
|
||||
bool CanSendReceive();
|
||||
|
||||
/**
|
||||
* Whether there is something pending in the send queue.
|
||||
* @return true when something is pending in the send queue.
|
||||
*/
|
||||
bool HasSendQueue() { return this->packet_queue != NULL; }
|
||||
|
||||
NetworkTCPSocketHandler(SOCKET s = INVALID_SOCKET);
|
||||
~NetworkTCPSocketHandler();
|
||||
};
|
||||
|
||||
/**
|
||||
* "Helper" class for creating TCP connections in a non-blocking manner
|
||||
*/
|
||||
class TCPConnecter {
|
||||
private:
|
||||
class ThreadObject *thread; ///< Thread used to create the TCP connection
|
||||
bool connected; ///< Whether we succeeded in making the connection
|
||||
bool aborted; ///< Whether we bailed out (i.e. connection making failed)
|
||||
bool killed; ///< Whether we got killed
|
||||
SOCKET sock; ///< The socket we're connecting with
|
||||
|
||||
void Connect();
|
||||
|
||||
static void ThreadEntry(void *param);
|
||||
|
||||
protected:
|
||||
/** Address we're connecting to */
|
||||
NetworkAddress address;
|
||||
|
||||
public:
|
||||
TCPConnecter(const NetworkAddress &address);
|
||||
/** Silence the warnings */
|
||||
virtual ~TCPConnecter() {}
|
||||
|
||||
/**
|
||||
* Callback when the connection succeeded.
|
||||
* @param s the socket that we opened
|
||||
*/
|
||||
virtual void OnConnect(SOCKET s) {}
|
||||
|
||||
/**
|
||||
* Callback for when the connection attempt failed.
|
||||
*/
|
||||
virtual void OnFailure() {}
|
||||
|
||||
static void CheckCallbacks();
|
||||
static void KillAll();
|
||||
};
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
|
||||
#endif /* NETWORK_CORE_TCP_H */
|
||||
176
src/network/core/tcp_admin.cpp
Normal file
176
src/network/core/tcp_admin.cpp
Normal file
@@ -0,0 +1,176 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file tcp_admin.cpp Basic functions to receive and send TCP packets to and from the admin network.
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
#include "../../stdafx.h"
|
||||
|
||||
#include "../network_internal.h"
|
||||
#include "tcp_admin.h"
|
||||
#include "../../debug.h"
|
||||
|
||||
#include "../../safeguards.h"
|
||||
|
||||
/* Make sure that these enums match. */
|
||||
assert_compile((int)CRR_MANUAL == (int)ADMIN_CRR_MANUAL);
|
||||
assert_compile((int)CRR_AUTOCLEAN == (int)ADMIN_CRR_AUTOCLEAN);
|
||||
assert_compile((int)CRR_BANKRUPT == (int)ADMIN_CRR_BANKRUPT);
|
||||
assert_compile((int)CRR_END == (int)ADMIN_CRR_END);
|
||||
|
||||
/**
|
||||
* Create the admin handler for the given socket.
|
||||
* @param s The socket to communicate over.
|
||||
*/
|
||||
NetworkAdminSocketHandler::NetworkAdminSocketHandler(SOCKET s) : status(ADMIN_STATUS_INACTIVE)
|
||||
{
|
||||
this->sock = s;
|
||||
this->admin_name[0] = '\0';
|
||||
this->admin_version[0] = '\0';
|
||||
}
|
||||
|
||||
NetworkAdminSocketHandler::~NetworkAdminSocketHandler()
|
||||
{
|
||||
}
|
||||
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::CloseConnection(bool error)
|
||||
{
|
||||
delete this;
|
||||
return NETWORK_RECV_STATUS_CONN_LOST;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the given packet, i.e. pass it to the right parser receive command.
|
||||
* @param p the packet to handle.
|
||||
* @return #NetworkRecvStatus of handling.
|
||||
*/
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::HandlePacket(Packet *p)
|
||||
{
|
||||
PacketAdminType type = (PacketAdminType)p->Recv_uint8();
|
||||
|
||||
switch (this->HasClientQuit() ? INVALID_ADMIN_PACKET : type) {
|
||||
case ADMIN_PACKET_ADMIN_JOIN: return this->Receive_ADMIN_JOIN(p);
|
||||
case ADMIN_PACKET_ADMIN_QUIT: return this->Receive_ADMIN_QUIT(p);
|
||||
case ADMIN_PACKET_ADMIN_UPDATE_FREQUENCY: return this->Receive_ADMIN_UPDATE_FREQUENCY(p);
|
||||
case ADMIN_PACKET_ADMIN_POLL: return this->Receive_ADMIN_POLL(p);
|
||||
case ADMIN_PACKET_ADMIN_CHAT: return this->Receive_ADMIN_CHAT(p);
|
||||
case ADMIN_PACKET_ADMIN_RCON: return this->Receive_ADMIN_RCON(p);
|
||||
case ADMIN_PACKET_ADMIN_GAMESCRIPT: return this->Receive_ADMIN_GAMESCRIPT(p);
|
||||
case ADMIN_PACKET_ADMIN_PING: return this->Receive_ADMIN_PING(p);
|
||||
|
||||
case ADMIN_PACKET_SERVER_FULL: return this->Receive_SERVER_FULL(p);
|
||||
case ADMIN_PACKET_SERVER_BANNED: return this->Receive_SERVER_BANNED(p);
|
||||
case ADMIN_PACKET_SERVER_ERROR: return this->Receive_SERVER_ERROR(p);
|
||||
case ADMIN_PACKET_SERVER_PROTOCOL: return this->Receive_SERVER_PROTOCOL(p);
|
||||
case ADMIN_PACKET_SERVER_WELCOME: return this->Receive_SERVER_WELCOME(p);
|
||||
case ADMIN_PACKET_SERVER_NEWGAME: return this->Receive_SERVER_NEWGAME(p);
|
||||
case ADMIN_PACKET_SERVER_SHUTDOWN: return this->Receive_SERVER_SHUTDOWN(p);
|
||||
|
||||
case ADMIN_PACKET_SERVER_DATE: return this->Receive_SERVER_DATE(p);
|
||||
case ADMIN_PACKET_SERVER_CLIENT_JOIN: return this->Receive_SERVER_CLIENT_JOIN(p);
|
||||
case ADMIN_PACKET_SERVER_CLIENT_INFO: return this->Receive_SERVER_CLIENT_INFO(p);
|
||||
case ADMIN_PACKET_SERVER_CLIENT_UPDATE: return this->Receive_SERVER_CLIENT_UPDATE(p);
|
||||
case ADMIN_PACKET_SERVER_CLIENT_QUIT: return this->Receive_SERVER_CLIENT_QUIT(p);
|
||||
case ADMIN_PACKET_SERVER_CLIENT_ERROR: return this->Receive_SERVER_CLIENT_ERROR(p);
|
||||
case ADMIN_PACKET_SERVER_COMPANY_NEW: return this->Receive_SERVER_COMPANY_NEW(p);
|
||||
case ADMIN_PACKET_SERVER_COMPANY_INFO: return this->Receive_SERVER_COMPANY_INFO(p);
|
||||
case ADMIN_PACKET_SERVER_COMPANY_UPDATE: return this->Receive_SERVER_COMPANY_UPDATE(p);
|
||||
case ADMIN_PACKET_SERVER_COMPANY_REMOVE: return this->Receive_SERVER_COMPANY_REMOVE(p);
|
||||
case ADMIN_PACKET_SERVER_COMPANY_ECONOMY: return this->Receive_SERVER_COMPANY_ECONOMY(p);
|
||||
case ADMIN_PACKET_SERVER_COMPANY_STATS: return this->Receive_SERVER_COMPANY_STATS(p);
|
||||
case ADMIN_PACKET_SERVER_CHAT: return this->Receive_SERVER_CHAT(p);
|
||||
case ADMIN_PACKET_SERVER_RCON: return this->Receive_SERVER_RCON(p);
|
||||
case ADMIN_PACKET_SERVER_CONSOLE: return this->Receive_SERVER_CONSOLE(p);
|
||||
case ADMIN_PACKET_SERVER_CMD_NAMES: return this->Receive_SERVER_CMD_NAMES(p);
|
||||
case ADMIN_PACKET_SERVER_CMD_LOGGING: return this->Receive_SERVER_CMD_LOGGING(p);
|
||||
case ADMIN_PACKET_SERVER_RCON_END: return this->Receive_SERVER_RCON_END(p);
|
||||
case ADMIN_PACKET_SERVER_PONG: return this->Receive_SERVER_PONG(p);
|
||||
|
||||
default:
|
||||
if (this->HasClientQuit()) {
|
||||
DEBUG(net, 0, "[tcp/admin] received invalid packet type %d from '%s' (%s)", type, this->admin_name, this->admin_version);
|
||||
} else {
|
||||
DEBUG(net, 0, "[tcp/admin] received illegal packet from '%s' (%s)", this->admin_name, this->admin_version);
|
||||
}
|
||||
|
||||
this->CloseConnection();
|
||||
return NETWORK_RECV_STATUS_MALFORMED_PACKET;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the actual receiving of packets.
|
||||
* As long as HandlePacket returns OKAY packets are handled. Upon
|
||||
* failure, or no more packets to process the last result of
|
||||
* HandlePacket is returned.
|
||||
* @return #NetworkRecvStatus of the last handled packet.
|
||||
*/
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::ReceivePackets()
|
||||
{
|
||||
Packet *p;
|
||||
while ((p = this->ReceivePacket()) != NULL) {
|
||||
NetworkRecvStatus res = this->HandlePacket(p);
|
||||
if (res != NETWORK_RECV_STATUS_OKAY) return res;
|
||||
}
|
||||
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for logging receiving invalid packets.
|
||||
* @param type The received packet type.
|
||||
* @return The status the network should have, in this case: "malformed packet error".
|
||||
*/
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::ReceiveInvalidPacket(PacketAdminType type)
|
||||
{
|
||||
DEBUG(net, 0, "[tcp/admin] received illegal packet type %d from admin %s (%s)", type, this->admin_name, this->admin_version);
|
||||
return NETWORK_RECV_STATUS_MALFORMED_PACKET;
|
||||
}
|
||||
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_ADMIN_JOIN(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_ADMIN_JOIN); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_ADMIN_QUIT(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_ADMIN_QUIT); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_ADMIN_UPDATE_FREQUENCY(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_ADMIN_UPDATE_FREQUENCY); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_ADMIN_POLL(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_ADMIN_POLL); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_ADMIN_CHAT(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_ADMIN_CHAT); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_ADMIN_RCON(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_ADMIN_RCON); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_ADMIN_GAMESCRIPT(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_ADMIN_GAMESCRIPT); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_ADMIN_PING(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_ADMIN_PING); }
|
||||
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_SERVER_FULL(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_SERVER_FULL); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_SERVER_BANNED(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_SERVER_BANNED); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_SERVER_ERROR(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_SERVER_ERROR); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_SERVER_PROTOCOL(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_SERVER_PROTOCOL); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_SERVER_WELCOME(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_SERVER_WELCOME); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_SERVER_NEWGAME(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_SERVER_NEWGAME); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_SERVER_SHUTDOWN(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_SERVER_SHUTDOWN); }
|
||||
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_SERVER_DATE(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_SERVER_DATE); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_SERVER_CLIENT_JOIN(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_SERVER_CLIENT_JOIN); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_SERVER_CLIENT_INFO(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_SERVER_CLIENT_INFO); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_SERVER_CLIENT_UPDATE(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_SERVER_CLIENT_UPDATE); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_SERVER_CLIENT_QUIT(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_SERVER_CLIENT_QUIT); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_SERVER_CLIENT_ERROR(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_SERVER_CLIENT_ERROR); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_SERVER_COMPANY_NEW(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_SERVER_COMPANY_NEW); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_SERVER_COMPANY_INFO(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_SERVER_COMPANY_INFO); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_SERVER_COMPANY_UPDATE(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_SERVER_COMPANY_UPDATE); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_SERVER_COMPANY_REMOVE(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_SERVER_COMPANY_REMOVE); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_SERVER_COMPANY_ECONOMY(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_SERVER_COMPANY_ECONOMY); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_SERVER_COMPANY_STATS(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_SERVER_COMPANY_STATS); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_SERVER_CHAT(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_SERVER_CHAT); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_SERVER_RCON(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_SERVER_RCON); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_SERVER_CONSOLE(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_SERVER_CONSOLE); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_SERVER_CMD_NAMES(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_SERVER_CMD_NAMES); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_SERVER_CMD_LOGGING(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_SERVER_CMD_LOGGING); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_SERVER_RCON_END(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_SERVER_RCON_END); }
|
||||
NetworkRecvStatus NetworkAdminSocketHandler::Receive_SERVER_PONG(Packet *p) { return this->ReceiveInvalidPacket(ADMIN_PACKET_SERVER_PONG); }
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
505
src/network/core/tcp_admin.h
Normal file
505
src/network/core/tcp_admin.h
Normal file
@@ -0,0 +1,505 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file tcp_admin.h Basic functions to receive and send TCP packets to and from the admin network.
|
||||
*/
|
||||
|
||||
#ifndef NETWORK_CORE_TCP_ADMIN_H
|
||||
#define NETWORK_CORE_TCP_ADMIN_H
|
||||
|
||||
#include "os_abstraction.h"
|
||||
#include "tcp.h"
|
||||
#include "../network_type.h"
|
||||
#include "../../core/pool_type.hpp"
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
/**
|
||||
* Enum with types of TCP packets specific to the admin network.
|
||||
* This protocol may only be extended to ensure stability.
|
||||
*/
|
||||
enum PacketAdminType {
|
||||
ADMIN_PACKET_ADMIN_JOIN, ///< The admin announces and authenticates itself to the server.
|
||||
ADMIN_PACKET_ADMIN_QUIT, ///< The admin tells the server that it is quitting.
|
||||
ADMIN_PACKET_ADMIN_UPDATE_FREQUENCY, ///< The admin tells the server the update frequency of a particular piece of information.
|
||||
ADMIN_PACKET_ADMIN_POLL, ///< The admin explicitly polls for a piece of information.
|
||||
ADMIN_PACKET_ADMIN_CHAT, ///< The admin sends a chat message to be distributed.
|
||||
ADMIN_PACKET_ADMIN_RCON, ///< The admin sends a remote console command.
|
||||
ADMIN_PACKET_ADMIN_GAMESCRIPT, ///< The admin sends a JSON string for the GameScript.
|
||||
ADMIN_PACKET_ADMIN_PING, ///< The admin sends a ping to the server, expecting a ping-reply (PONG) packet.
|
||||
|
||||
ADMIN_PACKET_SERVER_FULL = 100, ///< The server tells the admin it cannot accept the admin.
|
||||
ADMIN_PACKET_SERVER_BANNED, ///< The server tells the admin it is banned.
|
||||
ADMIN_PACKET_SERVER_ERROR, ///< The server tells the admin an error has occurred.
|
||||
ADMIN_PACKET_SERVER_PROTOCOL, ///< The server tells the admin its protocol version.
|
||||
ADMIN_PACKET_SERVER_WELCOME, ///< The server welcomes the admin to a game.
|
||||
ADMIN_PACKET_SERVER_NEWGAME, ///< The server tells the admin its going to start a new game.
|
||||
ADMIN_PACKET_SERVER_SHUTDOWN, ///< The server tells the admin its shutting down.
|
||||
|
||||
ADMIN_PACKET_SERVER_DATE, ///< The server tells the admin what the current game date is.
|
||||
ADMIN_PACKET_SERVER_CLIENT_JOIN, ///< The server tells the admin that a client has joined.
|
||||
ADMIN_PACKET_SERVER_CLIENT_INFO, ///< The server gives the admin information about a client.
|
||||
ADMIN_PACKET_SERVER_CLIENT_UPDATE, ///< The server gives the admin an information update on a client.
|
||||
ADMIN_PACKET_SERVER_CLIENT_QUIT, ///< The server tells the admin that a client quit.
|
||||
ADMIN_PACKET_SERVER_CLIENT_ERROR, ///< The server tells the admin that a client caused an error.
|
||||
ADMIN_PACKET_SERVER_COMPANY_NEW, ///< The server tells the admin that a new company has started.
|
||||
ADMIN_PACKET_SERVER_COMPANY_INFO, ///< The server gives the admin information about a company.
|
||||
ADMIN_PACKET_SERVER_COMPANY_UPDATE, ///< The server gives the admin an information update on a company.
|
||||
ADMIN_PACKET_SERVER_COMPANY_REMOVE, ///< The server tells the admin that a company was removed.
|
||||
ADMIN_PACKET_SERVER_COMPANY_ECONOMY, ///< The server gives the admin some economy related company information.
|
||||
ADMIN_PACKET_SERVER_COMPANY_STATS, ///< The server gives the admin some statistics about a company.
|
||||
ADMIN_PACKET_SERVER_CHAT, ///< The server received a chat message and relays it.
|
||||
ADMIN_PACKET_SERVER_RCON, ///< The server's reply to a remove console command.
|
||||
ADMIN_PACKET_SERVER_CONSOLE, ///< The server gives the admin the data that got printed to its console.
|
||||
ADMIN_PACKET_SERVER_CMD_NAMES, ///< The server sends out the names of the DoCommands to the admins.
|
||||
ADMIN_PACKET_SERVER_CMD_LOGGING, ///< The server gives the admin copies of incoming command packets.
|
||||
ADMIN_PACKET_SERVER_GAMESCRIPT, ///< The server gives the admin information from the GameScript in JSON.
|
||||
ADMIN_PACKET_SERVER_RCON_END, ///< The server indicates that the remote console command has completed.
|
||||
ADMIN_PACKET_SERVER_PONG, ///< The server replies to a ping request from the admin.
|
||||
|
||||
INVALID_ADMIN_PACKET = 0xFF, ///< An invalid marker for admin packets.
|
||||
};
|
||||
|
||||
/** Status of an admin. */
|
||||
enum AdminStatus {
|
||||
ADMIN_STATUS_INACTIVE, ///< The admin is not connected nor active.
|
||||
ADMIN_STATUS_ACTIVE, ///< The admin is active.
|
||||
ADMIN_STATUS_END, ///< Must ALWAYS be on the end of this list!! (period)
|
||||
};
|
||||
|
||||
/** Update types an admin can register a frequency for */
|
||||
enum AdminUpdateType {
|
||||
ADMIN_UPDATE_DATE, ///< Updates about the date of the game.
|
||||
ADMIN_UPDATE_CLIENT_INFO, ///< Updates about the information of clients.
|
||||
ADMIN_UPDATE_COMPANY_INFO, ///< Updates about the generic information of companies.
|
||||
ADMIN_UPDATE_COMPANY_ECONOMY, ///< Updates about the economy of companies.
|
||||
ADMIN_UPDATE_COMPANY_STATS, ///< Updates about the statistics of companies.
|
||||
ADMIN_UPDATE_CHAT, ///< The admin would like to have chat messages.
|
||||
ADMIN_UPDATE_CONSOLE, ///< The admin would like to have console messages.
|
||||
ADMIN_UPDATE_CMD_NAMES, ///< The admin would like a list of all DoCommand names.
|
||||
ADMIN_UPDATE_CMD_LOGGING, ///< The admin would like to have DoCommand information.
|
||||
ADMIN_UPDATE_GAMESCRIPT, ///< The admin would like to have gamescript messages.
|
||||
ADMIN_UPDATE_END, ///< Must ALWAYS be on the end of this list!! (period)
|
||||
};
|
||||
|
||||
/** Update frequencies an admin can register. */
|
||||
enum AdminUpdateFrequency {
|
||||
ADMIN_FREQUENCY_POLL = 0x01, ///< The admin can poll this.
|
||||
ADMIN_FREQUENCY_DAILY = 0x02, ///< The admin gets information about this on a daily basis.
|
||||
ADMIN_FREQUENCY_WEEKLY = 0x04, ///< The admin gets information about this on a weekly basis.
|
||||
ADMIN_FREQUENCY_MONTHLY = 0x08, ///< The admin gets information about this on a monthly basis.
|
||||
ADMIN_FREQUENCY_QUARTERLY = 0x10, ///< The admin gets information about this on a quarterly basis.
|
||||
ADMIN_FREQUENCY_ANUALLY = 0x20, ///< The admin gets information about this on a yearly basis.
|
||||
ADMIN_FREQUENCY_AUTOMATIC = 0x40, ///< The admin gets information about this when it changes.
|
||||
};
|
||||
DECLARE_ENUM_AS_BIT_SET(AdminUpdateFrequency)
|
||||
|
||||
/** Reasons for removing a company - communicated to admins. */
|
||||
enum AdminCompanyRemoveReason {
|
||||
ADMIN_CRR_MANUAL, ///< The company is manually removed.
|
||||
ADMIN_CRR_AUTOCLEAN, ///< The company is removed due to autoclean.
|
||||
ADMIN_CRR_BANKRUPT, ///< The company went belly-up.
|
||||
|
||||
ADMIN_CRR_END, ///< Sentinel for end.
|
||||
};
|
||||
|
||||
/** Main socket handler for admin related connections. */
|
||||
class NetworkAdminSocketHandler : public NetworkTCPSocketHandler {
|
||||
protected:
|
||||
char admin_name[NETWORK_CLIENT_NAME_LENGTH]; ///< Name of the admin.
|
||||
char admin_version[NETWORK_REVISION_LENGTH]; ///< Version string of the admin.
|
||||
AdminStatus status; ///< Status of this admin.
|
||||
|
||||
NetworkRecvStatus ReceiveInvalidPacket(PacketAdminType type);
|
||||
|
||||
/**
|
||||
* Join the admin network:
|
||||
* string Password the server is expecting for this network.
|
||||
* string Name of the application being used to connect.
|
||||
* string Version string of the application being used to connect.
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_ADMIN_JOIN(Packet *p);
|
||||
|
||||
/**
|
||||
* Notification to the server that this admin is quitting.
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_ADMIN_QUIT(Packet *p);
|
||||
|
||||
/**
|
||||
* Register updates to be sent at certain frequencies (as announced in the PROTOCOL packet):
|
||||
* uint16 Update type (see #AdminUpdateType).
|
||||
* uint16 Update frequency (see #AdminUpdateFrequency), setting #ADMIN_FREQUENCY_POLL is always ignored.
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_ADMIN_UPDATE_FREQUENCY(Packet *p);
|
||||
|
||||
/**
|
||||
* Poll the server for certain updates, an invalid poll (e.g. not existent id) gets silently dropped:
|
||||
* uint8 #AdminUpdateType the server should answer for, only if #AdminUpdateFrequency #ADMIN_FREQUENCY_POLL is advertised in the PROTOCOL packet.
|
||||
* uint32 ID relevant to the packet type, e.g.
|
||||
* - the client ID for #ADMIN_UPDATE_CLIENT_INFO. Use UINT32_MAX to show all clients.
|
||||
* - the company ID for #ADMIN_UPDATE_COMPANY_INFO. Use UINT32_MAX to show all companies.
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_ADMIN_POLL(Packet *p);
|
||||
|
||||
/**
|
||||
* Send chat as the server:
|
||||
* uint8 Action such as NETWORK_ACTION_CHAT_CLIENT (see #NetworkAction).
|
||||
* uint8 Destination type such as DESTTYPE_BROADCAST (see #DestType).
|
||||
* uint32 ID of the destination such as company or client id.
|
||||
* string Message.
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_ADMIN_CHAT(Packet *p);
|
||||
|
||||
/**
|
||||
* Execute a command on the servers console:
|
||||
* string Command to be executed.
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_ADMIN_RCON(Packet *p);
|
||||
|
||||
/**
|
||||
* Send a JSON string to the current active GameScript.
|
||||
* json JSON string for the GameScript.
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_ADMIN_GAMESCRIPT(Packet *p);
|
||||
|
||||
/**
|
||||
* Ping the server, requiring the server to reply with a pong packet.
|
||||
* uint32 Integer value to pass to the server, which is quoted in the reply.
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_ADMIN_PING(Packet *p);
|
||||
|
||||
/**
|
||||
* The server is full (connection gets closed).
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_FULL(Packet *p);
|
||||
|
||||
/**
|
||||
* The source IP address is banned (connection gets closed).
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_BANNED(Packet *p);
|
||||
|
||||
/**
|
||||
* An error was caused by this admin connection (connection gets closed).
|
||||
* uint8 NetworkErrorCode the error caused.
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_ERROR(Packet *p);
|
||||
|
||||
/**
|
||||
* Inform a just joined admin about the protocol specifics:
|
||||
* uint8 Protocol version.
|
||||
* bool Further protocol data follows (repeats through all update packet types).
|
||||
* uint16 Update packet type.
|
||||
* uint16 Frequencies allowed for this update packet (bitwise).
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_PROTOCOL(Packet *p);
|
||||
|
||||
/**
|
||||
* Welcome a connected admin to the game:
|
||||
* string Name of the Server (e.g. as advertised to master server).
|
||||
* string OpenTTD version string.
|
||||
* bool Server is dedicated.
|
||||
* string Name of the Map.
|
||||
* uint32 Random seed of the Map.
|
||||
* uint8 Landscape of the Map.
|
||||
* uint32 Start date of the Map.
|
||||
* uint16 Map width.
|
||||
* uint16 Map height.
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_WELCOME(Packet *p);
|
||||
|
||||
/**
|
||||
* Notification about a newgame.
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_NEWGAME(Packet *p);
|
||||
|
||||
/**
|
||||
* Notification about the server shutting down.
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_SHUTDOWN(Packet *p);
|
||||
|
||||
/**
|
||||
* Send the current date of the game:
|
||||
* uint32 Current game date.
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_DATE(Packet *p);
|
||||
|
||||
/**
|
||||
* Notification of a new client:
|
||||
* uint32 ID of the new client.
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_CLIENT_JOIN(Packet *p);
|
||||
|
||||
/**
|
||||
* Client information of a specific client:
|
||||
* uint32 ID of the client.
|
||||
* string Network address of the client.
|
||||
* string Name of the client.
|
||||
* uint8 Language of the client.
|
||||
* uint32 Date the client joined the game.
|
||||
* uint8 ID of the company the client is playing as (255 for spectators).
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_CLIENT_INFO(Packet *p);
|
||||
|
||||
/**
|
||||
* Client update details on a specific client (e.g. after rename or move):
|
||||
* uint32 ID of the client.
|
||||
* string Name of the client.
|
||||
* uint8 ID of the company the client is playing as (255 for spectators).
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_CLIENT_UPDATE(Packet *p);
|
||||
|
||||
/**
|
||||
* Notification about a client leaving the game.
|
||||
* uint32 ID of the client that just left.
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_CLIENT_QUIT(Packet *p);
|
||||
|
||||
/**
|
||||
* Notification about a client error (and thus the clients disconnection).
|
||||
* uint32 ID of the client that made the error.
|
||||
* uint8 Error the client made (see NetworkErrorCode).
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_CLIENT_ERROR(Packet *p);
|
||||
|
||||
/**
|
||||
* Notification of a new company:
|
||||
* uint8 ID of the new company.
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_COMPANY_NEW(Packet *p);
|
||||
|
||||
/**
|
||||
* Company information on a specific company:
|
||||
* uint8 ID of the company.
|
||||
* string Name of the company.
|
||||
* string Name of the companies manager.
|
||||
* uint8 Main company colour.
|
||||
* bool Company is password protected.
|
||||
* uint32 Year the company was inaugurated.
|
||||
* bool Company is an AI.
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_COMPANY_INFO(Packet *p);
|
||||
|
||||
/**
|
||||
* Company information of a specific company:
|
||||
* uint8 ID of the company.
|
||||
* string Name of the company.
|
||||
* string Name of the companies manager.
|
||||
* uint8 Main company colour.
|
||||
* bool Company is password protected.
|
||||
* uint8 Quarters of bankruptcy.
|
||||
* uint8 Owner of share 1.
|
||||
* uint8 Owner of share 2.
|
||||
* uint8 Owner of share 3.
|
||||
* uint8 Owner of share 4.
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_COMPANY_UPDATE(Packet *p);
|
||||
|
||||
/**
|
||||
* Notification about a removed company (e.g. due to bankruptcy).
|
||||
* uint8 ID of the company.
|
||||
* uint8 Reason for being removed (see #AdminCompanyRemoveReason).
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_COMPANY_REMOVE(Packet *p);
|
||||
|
||||
/**
|
||||
* Economy update of a specific company:
|
||||
* uint8 ID of the company.
|
||||
* uint64 Money.
|
||||
* uint64 Loan.
|
||||
* int64 Income.
|
||||
* uint16 Delivered cargo (this quarter).
|
||||
* uint64 Company value (last quarter).
|
||||
* uint16 Performance (last quarter).
|
||||
* uint16 Delivered cargo (last quarter).
|
||||
* uint64 Company value (previous quarter).
|
||||
* uint16 Performance (previous quarter).
|
||||
* uint16 Delivered cargo (previous quarter).
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_COMPANY_ECONOMY(Packet *p);
|
||||
|
||||
/**
|
||||
* Company statistics on stations and vehicles:
|
||||
* uint8 ID of the company.
|
||||
* uint16 Number of trains.
|
||||
* uint16 Number of lorries.
|
||||
* uint16 Number of busses.
|
||||
* uint16 Number of planes.
|
||||
* uint16 Number of ships.
|
||||
* uint16 Number of train stations.
|
||||
* uint16 Number of lorry stations.
|
||||
* uint16 Number of bus stops.
|
||||
* uint16 Number of airports and heliports.
|
||||
* uint16 Number of harbours.
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_COMPANY_STATS(Packet *p);
|
||||
|
||||
/**
|
||||
* Send chat from the game into the admin network:
|
||||
* uint8 Action such as NETWORK_ACTION_CHAT_CLIENT (see #NetworkAction).
|
||||
* uint8 Destination type such as DESTTYPE_BROADCAST (see #DestType).
|
||||
* uint32 ID of the client who sent this message.
|
||||
* string Message.
|
||||
* uint64 Money (only when it is a 'give money' action).
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_CHAT(Packet *p);
|
||||
|
||||
/**
|
||||
* Result of an rcon command:
|
||||
* uint16 Colour as it would be used on the server or a client.
|
||||
* string Output of the executed command.
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_RCON(Packet *p);
|
||||
|
||||
/**
|
||||
* Send what would be printed on the server's console also into the admin network.
|
||||
* string The origin of the text, e.g. "console" for console, or "net" for network related (debug) messages.
|
||||
* string Text as found on the console of the server.
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_CONSOLE(Packet *p);
|
||||
|
||||
/**
|
||||
* Send DoCommand names to the bot upon request only.
|
||||
* Multiple of these packets can follow each other in order to provide
|
||||
* all known DoCommand names.
|
||||
*
|
||||
* NOTICE: Data provided with this packet is not stable and will not be
|
||||
* treated as such. Do not rely on IDs or names to be constant
|
||||
* across different versions / revisions of OpenTTD.
|
||||
* Data provided in this packet is for logging purposes only.
|
||||
*
|
||||
* These three fields are repeated until the packet is full:
|
||||
* bool Data to follow.
|
||||
* uint16 ID of the DoCommand.
|
||||
* string Name of DoCommand.
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_CMD_NAMES(Packet *p);
|
||||
|
||||
/**
|
||||
* Send incoming command packets to the admin network.
|
||||
* This is for logging purposes only.
|
||||
*
|
||||
* NOTICE: Data provided with this packet is not stable and will not be
|
||||
* treated as such. Do not rely on IDs or names to be constant
|
||||
* across different versions / revisions of OpenTTD.
|
||||
* Data provided in this packet is for logging purposes only.
|
||||
*
|
||||
* uint32 ID of the client sending the command.
|
||||
* uint8 ID of the company (0..MAX_COMPANIES-1).
|
||||
* uint16 ID of the command.
|
||||
* uint32 P1 (variable data passed to the command).
|
||||
* uint32 P2 (variable data passed to the command).
|
||||
* uint32 Tile where this is taking place.
|
||||
* string Text passed to the command.
|
||||
* uint32 Frame of execution.
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_CMD_LOGGING(Packet *p);
|
||||
|
||||
/**
|
||||
* Send a ping-reply (pong) to the admin that sent us the ping packet.
|
||||
* uint32 Integer identifier - should be the same as read from the admins ping packet.
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_PONG(Packet *p);
|
||||
|
||||
/**
|
||||
* Notify the admin connection that the rcon command has finished.
|
||||
* string The command as requested by the admin connection.
|
||||
* @param p The packet that was just received.
|
||||
* @return The state the network should have.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_RCON_END(Packet *p);
|
||||
|
||||
NetworkRecvStatus HandlePacket(Packet *p);
|
||||
public:
|
||||
NetworkRecvStatus CloseConnection(bool error = true);
|
||||
|
||||
NetworkAdminSocketHandler(SOCKET s);
|
||||
~NetworkAdminSocketHandler();
|
||||
|
||||
NetworkRecvStatus ReceivePackets();
|
||||
|
||||
/**
|
||||
* Get the status of the admin.
|
||||
* @return The status of the admin.
|
||||
*/
|
||||
AdminStatus GetAdminStatus() const
|
||||
{
|
||||
return this->status;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
|
||||
#endif /* NETWORK_CORE_TCP_ADMIN_H */
|
||||
101
src/network/core/tcp_connect.cpp
Normal file
101
src/network/core/tcp_connect.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file tcp_connect.cpp Basic functions to create connections without blocking.
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
#include "../../stdafx.h"
|
||||
#include "../../thread/thread.h"
|
||||
|
||||
#include "tcp.h"
|
||||
|
||||
#include "../../safeguards.h"
|
||||
|
||||
/** List of connections that are currently being created */
|
||||
static SmallVector<TCPConnecter *, 1> _tcp_connecters;
|
||||
|
||||
/**
|
||||
* Create a new connecter for the given address
|
||||
* @param address the (un)resolved address to connect to
|
||||
*/
|
||||
TCPConnecter::TCPConnecter(const NetworkAddress &address) :
|
||||
connected(false),
|
||||
aborted(false),
|
||||
killed(false),
|
||||
sock(INVALID_SOCKET),
|
||||
address(address)
|
||||
{
|
||||
*_tcp_connecters.Append() = this;
|
||||
if (!ThreadObject::New(TCPConnecter::ThreadEntry, this, &this->thread, "ottd:tcp")) {
|
||||
this->Connect();
|
||||
}
|
||||
}
|
||||
|
||||
/** The actual connection function */
|
||||
void TCPConnecter::Connect()
|
||||
{
|
||||
this->sock = this->address.Connect();
|
||||
if (this->sock == INVALID_SOCKET) {
|
||||
this->aborted = true;
|
||||
} else {
|
||||
this->connected = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point for the new threads.
|
||||
* @param param the TCPConnecter instance to call Connect on.
|
||||
*/
|
||||
/* static */ void TCPConnecter::ThreadEntry(void *param)
|
||||
{
|
||||
static_cast<TCPConnecter*>(param)->Connect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether we need to call the callback, i.e. whether we
|
||||
* have connected or aborted and call the appropriate callback
|
||||
* for that. It's done this way to ease on the locking that
|
||||
* would otherwise be needed everywhere.
|
||||
*/
|
||||
/* static */ void TCPConnecter::CheckCallbacks()
|
||||
{
|
||||
for (TCPConnecter **iter = _tcp_connecters.Begin(); iter < _tcp_connecters.End(); /* nothing */) {
|
||||
TCPConnecter *cur = *iter;
|
||||
if ((cur->connected || cur->aborted) && cur->killed) {
|
||||
_tcp_connecters.Erase(iter);
|
||||
if (cur->sock != INVALID_SOCKET) closesocket(cur->sock);
|
||||
delete cur;
|
||||
continue;
|
||||
}
|
||||
if (cur->connected) {
|
||||
_tcp_connecters.Erase(iter);
|
||||
cur->OnConnect(cur->sock);
|
||||
delete cur;
|
||||
continue;
|
||||
}
|
||||
if (cur->aborted) {
|
||||
_tcp_connecters.Erase(iter);
|
||||
cur->OnFailure();
|
||||
delete cur;
|
||||
continue;
|
||||
}
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
|
||||
/** Kill all connection attempts. */
|
||||
/* static */ void TCPConnecter::KillAll()
|
||||
{
|
||||
for (TCPConnecter **iter = _tcp_connecters.Begin(); iter != _tcp_connecters.End(); iter++) (*iter)->killed = true;
|
||||
}
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
270
src/network/core/tcp_content.cpp
Normal file
270
src/network/core/tcp_content.cpp
Normal file
@@ -0,0 +1,270 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file tcp_content.cpp Basic functions to receive and send Content packets.
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
#include "../../stdafx.h"
|
||||
#ifndef OPENTTD_MSU
|
||||
#include "../../textfile_gui.h"
|
||||
#include "../../newgrf_config.h"
|
||||
#include "../../base_media_base.h"
|
||||
#include "../../ai/ai.hpp"
|
||||
#include "../../game/game.hpp"
|
||||
#include "../../fios.h"
|
||||
#endif /* OPENTTD_MSU */
|
||||
#include "tcp_content.h"
|
||||
|
||||
#include "../../safeguards.h"
|
||||
|
||||
/** Clear everything in the struct */
|
||||
ContentInfo::ContentInfo()
|
||||
{
|
||||
memset(this, 0, sizeof(*this));
|
||||
}
|
||||
|
||||
/** Free everything allocated */
|
||||
ContentInfo::~ContentInfo()
|
||||
{
|
||||
free(this->dependencies);
|
||||
free(this->tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy data from other #ContentInfo and take ownership of allocated stuff.
|
||||
* @param other Source to copy from. #dependencies and #tags will be NULLed.
|
||||
*/
|
||||
void ContentInfo::TransferFrom(ContentInfo *other)
|
||||
{
|
||||
if (other != this) {
|
||||
free(this->dependencies);
|
||||
free(this->tags);
|
||||
memcpy(this, other, sizeof(ContentInfo));
|
||||
other->dependencies = NULL;
|
||||
other->tags = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of the data as send over the network.
|
||||
* @return the size.
|
||||
*/
|
||||
size_t ContentInfo::Size() const
|
||||
{
|
||||
size_t len = 0;
|
||||
for (uint i = 0; i < this->tag_count; i++) len += strlen(this->tags[i]) + 1;
|
||||
|
||||
/* The size is never larger than the content info size plus the size of the
|
||||
* tags and dependencies */
|
||||
return sizeof(*this) +
|
||||
sizeof(this->dependency_count) +
|
||||
sizeof(*this->dependencies) * this->dependency_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the state either selected or autoselected?
|
||||
* @return true iff that's the case
|
||||
*/
|
||||
bool ContentInfo::IsSelected() const
|
||||
{
|
||||
switch (this->state) {
|
||||
case ContentInfo::SELECTED:
|
||||
case ContentInfo::AUTOSELECTED:
|
||||
case ContentInfo::ALREADY_HERE:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the information from this content info valid?
|
||||
* @return true iff it's valid
|
||||
*/
|
||||
bool ContentInfo::IsValid() const
|
||||
{
|
||||
return this->state < ContentInfo::INVALID && this->type >= CONTENT_TYPE_BEGIN && this->type < CONTENT_TYPE_END;
|
||||
}
|
||||
|
||||
#ifndef OPENTTD_MSU
|
||||
/**
|
||||
* Search a textfile file next to this file in the content list.
|
||||
* @param type The type of the textfile to search for.
|
||||
* @return The filename for the textfile, \c NULL otherwise.
|
||||
*/
|
||||
const char *ContentInfo::GetTextfile(TextfileType type) const
|
||||
{
|
||||
if (this->state == INVALID) return NULL;
|
||||
const char *tmp;
|
||||
switch (this->type) {
|
||||
default: NOT_REACHED();
|
||||
case CONTENT_TYPE_AI:
|
||||
tmp = AI::GetScannerInfo()->FindMainScript(this, true);
|
||||
break;
|
||||
case CONTENT_TYPE_AI_LIBRARY:
|
||||
tmp = AI::GetScannerLibrary()->FindMainScript(this, true);
|
||||
break;
|
||||
case CONTENT_TYPE_GAME:
|
||||
tmp = Game::GetScannerInfo()->FindMainScript(this, true);
|
||||
break;
|
||||
case CONTENT_TYPE_GAME_LIBRARY:
|
||||
tmp = Game::GetScannerLibrary()->FindMainScript(this, true);
|
||||
break;
|
||||
case CONTENT_TYPE_NEWGRF: {
|
||||
const GRFConfig *gc = FindGRFConfig(BSWAP32(this->unique_id), FGCM_EXACT, this->md5sum);
|
||||
tmp = gc != NULL ? gc->filename : NULL;
|
||||
break;
|
||||
}
|
||||
case CONTENT_TYPE_BASE_GRAPHICS:
|
||||
tmp = TryGetBaseSetFile(this, true, BaseGraphics::GetAvailableSets());
|
||||
break;
|
||||
case CONTENT_TYPE_BASE_SOUNDS:
|
||||
tmp = TryGetBaseSetFile(this, true, BaseSounds::GetAvailableSets());
|
||||
break;
|
||||
case CONTENT_TYPE_BASE_MUSIC:
|
||||
tmp = TryGetBaseSetFile(this, true, BaseMusic::GetAvailableSets());
|
||||
break;
|
||||
case CONTENT_TYPE_SCENARIO:
|
||||
case CONTENT_TYPE_HEIGHTMAP:
|
||||
extern const char *FindScenario(const ContentInfo *ci, bool md5sum);
|
||||
tmp = FindScenario(this, true);
|
||||
break;
|
||||
}
|
||||
if (tmp == NULL) return NULL;
|
||||
return ::GetTextfile(type, GetContentInfoSubDir(this->type), tmp);
|
||||
}
|
||||
#endif /* OPENTTD_MSU */
|
||||
|
||||
void NetworkContentSocketHandler::Close()
|
||||
{
|
||||
CloseConnection();
|
||||
if (this->sock == INVALID_SOCKET) return;
|
||||
|
||||
closesocket(this->sock);
|
||||
this->sock = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the given packet, i.e. pass it to the right
|
||||
* parser receive command.
|
||||
* @param p the packet to handle
|
||||
* @return true if we should immediately handle further packets, false otherwise
|
||||
*/
|
||||
bool NetworkContentSocketHandler::HandlePacket(Packet *p)
|
||||
{
|
||||
PacketContentType type = (PacketContentType)p->Recv_uint8();
|
||||
|
||||
switch (this->HasClientQuit() ? PACKET_CONTENT_END : type) {
|
||||
case PACKET_CONTENT_CLIENT_INFO_LIST: return this->Receive_CLIENT_INFO_LIST(p);
|
||||
case PACKET_CONTENT_CLIENT_INFO_ID: return this->Receive_CLIENT_INFO_ID(p);
|
||||
case PACKET_CONTENT_CLIENT_INFO_EXTID: return this->Receive_CLIENT_INFO_EXTID(p);
|
||||
case PACKET_CONTENT_CLIENT_INFO_EXTID_MD5: return this->Receive_CLIENT_INFO_EXTID_MD5(p);
|
||||
case PACKET_CONTENT_SERVER_INFO: return this->Receive_SERVER_INFO(p);
|
||||
case PACKET_CONTENT_CLIENT_CONTENT: return this->Receive_CLIENT_CONTENT(p);
|
||||
case PACKET_CONTENT_SERVER_CONTENT: return this->Receive_SERVER_CONTENT(p);
|
||||
|
||||
default:
|
||||
if (this->HasClientQuit()) {
|
||||
DEBUG(net, 0, "[tcp/content] received invalid packet type %d from %s", type, this->client_addr.GetAddressAsString());
|
||||
} else {
|
||||
DEBUG(net, 0, "[tcp/content] received illegal packet from %s", this->client_addr.GetAddressAsString());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive a packet at TCP level
|
||||
* @return Whether at least one packet was received.
|
||||
*/
|
||||
bool NetworkContentSocketHandler::ReceivePackets()
|
||||
{
|
||||
/*
|
||||
* We read only a few of the packets. This as receiving packets can be expensive
|
||||
* due to the re-resolving of the parent/child relations and checking the toggle
|
||||
* state of all bits. We cannot do this all in one go, as we want to show the
|
||||
* user what we already received. Otherwise, it can take very long before any
|
||||
* progress is shown to the end user that something has been received.
|
||||
* It is also the case that we request extra content from the content server in
|
||||
* case there is an unknown (in the content list) piece of content. These will
|
||||
* come in after the main lists have been requested. As a result, we won't be
|
||||
* getting everything reliably in one batch. Thus, we need to make subsequent
|
||||
* updates in that case as well.
|
||||
*
|
||||
* As a result, we simple handle an arbitrary number of packets in one cycle,
|
||||
* and let the rest be handled in subsequent cycles. These are ran, almost,
|
||||
* immediately after this cycle so in speed it does not matter much, except
|
||||
* that the user inferface will appear better responding.
|
||||
*
|
||||
* What arbitrary number to choose is the ultimate question though.
|
||||
*/
|
||||
Packet *p;
|
||||
static const int MAX_PACKETS_TO_RECEIVE = 42;
|
||||
int i = MAX_PACKETS_TO_RECEIVE;
|
||||
while (--i != 0 && (p = this->ReceivePacket()) != NULL) {
|
||||
bool cont = this->HandlePacket(p);
|
||||
delete p;
|
||||
if (!cont) return true;
|
||||
}
|
||||
|
||||
return i != MAX_PACKETS_TO_RECEIVE - 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper for logging receiving invalid packets.
|
||||
* @param type The received packet type.
|
||||
* @return Always false, as it's an error.
|
||||
*/
|
||||
bool NetworkContentSocketHandler::ReceiveInvalidPacket(PacketContentType type)
|
||||
{
|
||||
DEBUG(net, 0, "[tcp/content] received illegal packet type %d from %s", type, this->client_addr.GetAddressAsString());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NetworkContentSocketHandler::Receive_CLIENT_INFO_LIST(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_CLIENT_INFO_LIST); }
|
||||
bool NetworkContentSocketHandler::Receive_CLIENT_INFO_ID(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_CLIENT_INFO_ID); }
|
||||
bool NetworkContentSocketHandler::Receive_CLIENT_INFO_EXTID(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_CLIENT_INFO_EXTID); }
|
||||
bool NetworkContentSocketHandler::Receive_CLIENT_INFO_EXTID_MD5(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_CLIENT_INFO_EXTID_MD5); }
|
||||
bool NetworkContentSocketHandler::Receive_SERVER_INFO(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_SERVER_INFO); }
|
||||
bool NetworkContentSocketHandler::Receive_CLIENT_CONTENT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_CLIENT_CONTENT); }
|
||||
bool NetworkContentSocketHandler::Receive_SERVER_CONTENT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_SERVER_CONTENT); }
|
||||
|
||||
#ifndef OPENTTD_MSU
|
||||
/**
|
||||
* Helper to get the subdirectory a #ContentInfo is located in.
|
||||
* @param type The type of content.
|
||||
* @return The subdirectory the content is located in.
|
||||
*/
|
||||
Subdirectory GetContentInfoSubDir(ContentType type)
|
||||
{
|
||||
switch (type) {
|
||||
default: return NO_DIRECTORY;
|
||||
case CONTENT_TYPE_AI: return AI_DIR;
|
||||
case CONTENT_TYPE_AI_LIBRARY: return AI_LIBRARY_DIR;
|
||||
case CONTENT_TYPE_GAME: return GAME_DIR;
|
||||
case CONTENT_TYPE_GAME_LIBRARY: return GAME_LIBRARY_DIR;
|
||||
case CONTENT_TYPE_NEWGRF: return NEWGRF_DIR;
|
||||
|
||||
case CONTENT_TYPE_BASE_GRAPHICS:
|
||||
case CONTENT_TYPE_BASE_SOUNDS:
|
||||
case CONTENT_TYPE_BASE_MUSIC:
|
||||
return BASESET_DIR;
|
||||
|
||||
case CONTENT_TYPE_SCENARIO: return SCENARIO_DIR;
|
||||
case CONTENT_TYPE_HEIGHTMAP: return HEIGHTMAP_DIR;
|
||||
}
|
||||
}
|
||||
#endif /* OPENTTD_MSU */
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
218
src/network/core/tcp_content.h
Normal file
218
src/network/core/tcp_content.h
Normal file
@@ -0,0 +1,218 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file tcp_content.h Basic functions to receive and send TCP packets to/from the content server.
|
||||
*/
|
||||
|
||||
#ifndef NETWORK_CORE_TCP_CONTENT_H
|
||||
#define NETWORK_CORE_TCP_CONTENT_H
|
||||
|
||||
#include "os_abstraction.h"
|
||||
#include "tcp.h"
|
||||
#include "packet.h"
|
||||
#include "../../debug.h"
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
/** The values in the enum are important; they are used as database 'keys' */
|
||||
enum ContentType {
|
||||
CONTENT_TYPE_BEGIN = 1, ///< Helper to mark the begin of the types
|
||||
CONTENT_TYPE_BASE_GRAPHICS = 1, ///< The content consists of base graphics
|
||||
CONTENT_TYPE_NEWGRF = 2, ///< The content consists of a NewGRF
|
||||
CONTENT_TYPE_AI = 3, ///< The content consists of an AI
|
||||
CONTENT_TYPE_AI_LIBRARY = 4, ///< The content consists of an AI library
|
||||
CONTENT_TYPE_SCENARIO = 5, ///< The content consists of a scenario
|
||||
CONTENT_TYPE_HEIGHTMAP = 6, ///< The content consists of a heightmap
|
||||
CONTENT_TYPE_BASE_SOUNDS = 7, ///< The content consists of base sounds
|
||||
CONTENT_TYPE_BASE_MUSIC = 8, ///< The content consists of base music
|
||||
CONTENT_TYPE_GAME = 9, ///< The content consists of a game script
|
||||
CONTENT_TYPE_GAME_LIBRARY = 10, ///< The content consists of a GS library
|
||||
CONTENT_TYPE_END, ///< Helper to mark the end of the types
|
||||
};
|
||||
|
||||
/** Enum with all types of TCP content packets. The order MUST not be changed **/
|
||||
enum PacketContentType {
|
||||
PACKET_CONTENT_CLIENT_INFO_LIST, ///< Queries the content server for a list of info of a given content type
|
||||
PACKET_CONTENT_CLIENT_INFO_ID, ///< Queries the content server for information about a list of internal IDs
|
||||
PACKET_CONTENT_CLIENT_INFO_EXTID, ///< Queries the content server for information about a list of external IDs
|
||||
PACKET_CONTENT_CLIENT_INFO_EXTID_MD5, ///< Queries the content server for information about a list of external IDs and MD5
|
||||
PACKET_CONTENT_SERVER_INFO, ///< Reply of content server with information about content
|
||||
PACKET_CONTENT_CLIENT_CONTENT, ///< Request a content file given an internal ID
|
||||
PACKET_CONTENT_SERVER_CONTENT, ///< Reply with the content of the given ID
|
||||
PACKET_CONTENT_END, ///< Must ALWAYS be on the end of this list!! (period)
|
||||
};
|
||||
|
||||
/** Unique identifier for the content. */
|
||||
enum ContentID {
|
||||
INVALID_CONTENT_ID = UINT32_MAX, ///< Sentinel for invalid content.
|
||||
};
|
||||
|
||||
/** Container for all important information about a piece of content. */
|
||||
struct ContentInfo {
|
||||
/** The state the content can be in. */
|
||||
enum State {
|
||||
UNSELECTED, ///< The content has not been selected
|
||||
SELECTED, ///< The content has been manually selected
|
||||
AUTOSELECTED, ///< The content has been selected as dependency
|
||||
ALREADY_HERE, ///< The content is already at the client side
|
||||
DOES_NOT_EXIST, ///< The content does not exist in the content system
|
||||
INVALID, ///< The content's invalid
|
||||
};
|
||||
|
||||
ContentType type; ///< Type of content
|
||||
ContentID id; ///< Unique (server side) ID for the content
|
||||
uint32 filesize; ///< Size of the file
|
||||
char filename[48]; ///< Filename (for the .tar.gz; only valid on download)
|
||||
char name[32]; ///< Name of the content
|
||||
char version[16]; ///< Version of the content
|
||||
char url[96]; ///< URL related to the content
|
||||
char description[512]; ///< Description of the content
|
||||
uint32 unique_id; ///< Unique ID; either GRF ID or shortname
|
||||
byte md5sum[16]; ///< The MD5 checksum
|
||||
uint8 dependency_count; ///< Number of dependencies
|
||||
ContentID *dependencies; ///< Malloced array of dependencies (unique server side ids)
|
||||
uint8 tag_count; ///< Number of tags
|
||||
char (*tags)[32]; ///< Malloced array of tags (strings)
|
||||
State state; ///< Whether the content info is selected (for download)
|
||||
bool upgrade; ///< This item is an upgrade
|
||||
|
||||
ContentInfo();
|
||||
~ContentInfo();
|
||||
|
||||
void TransferFrom(ContentInfo *other);
|
||||
|
||||
size_t Size() const;
|
||||
bool IsSelected() const;
|
||||
bool IsValid() const;
|
||||
#ifndef OPENTTD_MSU
|
||||
const char *GetTextfile(TextfileType type) const;
|
||||
#endif /* OPENTTD_MSU */
|
||||
};
|
||||
|
||||
/** Base socket handler for all Content TCP sockets */
|
||||
class NetworkContentSocketHandler : public NetworkTCPSocketHandler {
|
||||
protected:
|
||||
NetworkAddress client_addr; ///< The address we're connected to.
|
||||
virtual void Close();
|
||||
|
||||
bool ReceiveInvalidPacket(PacketContentType type);
|
||||
|
||||
/**
|
||||
* Client requesting a list of content info:
|
||||
* byte type
|
||||
* uint32 openttd version
|
||||
* @param p The packet that was just received.
|
||||
* @return True upon success, otherwise false.
|
||||
*/
|
||||
virtual bool Receive_CLIENT_INFO_LIST(Packet *p);
|
||||
|
||||
/**
|
||||
* Client requesting a list of content info:
|
||||
* uint16 count of ids
|
||||
* uint32 id (count times)
|
||||
* @param p The packet that was just received.
|
||||
* @return True upon success, otherwise false.
|
||||
*/
|
||||
virtual bool Receive_CLIENT_INFO_ID(Packet *p);
|
||||
|
||||
/**
|
||||
* Client requesting a list of content info based on an external
|
||||
* 'unique' id; GRF ID for NewGRFS, shortname and for base
|
||||
* graphics and AIs.
|
||||
* Scenarios and AI libraries are not supported
|
||||
* uint8 count of requests
|
||||
* for each request:
|
||||
* uint8 type
|
||||
* unique id (uint32)
|
||||
* @param p The packet that was just received.
|
||||
* @return True upon success, otherwise false.
|
||||
*/
|
||||
virtual bool Receive_CLIENT_INFO_EXTID(Packet *p);
|
||||
|
||||
/**
|
||||
* Client requesting a list of content info based on an external
|
||||
* 'unique' id; GRF ID + MD5 checksum for NewGRFS, shortname and
|
||||
* xor-ed MD5 checksums for base graphics and AIs.
|
||||
* Scenarios and AI libraries are not supported
|
||||
* uint8 count of requests
|
||||
* for each request:
|
||||
* uint8 type
|
||||
* unique id (uint32)
|
||||
* md5 (16 bytes)
|
||||
* @param p The packet that was just received.
|
||||
* @return True upon success, otherwise false.
|
||||
*/
|
||||
virtual bool Receive_CLIENT_INFO_EXTID_MD5(Packet *p);
|
||||
|
||||
/**
|
||||
* Server sending list of content info:
|
||||
* byte type (invalid ID == does not exist)
|
||||
* uint32 id
|
||||
* uint32 file_size
|
||||
* string name (max 32 characters)
|
||||
* string version (max 16 characters)
|
||||
* uint32 unique id
|
||||
* uint8 md5sum (16 bytes)
|
||||
* uint8 dependency count
|
||||
* uint32 unique id of dependency (dependency count times)
|
||||
* uint8 tag count
|
||||
* string tag (max 32 characters for tag count times)
|
||||
* @param p The packet that was just received.
|
||||
* @return True upon success, otherwise false.
|
||||
*/
|
||||
virtual bool Receive_SERVER_INFO(Packet *p);
|
||||
|
||||
/**
|
||||
* Client requesting the actual content:
|
||||
* uint16 count of unique ids
|
||||
* uint32 unique id (count times)
|
||||
* @param p The packet that was just received.
|
||||
* @return True upon success, otherwise false.
|
||||
*/
|
||||
virtual bool Receive_CLIENT_CONTENT(Packet *p);
|
||||
|
||||
/**
|
||||
* Server sending list of content info:
|
||||
* uint32 unique id
|
||||
* uint32 file size (0 == does not exist)
|
||||
* string file name (max 48 characters)
|
||||
* After this initial packet, packets with the actual data are send using
|
||||
* the same packet type.
|
||||
* @param p The packet that was just received.
|
||||
* @return True upon success, otherwise false.
|
||||
*/
|
||||
virtual bool Receive_SERVER_CONTENT(Packet *p);
|
||||
|
||||
bool HandlePacket(Packet *p);
|
||||
public:
|
||||
/**
|
||||
* Create a new cs socket handler for a given cs
|
||||
* @param s the socket we are connected with
|
||||
* @param address IP etc. of the client
|
||||
*/
|
||||
NetworkContentSocketHandler(SOCKET s = INVALID_SOCKET, const NetworkAddress &address = NetworkAddress()) :
|
||||
NetworkTCPSocketHandler(s),
|
||||
client_addr(address)
|
||||
{
|
||||
}
|
||||
|
||||
/** On destructing of this class, the socket needs to be closed */
|
||||
virtual ~NetworkContentSocketHandler() { this->Close(); }
|
||||
|
||||
bool ReceivePackets();
|
||||
};
|
||||
|
||||
#ifndef OPENTTD_MSU
|
||||
Subdirectory GetContentInfoSubDir(ContentType type);
|
||||
#endif /* OPENTTD_MSU */
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
|
||||
#endif /* NETWORK_CORE_TCP_CONTENT_H */
|
||||
203
src/network/core/tcp_game.cpp
Normal file
203
src/network/core/tcp_game.cpp
Normal file
@@ -0,0 +1,203 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file tcp_game.cpp Basic functions to receive and send TCP packets for game purposes.
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
#include "../../stdafx.h"
|
||||
|
||||
#include "../network.h"
|
||||
#include "../network_internal.h"
|
||||
#include "../../debug.h"
|
||||
#include "../../error.h"
|
||||
|
||||
#include "table/strings.h"
|
||||
|
||||
#include "../../safeguards.h"
|
||||
|
||||
/**
|
||||
* Create a new socket for the game connection.
|
||||
* @param s The socket to connect with.
|
||||
*/
|
||||
NetworkGameSocketHandler::NetworkGameSocketHandler(SOCKET s) : info(NULL), client_id(INVALID_CLIENT_ID),
|
||||
last_frame(_frame_counter), last_frame_server(_frame_counter), last_packet(_realtime_tick)
|
||||
{
|
||||
this->sock = s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Functions to help ReceivePacket/SendPacket a bit
|
||||
* A socket can make errors. When that happens this handles what to do.
|
||||
* For clients: close connection and drop back to main-menu
|
||||
* For servers: close connection and that is it
|
||||
* @return the new status
|
||||
*/
|
||||
NetworkRecvStatus NetworkGameSocketHandler::CloseConnection(bool error)
|
||||
{
|
||||
/* Clients drop back to the main menu */
|
||||
if (!_network_server && _networking) {
|
||||
extern void ClientNetworkEmergencySave(); // from network_client.cpp
|
||||
ClientNetworkEmergencySave();
|
||||
_switch_mode = SM_MENU;
|
||||
_networking = false;
|
||||
ShowErrorMessage(STR_NETWORK_ERROR_LOSTCONNECTION, INVALID_STRING_ID, WL_CRITICAL);
|
||||
|
||||
return NETWORK_RECV_STATUS_CONN_LOST;
|
||||
}
|
||||
|
||||
return this->CloseConnection(error ? NETWORK_RECV_STATUS_SERVER_ERROR : NETWORK_RECV_STATUS_CONN_LOST);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle the given packet, i.e. pass it to the right parser receive command.
|
||||
* @param p the packet to handle
|
||||
* @return #NetworkRecvStatus of handling.
|
||||
*/
|
||||
NetworkRecvStatus NetworkGameSocketHandler::HandlePacket(Packet *p)
|
||||
{
|
||||
PacketGameType type = (PacketGameType)p->Recv_uint8();
|
||||
|
||||
this->last_packet = _realtime_tick;
|
||||
|
||||
switch (this->HasClientQuit() ? PACKET_END : type) {
|
||||
case PACKET_SERVER_FULL: return this->Receive_SERVER_FULL(p);
|
||||
case PACKET_SERVER_BANNED: return this->Receive_SERVER_BANNED(p);
|
||||
case PACKET_CLIENT_JOIN: return this->Receive_CLIENT_JOIN(p);
|
||||
case PACKET_SERVER_ERROR: return this->Receive_SERVER_ERROR(p);
|
||||
case PACKET_CLIENT_COMPANY_INFO: return this->Receive_CLIENT_COMPANY_INFO(p);
|
||||
case PACKET_SERVER_COMPANY_INFO: return this->Receive_SERVER_COMPANY_INFO(p);
|
||||
case PACKET_SERVER_CLIENT_INFO: return this->Receive_SERVER_CLIENT_INFO(p);
|
||||
case PACKET_SERVER_NEED_GAME_PASSWORD: return this->Receive_SERVER_NEED_GAME_PASSWORD(p);
|
||||
case PACKET_SERVER_NEED_COMPANY_PASSWORD: return this->Receive_SERVER_NEED_COMPANY_PASSWORD(p);
|
||||
case PACKET_CLIENT_GAME_PASSWORD: return this->Receive_CLIENT_GAME_PASSWORD(p);
|
||||
case PACKET_CLIENT_COMPANY_PASSWORD: return this->Receive_CLIENT_COMPANY_PASSWORD(p);
|
||||
case PACKET_SERVER_WELCOME: return this->Receive_SERVER_WELCOME(p);
|
||||
case PACKET_CLIENT_GETMAP: return this->Receive_CLIENT_GETMAP(p);
|
||||
case PACKET_SERVER_WAIT: return this->Receive_SERVER_WAIT(p);
|
||||
case PACKET_SERVER_MAP_BEGIN: return this->Receive_SERVER_MAP_BEGIN(p);
|
||||
case PACKET_SERVER_MAP_SIZE: return this->Receive_SERVER_MAP_SIZE(p);
|
||||
case PACKET_SERVER_MAP_DATA: return this->Receive_SERVER_MAP_DATA(p);
|
||||
case PACKET_SERVER_MAP_DONE: return this->Receive_SERVER_MAP_DONE(p);
|
||||
case PACKET_CLIENT_MAP_OK: return this->Receive_CLIENT_MAP_OK(p);
|
||||
case PACKET_SERVER_JOIN: return this->Receive_SERVER_JOIN(p);
|
||||
case PACKET_SERVER_FRAME: return this->Receive_SERVER_FRAME(p);
|
||||
case PACKET_SERVER_SYNC: return this->Receive_SERVER_SYNC(p);
|
||||
case PACKET_CLIENT_ACK: return this->Receive_CLIENT_ACK(p);
|
||||
case PACKET_CLIENT_COMMAND: return this->Receive_CLIENT_COMMAND(p);
|
||||
case PACKET_SERVER_COMMAND: return this->Receive_SERVER_COMMAND(p);
|
||||
case PACKET_CLIENT_CHAT: return this->Receive_CLIENT_CHAT(p);
|
||||
case PACKET_SERVER_CHAT: return this->Receive_SERVER_CHAT(p);
|
||||
case PACKET_CLIENT_SET_PASSWORD: return this->Receive_CLIENT_SET_PASSWORD(p);
|
||||
case PACKET_CLIENT_SET_NAME: return this->Receive_CLIENT_SET_NAME(p);
|
||||
case PACKET_CLIENT_QUIT: return this->Receive_CLIENT_QUIT(p);
|
||||
case PACKET_CLIENT_ERROR: return this->Receive_CLIENT_ERROR(p);
|
||||
case PACKET_SERVER_QUIT: return this->Receive_SERVER_QUIT(p);
|
||||
case PACKET_SERVER_ERROR_QUIT: return this->Receive_SERVER_ERROR_QUIT(p);
|
||||
case PACKET_SERVER_SHUTDOWN: return this->Receive_SERVER_SHUTDOWN(p);
|
||||
case PACKET_SERVER_NEWGAME: return this->Receive_SERVER_NEWGAME(p);
|
||||
case PACKET_SERVER_RCON: return this->Receive_SERVER_RCON(p);
|
||||
case PACKET_CLIENT_RCON: return this->Receive_CLIENT_RCON(p);
|
||||
case PACKET_SERVER_CHECK_NEWGRFS: return this->Receive_SERVER_CHECK_NEWGRFS(p);
|
||||
case PACKET_CLIENT_NEWGRFS_CHECKED: return this->Receive_CLIENT_NEWGRFS_CHECKED(p);
|
||||
case PACKET_SERVER_MOVE: return this->Receive_SERVER_MOVE(p);
|
||||
case PACKET_CLIENT_MOVE: return this->Receive_CLIENT_MOVE(p);
|
||||
case PACKET_SERVER_COMPANY_UPDATE: return this->Receive_SERVER_COMPANY_UPDATE(p);
|
||||
case PACKET_SERVER_CONFIG_UPDATE: return this->Receive_SERVER_CONFIG_UPDATE(p);
|
||||
|
||||
default:
|
||||
this->CloseConnection();
|
||||
|
||||
if (this->HasClientQuit()) {
|
||||
DEBUG(net, 0, "[tcp/game] received invalid packet type %d from client %d", type, this->client_id);
|
||||
} else {
|
||||
DEBUG(net, 0, "[tcp/game] received illegal packet from client %d", this->client_id);
|
||||
}
|
||||
return NETWORK_RECV_STATUS_MALFORMED_PACKET;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the actual receiving of packets.
|
||||
* As long as HandlePacket returns OKAY packets are handled. Upon
|
||||
* failure, or no more packets to process the last result of
|
||||
* HandlePacket is returned.
|
||||
* @return #NetworkRecvStatus of the last handled packet.
|
||||
*/
|
||||
NetworkRecvStatus NetworkGameSocketHandler::ReceivePackets()
|
||||
{
|
||||
Packet *p;
|
||||
while ((p = this->ReceivePacket()) != NULL) {
|
||||
NetworkRecvStatus res = HandlePacket(p);
|
||||
delete p;
|
||||
if (res != NETWORK_RECV_STATUS_OKAY) return res;
|
||||
}
|
||||
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for logging receiving invalid packets.
|
||||
* @param type The received packet type.
|
||||
* @return The status the network should have, in this case: "malformed packet error".
|
||||
*/
|
||||
NetworkRecvStatus NetworkGameSocketHandler::ReceiveInvalidPacket(PacketGameType type)
|
||||
{
|
||||
DEBUG(net, 0, "[tcp/game] received illegal packet type %d from client %d", type, this->client_id);
|
||||
return NETWORK_RECV_STATUS_MALFORMED_PACKET;
|
||||
}
|
||||
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_FULL(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_FULL); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_BANNED(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_BANNED); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_JOIN(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_JOIN); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_ERROR(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_ERROR); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_COMPANY_INFO(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_COMPANY_INFO); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_COMPANY_INFO(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_COMPANY_INFO); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_CLIENT_INFO(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_CLIENT_INFO); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_NEED_GAME_PASSWORD(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_NEED_GAME_PASSWORD); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_NEED_COMPANY_PASSWORD(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_NEED_COMPANY_PASSWORD); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_GAME_PASSWORD(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_GAME_PASSWORD); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_COMPANY_PASSWORD(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_COMPANY_PASSWORD); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_WELCOME(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_WELCOME); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_GETMAP(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_GETMAP); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_WAIT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_WAIT); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_MAP_BEGIN(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_MAP_BEGIN); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_MAP_SIZE(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_MAP_SIZE); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_MAP_DATA(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_MAP_DATA); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_MAP_DONE(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_MAP_DONE); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_MAP_OK(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_MAP_OK); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_JOIN(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_JOIN); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_FRAME(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_FRAME); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_SYNC(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_SYNC); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_ACK(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_ACK); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_COMMAND(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_COMMAND); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_COMMAND(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_COMMAND); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_CHAT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_CHAT); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_CHAT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_CHAT); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_SET_PASSWORD(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_SET_PASSWORD); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_SET_NAME(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_SET_NAME); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_QUIT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_QUIT); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_ERROR(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_ERROR); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_QUIT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_QUIT); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_ERROR_QUIT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_ERROR_QUIT); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_SHUTDOWN(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_SHUTDOWN); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_NEWGAME(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_NEWGAME); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_RCON(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_RCON); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_RCON(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_RCON); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_CHECK_NEWGRFS(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_CHECK_NEWGRFS); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_NEWGRFS_CHECKED(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_NEWGRFS_CHECKED); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_MOVE(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_MOVE); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_MOVE(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_MOVE); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_COMPANY_UPDATE(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_COMPANY_UPDATE); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_CONFIG_UPDATE(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_CONFIG_UPDATE); }
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
563
src/network/core/tcp_game.h
Normal file
563
src/network/core/tcp_game.h
Normal file
@@ -0,0 +1,563 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file tcp_game.h Basic functions to receive and send TCP packets for game purposes.
|
||||
*/
|
||||
|
||||
#ifndef NETWORK_CORE_TCP_GAME_H
|
||||
#define NETWORK_CORE_TCP_GAME_H
|
||||
|
||||
#include "os_abstraction.h"
|
||||
#include "tcp.h"
|
||||
#include "../network_type.h"
|
||||
#include "../../core/pool_type.hpp"
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
/**
|
||||
* Enum with all types of TCP packets.
|
||||
* For the exact meaning, look at #NetworkGameSocketHandler.
|
||||
*/
|
||||
enum PacketGameType {
|
||||
/*
|
||||
* These first three pair of packets (thus six in
|
||||
* total) must remain in this order for backward
|
||||
* and forward compatibility between clients that
|
||||
* are trying to join directly.
|
||||
*/
|
||||
|
||||
/* Packets sent by socket accepting code without ever constructing a client socket instance. */
|
||||
PACKET_SERVER_FULL, ///< The server is full and has no place for you.
|
||||
PACKET_SERVER_BANNED, ///< The server has banned you.
|
||||
|
||||
/* Packets used by the client to join and an error message when the revision is wrong. */
|
||||
PACKET_CLIENT_JOIN, ///< The client telling the server it wants to join.
|
||||
PACKET_SERVER_ERROR, ///< Server sending an error message to the client.
|
||||
|
||||
/* Packets used for the pre-game lobby. */
|
||||
PACKET_CLIENT_COMPANY_INFO, ///< Request information about all companies.
|
||||
PACKET_SERVER_COMPANY_INFO, ///< Information about a single company.
|
||||
|
||||
/*
|
||||
* Packets after here assume that the client
|
||||
* and server are running the same version. As
|
||||
* such ordering is unimportant from here on.
|
||||
*
|
||||
* The following is the remainder of the packets
|
||||
* sent as part of authenticating and getting
|
||||
* the map and other important data.
|
||||
*/
|
||||
|
||||
/* After the join step, the first is checking NewGRFs. */
|
||||
PACKET_SERVER_CHECK_NEWGRFS, ///< Server sends NewGRF IDs and MD5 checksums for the client to check.
|
||||
PACKET_CLIENT_NEWGRFS_CHECKED, ///< Client acknowledges that it has all required NewGRFs.
|
||||
|
||||
/* Checking the game, and then company passwords. */
|
||||
PACKET_SERVER_NEED_GAME_PASSWORD, ///< Server requests the (hashed) game password.
|
||||
PACKET_CLIENT_GAME_PASSWORD, ///< Clients sends the (hashed) game password.
|
||||
PACKET_SERVER_NEED_COMPANY_PASSWORD, ///< Server requests the (hashed) company password.
|
||||
PACKET_CLIENT_COMPANY_PASSWORD, ///< Client sends the (hashed) company password.
|
||||
|
||||
/* The server welcomes the authenticated client and sends information of other clients. */
|
||||
PACKET_SERVER_WELCOME, ///< Server welcomes you and gives you your #ClientID.
|
||||
PACKET_SERVER_CLIENT_INFO, ///< Server sends you information about a client.
|
||||
|
||||
/* Getting the savegame/map. */
|
||||
PACKET_CLIENT_GETMAP, ///< Client requests the actual map.
|
||||
PACKET_SERVER_WAIT, ///< Server tells the client there are some people waiting for the map as well.
|
||||
PACKET_SERVER_MAP_BEGIN, ///< Server tells the client that it is beginning to send the map.
|
||||
PACKET_SERVER_MAP_SIZE, ///< Server tells the client what the (compressed) size of the map is.
|
||||
PACKET_SERVER_MAP_DATA, ///< Server sends bits of the map to the client.
|
||||
PACKET_SERVER_MAP_DONE, ///< Server tells it has just sent the last bits of the map to the client.
|
||||
PACKET_CLIENT_MAP_OK, ///< Client tells the server that it received the whole map.
|
||||
|
||||
PACKET_SERVER_JOIN, ///< Tells clients that a new client has joined.
|
||||
|
||||
/*
|
||||
* At this moment the client has the map and
|
||||
* the client is fully authenticated. Now the
|
||||
* normal communication starts.
|
||||
*/
|
||||
|
||||
/* Game progress monitoring. */
|
||||
PACKET_SERVER_FRAME, ///< Server tells the client what frame it is in, and thus to where the client may progress.
|
||||
PACKET_CLIENT_ACK, ///< The client tells the server which frame it has executed.
|
||||
PACKET_SERVER_SYNC, ///< Server tells the client what the random state should be.
|
||||
|
||||
/* Sending commands around. */
|
||||
PACKET_CLIENT_COMMAND, ///< Client executed a command and sends it to the server.
|
||||
PACKET_SERVER_COMMAND, ///< Server distributes a command to (all) the clients.
|
||||
|
||||
/* Human communication! */
|
||||
PACKET_CLIENT_CHAT, ///< Client said something that should be distributed.
|
||||
PACKET_SERVER_CHAT, ///< Server distributing the message of a client (or itself).
|
||||
|
||||
/* Remote console. */
|
||||
PACKET_CLIENT_RCON, ///< Client asks the server to execute some command.
|
||||
PACKET_SERVER_RCON, ///< Response of the executed command on the server.
|
||||
|
||||
/* Moving a client.*/
|
||||
PACKET_CLIENT_MOVE, ///< A client would like to be moved to another company.
|
||||
PACKET_SERVER_MOVE, ///< Server tells everyone that someone is moved to another company.
|
||||
|
||||
/* Configuration updates. */
|
||||
PACKET_CLIENT_SET_PASSWORD, ///< A client (re)sets its company's password.
|
||||
PACKET_CLIENT_SET_NAME, ///< A client changes its name.
|
||||
PACKET_SERVER_COMPANY_UPDATE, ///< Information (password) of a company changed.
|
||||
PACKET_SERVER_CONFIG_UPDATE, ///< Some network configuration important to the client changed.
|
||||
|
||||
/* A server quitting this game. */
|
||||
PACKET_SERVER_NEWGAME, ///< The server is preparing to start a new game.
|
||||
PACKET_SERVER_SHUTDOWN, ///< The server is shutting down.
|
||||
|
||||
/* A client quitting. */
|
||||
PACKET_CLIENT_QUIT, ///< A client tells the server it is going to quit.
|
||||
PACKET_SERVER_QUIT, ///< A server tells that a client has quit.
|
||||
PACKET_CLIENT_ERROR, ///< A client reports an error to the server.
|
||||
PACKET_SERVER_ERROR_QUIT, ///< A server tells that a client has hit an error and did quit.
|
||||
|
||||
PACKET_END, ///< Must ALWAYS be on the end of this list!! (period)
|
||||
};
|
||||
|
||||
/** Packet that wraps a command */
|
||||
struct CommandPacket;
|
||||
|
||||
/** A queue of CommandPackets. */
|
||||
class CommandQueue {
|
||||
CommandPacket *first; ///< The first packet in the queue.
|
||||
CommandPacket *last; ///< The last packet in the queue; only valid when first != NULL.
|
||||
uint count; ///< The number of items in the queue.
|
||||
|
||||
public:
|
||||
/** Initialise the command queue. */
|
||||
CommandQueue() : first(NULL), last(NULL), count(0) {}
|
||||
/** Clear the command queue. */
|
||||
~CommandQueue() { this->Free(); }
|
||||
void Append(CommandPacket *p);
|
||||
CommandPacket *Pop(bool ignore_paused = false);
|
||||
CommandPacket *Peek(bool ignore_paused = false);
|
||||
void Free();
|
||||
/** Get the number of items in the queue. */
|
||||
uint Count() const { return this->count; }
|
||||
};
|
||||
|
||||
/** Base socket handler for all TCP sockets */
|
||||
class NetworkGameSocketHandler : public NetworkTCPSocketHandler {
|
||||
/* TODO: rewrite into a proper class */
|
||||
private:
|
||||
NetworkClientInfo *info; ///< Client info related to this socket
|
||||
|
||||
protected:
|
||||
NetworkRecvStatus ReceiveInvalidPacket(PacketGameType type);
|
||||
|
||||
/**
|
||||
* Notification that the server is full.
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_FULL(Packet *p);
|
||||
|
||||
/**
|
||||
* Notification that the client trying to join is banned.
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_BANNED(Packet *p);
|
||||
|
||||
/**
|
||||
* Try to join the server:
|
||||
* string OpenTTD revision (norev000 if no revision).
|
||||
* string Name of the client (max NETWORK_NAME_LENGTH).
|
||||
* uint8 ID of the company to play as (1..MAX_COMPANIES).
|
||||
* uint8 ID of the clients Language.
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_CLIENT_JOIN(Packet *p);
|
||||
|
||||
/**
|
||||
* The client made an error:
|
||||
* uint8 Error code caused (see NetworkErrorCode).
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_ERROR(Packet *p);
|
||||
|
||||
/**
|
||||
* Request company information (in detail).
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_CLIENT_COMPANY_INFO(Packet *p);
|
||||
|
||||
/**
|
||||
* Sends information about the companies (one packet per company):
|
||||
* uint8 Version of the structure of this packet (NETWORK_COMPANY_INFO_VERSION).
|
||||
* bool Contains data (false marks the end of updates).
|
||||
* uint8 ID of the company.
|
||||
* string Name of the company.
|
||||
* uint32 Year the company was inaugurated.
|
||||
* uint64 Value.
|
||||
* uint64 Money.
|
||||
* uint64 Income.
|
||||
* uint16 Performance (last quarter).
|
||||
* bool Company is password protected.
|
||||
* uint16 Number of trains.
|
||||
* uint16 Number of lorries.
|
||||
* uint16 Number of busses.
|
||||
* uint16 Number of planes.
|
||||
* uint16 Number of ships.
|
||||
* uint16 Number of train stations.
|
||||
* uint16 Number of lorry stations.
|
||||
* uint16 Number of bus stops.
|
||||
* uint16 Number of airports and heliports.
|
||||
* uint16 Number of harbours.
|
||||
* bool Company is an AI.
|
||||
* string Client names (comma separated list)
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_COMPANY_INFO(Packet *p);
|
||||
|
||||
/**
|
||||
* Send information about a client:
|
||||
* uint32 ID of the client (always unique on a server. 1 = server, 0 is invalid).
|
||||
* uint8 ID of the company the client is playing as (255 for spectators).
|
||||
* string Name of the client.
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_CLIENT_INFO(Packet *p);
|
||||
|
||||
/**
|
||||
* Indication to the client that the server needs a game password.
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_NEED_GAME_PASSWORD(Packet *p);
|
||||
|
||||
/**
|
||||
* Indication to the client that the server needs a company password:
|
||||
* uint32 Generation seed.
|
||||
* string Network ID of the server.
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_NEED_COMPANY_PASSWORD(Packet *p);
|
||||
|
||||
/**
|
||||
* Send a password to the server to authorize:
|
||||
* uint8 Password type (see NetworkPasswordType).
|
||||
* string The password.
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_CLIENT_GAME_PASSWORD(Packet *p);
|
||||
|
||||
/**
|
||||
* Send a password to the server to authorize
|
||||
* uint8 Password type (see NetworkPasswordType).
|
||||
* string The password.
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_CLIENT_COMPANY_PASSWORD(Packet *p);
|
||||
|
||||
/**
|
||||
* The client is joined and ready to receive his map:
|
||||
* uint32 Own client ID.
|
||||
* uint32 Generation seed.
|
||||
* string Network ID of the server.
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_WELCOME(Packet *p);
|
||||
|
||||
/**
|
||||
* Request the map from the server.
|
||||
* uint32 NewGRF version (release versions of OpenTTD only).
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_CLIENT_GETMAP(Packet *p);
|
||||
|
||||
/**
|
||||
* Notification that another client is currently receiving the map:
|
||||
* uint8 Number of clients waiting in front of you.
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_WAIT(Packet *p);
|
||||
|
||||
/**
|
||||
* Sends that the server will begin with sending the map to the client:
|
||||
* uint32 Current frame.
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_MAP_BEGIN(Packet *p);
|
||||
|
||||
/**
|
||||
* Sends the size of the map to the client.
|
||||
* uint32 Size of the (compressed) map (in bytes).
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_MAP_SIZE(Packet *p);
|
||||
|
||||
/**
|
||||
* Sends the data of the map to the client:
|
||||
* Contains a part of the map (until max size of packet).
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_MAP_DATA(Packet *p);
|
||||
|
||||
/**
|
||||
* Sends that all data of the map are sent to the client:
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_MAP_DONE(Packet *p);
|
||||
|
||||
/**
|
||||
* Tell the server that we are done receiving/loading the map.
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_CLIENT_MAP_OK(Packet *p);
|
||||
|
||||
/**
|
||||
* A client joined (PACKET_CLIENT_MAP_OK), what usually directly follows is a PACKET_SERVER_CLIENT_INFO:
|
||||
* uint32 ID of the client that just joined the game.
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_JOIN(Packet *p);
|
||||
|
||||
/**
|
||||
* Sends the current frame counter to the client:
|
||||
* uint32 Frame counter
|
||||
* uint32 Frame counter max (how far may the client walk before the server?)
|
||||
* uint32 General seed 1 (dependent on compile settings, not default).
|
||||
* uint32 General seed 2 (dependent on compile settings, not default).
|
||||
* uint8 Random token to validate the client is actually listening (only occasionally present).
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_FRAME(Packet *p);
|
||||
|
||||
/**
|
||||
* Sends a sync-check to the client:
|
||||
* uint32 Frame counter.
|
||||
* uint32 General seed 1.
|
||||
* uint32 General seed 2 (dependent on compile settings, not default).
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_SYNC(Packet *p);
|
||||
|
||||
/**
|
||||
* Tell the server we are done with this frame:
|
||||
* uint32 Current frame counter of the client.
|
||||
* uint8 The random token that the server sent in the PACKET_SERVER_FRAME packet.
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_CLIENT_ACK(Packet *p);
|
||||
|
||||
/**
|
||||
* Send a DoCommand to the Server:
|
||||
* uint8 ID of the company (0..MAX_COMPANIES-1).
|
||||
* uint32 ID of the command (see command.h).
|
||||
* uint32 P1 (free variables used in DoCommand).
|
||||
* uint32 P2
|
||||
* uint32 Tile where this is taking place.
|
||||
* string Text.
|
||||
* uint8 ID of the callback.
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_CLIENT_COMMAND(Packet *p);
|
||||
|
||||
/**
|
||||
* Sends a DoCommand to the client:
|
||||
* uint8 ID of the company (0..MAX_COMPANIES-1).
|
||||
* uint32 ID of the command (see command.h).
|
||||
* uint32 P1 (free variable used in DoCommand).
|
||||
* uint32 P2.
|
||||
* uint32 Tile where this is taking place.
|
||||
* string Text.
|
||||
* uint8 ID of the callback.
|
||||
* uint32 Frame of execution.
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_COMMAND(Packet *p);
|
||||
|
||||
/**
|
||||
* Sends a chat-packet to the server:
|
||||
* uint8 ID of the action (see NetworkAction).
|
||||
* uint8 ID of the destination type (see DestType).
|
||||
* uint32 ID of the client or company (destination of the chat).
|
||||
* string Message (max NETWORK_CHAT_LENGTH).
|
||||
* uint64 data (used e.g. for 'give money' actions).
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_CLIENT_CHAT(Packet *p);
|
||||
|
||||
/**
|
||||
* Sends a chat-packet to the client:
|
||||
* uint8 ID of the action (see NetworkAction).
|
||||
* uint32 ID of the client (origin of the chat).
|
||||
* string Message (max NETWORK_CHAT_LENGTH).
|
||||
* uint64 data (used e.g. for 'give money' actions).
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_CHAT(Packet *p);
|
||||
|
||||
/**
|
||||
* Set the password for the clients current company:
|
||||
* string The password.
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_CLIENT_SET_PASSWORD(Packet *p);
|
||||
|
||||
/**
|
||||
* Gives the client a new name:
|
||||
* string New name of the client.
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_CLIENT_SET_NAME(Packet *p);
|
||||
|
||||
/**
|
||||
* The client is quitting the game.
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_CLIENT_QUIT(Packet *p);
|
||||
|
||||
/**
|
||||
* The client made an error and is quitting the game.
|
||||
* uint8 Error of the code caused (see NetworkErrorCode).
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_CLIENT_ERROR(Packet *p);
|
||||
|
||||
/**
|
||||
* Notification that a client left the game:
|
||||
* uint32 ID of the client.
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_QUIT(Packet *p);
|
||||
|
||||
/**
|
||||
* Inform all clients that one client made an error and thus has quit/been disconnected:
|
||||
* uint32 ID of the client that caused the error.
|
||||
* uint8 Code of the error caused (see NetworkErrorCode).
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_ERROR_QUIT(Packet *p);
|
||||
|
||||
/**
|
||||
* Let the clients know that the server is closing.
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_SHUTDOWN(Packet *p);
|
||||
|
||||
/**
|
||||
* Let the clients know that the server is loading a new map.
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_NEWGAME(Packet *p);
|
||||
|
||||
/**
|
||||
* Send the result of an issues RCon command back to the client:
|
||||
* uint16 Colour code.
|
||||
* string Output of the RCon command
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_RCON(Packet *p);
|
||||
|
||||
/**
|
||||
* Send an RCon command to the server:
|
||||
* string RCon password.
|
||||
* string Command to be executed.
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_CLIENT_RCON(Packet *p);
|
||||
|
||||
/**
|
||||
* Sends information about all used GRFs to the client:
|
||||
* uint8 Amount of GRFs (the following data is repeated this many times, i.e. per GRF data).
|
||||
* uint32 GRF ID
|
||||
* 16 * uint8 MD5 checksum of the GRF
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_CHECK_NEWGRFS(Packet *p);
|
||||
|
||||
/**
|
||||
* Tell the server that we have the required GRFs
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_CLIENT_NEWGRFS_CHECKED(Packet *p);
|
||||
|
||||
/**
|
||||
* Move a client from one company into another:
|
||||
* uint32 ID of the client.
|
||||
* uint8 ID of the new company.
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_MOVE(Packet *p);
|
||||
|
||||
/**
|
||||
* Request the server to move this client into another company:
|
||||
* uint8 ID of the company the client wants to join.
|
||||
* string Password, if the company is password protected.
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_CLIENT_MOVE(Packet *p);
|
||||
|
||||
/**
|
||||
* Update the clients knowledge of which company is password protected:
|
||||
* uint16 Bitwise representation of each company
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_COMPANY_UPDATE(Packet *p);
|
||||
|
||||
/**
|
||||
* Update the clients knowledge of the max settings:
|
||||
* uint8 Maximum number of companies allowed.
|
||||
* uint8 Maximum number of spectators allowed.
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_CONFIG_UPDATE(Packet *p);
|
||||
|
||||
NetworkRecvStatus HandlePacket(Packet *p);
|
||||
|
||||
NetworkGameSocketHandler(SOCKET s);
|
||||
public:
|
||||
ClientID client_id; ///< Client identifier
|
||||
uint32 last_frame; ///< Last frame we have executed
|
||||
uint32 last_frame_server; ///< Last frame the server has executed
|
||||
CommandQueue incoming_queue; ///< The command-queue awaiting handling
|
||||
uint last_packet; ///< Time we received the last frame.
|
||||
|
||||
NetworkRecvStatus CloseConnection(bool error = true);
|
||||
|
||||
/**
|
||||
* Close the network connection due to the given status.
|
||||
* @param status The reason the connection got closed.
|
||||
*/
|
||||
virtual NetworkRecvStatus CloseConnection(NetworkRecvStatus status) = 0;
|
||||
virtual ~NetworkGameSocketHandler() {}
|
||||
|
||||
/**
|
||||
* Sets the client info for this socket handler.
|
||||
* @param info The new client info.
|
||||
*/
|
||||
inline void SetInfo(NetworkClientInfo *info)
|
||||
{
|
||||
assert(info != NULL && this->info == NULL);
|
||||
this->info = info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the client info of this socket handler.
|
||||
* @return The client info.
|
||||
*/
|
||||
inline NetworkClientInfo *GetInfo() const
|
||||
{
|
||||
return this->info;
|
||||
}
|
||||
|
||||
NetworkRecvStatus ReceivePackets();
|
||||
|
||||
const char *ReceiveCommand(Packet *p, CommandPacket *cp);
|
||||
void SendCommand(Packet *p, const CommandPacket *cp);
|
||||
};
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
|
||||
#endif /* NETWORK_CORE_TCP_GAME_H */
|
||||
339
src/network/core/tcp_http.cpp
Normal file
339
src/network/core/tcp_http.cpp
Normal file
@@ -0,0 +1,339 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file tcp_http.cpp Basic functions to receive and send HTTP TCP packets.
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
#include "../../stdafx.h"
|
||||
#include "../../debug.h"
|
||||
#include "../../rev.h"
|
||||
#include "../network_func.h"
|
||||
|
||||
#include "tcp_http.h"
|
||||
|
||||
#include "../../safeguards.h"
|
||||
|
||||
/** List of open HTTP connections. */
|
||||
static SmallVector<NetworkHTTPSocketHandler *, 1> _http_connections;
|
||||
|
||||
/**
|
||||
* Start the querying
|
||||
* @param s the socket of this connection
|
||||
* @param callback the callback for HTTP retrieval
|
||||
* @param host the hostname of the server to connect to
|
||||
* @param url the url at the server
|
||||
* @param data the data to send
|
||||
* @param depth the depth (redirect recursion) of the queries
|
||||
*/
|
||||
NetworkHTTPSocketHandler::NetworkHTTPSocketHandler(SOCKET s,
|
||||
HTTPCallback *callback, const char *host, const char *url,
|
||||
const char *data, int depth) :
|
||||
NetworkSocketHandler(),
|
||||
recv_pos(0),
|
||||
recv_length(0),
|
||||
callback(callback),
|
||||
data(data),
|
||||
redirect_depth(depth),
|
||||
sock(s)
|
||||
{
|
||||
size_t bufferSize = strlen(url) + strlen(host) + strlen(GetNetworkRevisionString()) + (data == NULL ? 0 : strlen(data)) + 128;
|
||||
char *buffer = AllocaM(char, bufferSize);
|
||||
|
||||
DEBUG(net, 7, "[tcp/http] requesting %s%s", host, url);
|
||||
if (data != NULL) {
|
||||
seprintf(buffer, buffer + bufferSize - 1, "POST %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: OpenTTD/%s\r\nContent-Type: text/plain\r\nContent-Length: %d\r\n\r\n%s\r\n", url, host, GetNetworkRevisionString(), (int)strlen(data), data);
|
||||
} else {
|
||||
seprintf(buffer, buffer + bufferSize - 1, "GET %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: OpenTTD/%s\r\n\r\n", url, host, GetNetworkRevisionString());
|
||||
}
|
||||
|
||||
ssize_t size = strlen(buffer);
|
||||
ssize_t res = send(this->sock, (const char*)buffer, size, 0);
|
||||
if (res != size) {
|
||||
/* Sending all data failed. Socket can't handle this little bit
|
||||
* of information? Just fall back to the old system! */
|
||||
this->callback->OnFailure();
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
|
||||
*_http_connections.Append() = this;
|
||||
}
|
||||
|
||||
/** Free whatever needs to be freed. */
|
||||
NetworkHTTPSocketHandler::~NetworkHTTPSocketHandler()
|
||||
{
|
||||
this->CloseConnection();
|
||||
|
||||
if (this->sock != INVALID_SOCKET) closesocket(this->sock);
|
||||
this->sock = INVALID_SOCKET;
|
||||
free(this->data);
|
||||
}
|
||||
|
||||
NetworkRecvStatus NetworkHTTPSocketHandler::CloseConnection(bool error)
|
||||
{
|
||||
NetworkSocketHandler::CloseConnection(error);
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to simplify the error handling.
|
||||
* @param msg the error message to show.
|
||||
*/
|
||||
#define return_error(msg) { DEBUG(net, 0, msg); return -1; }
|
||||
|
||||
static const char * const NEWLINE = "\r\n"; ///< End of line marker
|
||||
static const char * const END_OF_HEADER = "\r\n\r\n"; ///< End of header marker
|
||||
static const char * const HTTP_1_0 = "HTTP/1.0 "; ///< Preamble for HTTP 1.0 servers
|
||||
static const char * const HTTP_1_1 = "HTTP/1.1 "; ///< Preamble for HTTP 1.1 servers
|
||||
static const char * const CONTENT_LENGTH = "Content-Length: "; ///< Header for the length of the content
|
||||
static const char * const LOCATION = "Location: "; ///< Header for location
|
||||
|
||||
/**
|
||||
* Handle the header of a HTTP reply.
|
||||
* @return amount of data to continue downloading.
|
||||
* > 0: we need to download N bytes.
|
||||
* = 0: we're being redirected.
|
||||
* < 0: an error occurred. Downloading failed.
|
||||
* @note if an error occurred the header might not be in its
|
||||
* original state. No effort is undertaken to bring
|
||||
* the header in its original state.
|
||||
*/
|
||||
int NetworkHTTPSocketHandler::HandleHeader()
|
||||
{
|
||||
assert(strlen(HTTP_1_0) == strlen(HTTP_1_1));
|
||||
assert(strstr(this->recv_buffer, END_OF_HEADER) != NULL);
|
||||
|
||||
/* We expect a HTTP/1.[01] reply */
|
||||
if (strncmp(this->recv_buffer, HTTP_1_0, strlen(HTTP_1_0)) != 0 &&
|
||||
strncmp(this->recv_buffer, HTTP_1_1, strlen(HTTP_1_1)) != 0) {
|
||||
return_error("[tcp/http] received invalid HTTP reply");
|
||||
}
|
||||
|
||||
char *status = this->recv_buffer + strlen(HTTP_1_0);
|
||||
if (strncmp(status, "200", 3) == 0) {
|
||||
/* We are going to receive a document. */
|
||||
|
||||
/* Get the length of the document to receive */
|
||||
char *length = strcasestr(this->recv_buffer, CONTENT_LENGTH);
|
||||
if (length == NULL) return_error("[tcp/http] missing 'content-length' header");
|
||||
|
||||
/* Skip the header */
|
||||
length += strlen(CONTENT_LENGTH);
|
||||
|
||||
/* Search the end of the line. This is safe because the header will
|
||||
* always end with two newlines. */
|
||||
char *end_of_line = strstr(length, NEWLINE);
|
||||
|
||||
/* Read the length */
|
||||
*end_of_line = '\0';
|
||||
int len = atoi(length);
|
||||
/* Restore the header. */
|
||||
*end_of_line = '\r';
|
||||
|
||||
/* Make sure we're going to download at least something;
|
||||
* zero sized files are, for OpenTTD's purposes, always
|
||||
* wrong. You can't have gzips of 0 bytes! */
|
||||
if (len == 0) return_error("[tcp/http] refusing to download 0 bytes");
|
||||
|
||||
DEBUG(net, 7, "[tcp/http] downloading %i bytes", len);
|
||||
return len;
|
||||
}
|
||||
|
||||
if (strncmp(status, "301", 3) != 0 &&
|
||||
strncmp(status, "302", 3) != 0 &&
|
||||
strncmp(status, "303", 3) != 0 &&
|
||||
strncmp(status, "307", 3) != 0) {
|
||||
/* We are not going to be redirected :(. */
|
||||
|
||||
/* Search the end of the line. This is safe because the header will
|
||||
* always end with two newlines. */
|
||||
*strstr(status, NEWLINE) = '\0';
|
||||
DEBUG(net, 0, "[tcp/http] unhandled status reply %s", status);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (this->redirect_depth == 5) return_error("[tcp/http] too many redirects, looping redirects?");
|
||||
|
||||
/* Redirect to other URL */
|
||||
char *uri = strcasestr(this->recv_buffer, LOCATION);
|
||||
if (uri == NULL) return_error("[tcp/http] missing 'location' header for redirect");
|
||||
|
||||
uri += strlen(LOCATION);
|
||||
|
||||
/* Search the end of the line. This is safe because the header will
|
||||
* always end with two newlines. */
|
||||
char *end_of_line = strstr(uri, NEWLINE);
|
||||
*end_of_line = '\0';
|
||||
|
||||
DEBUG(net, 6, "[tcp/http] redirecting to %s", uri);
|
||||
|
||||
int ret = NetworkHTTPSocketHandler::Connect(uri, this->callback, this->data, this->redirect_depth + 1);
|
||||
if (ret != 0) return ret;
|
||||
|
||||
/* We've relinquished control of data now. */
|
||||
this->data = NULL;
|
||||
|
||||
/* Restore the header. */
|
||||
*end_of_line = '\r';
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the given URI.
|
||||
* @param uri the URI to connect to.
|
||||
* @param callback the callback to send data back on.
|
||||
* @param data the data we want to send (as POST).
|
||||
* @param depth the recursion/redirect depth.
|
||||
*/
|
||||
/* static */ int NetworkHTTPSocketHandler::Connect(char *uri, HTTPCallback *callback, const char *data, int depth)
|
||||
{
|
||||
char *hname = strstr(uri, "://");
|
||||
if (hname == NULL) return_error("[tcp/http] invalid location");
|
||||
|
||||
hname += 3;
|
||||
|
||||
char *url = strchr(hname, '/');
|
||||
if (url == NULL) return_error("[tcp/http] invalid location");
|
||||
|
||||
*url = '\0';
|
||||
|
||||
/* Fetch the hostname, and possible port number. */
|
||||
const char *company = NULL;
|
||||
const char *port = NULL;
|
||||
ParseConnectionString(&company, &port, hname);
|
||||
if (company != NULL) return_error("[tcp/http] invalid hostname");
|
||||
|
||||
NetworkAddress address(hname, port == NULL ? 80 : atoi(port));
|
||||
|
||||
/* Restore the URL. */
|
||||
*url = '/';
|
||||
new NetworkHTTPContentConnecter(address, callback, url, data, depth);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef return_error
|
||||
|
||||
/**
|
||||
* Handle receiving of HTTP data.
|
||||
* @return state of the receival of HTTP data.
|
||||
* > 0: we need more cycles for downloading
|
||||
* = 0: we are done downloading
|
||||
* < 0: we have hit an error
|
||||
*/
|
||||
int NetworkHTTPSocketHandler::Receive()
|
||||
{
|
||||
for (;;) {
|
||||
ssize_t res = recv(this->sock, (char *)this->recv_buffer + this->recv_pos, lengthof(this->recv_buffer) - this->recv_pos, 0);
|
||||
if (res == -1) {
|
||||
int err = GET_LAST_ERROR();
|
||||
if (err != EWOULDBLOCK) {
|
||||
/* Something went wrong... (104 is connection reset by peer) */
|
||||
if (err != 104) DEBUG(net, 0, "recv failed with error %d", err);
|
||||
return -1;
|
||||
}
|
||||
/* Connection would block, so stop for now */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* No more data... did we get everything we wanted? */
|
||||
if (res == 0) {
|
||||
if (this->recv_length != 0) return -1;
|
||||
|
||||
this->callback->OnReceiveData(NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Wait till we read the end-of-header identifier */
|
||||
if (this->recv_length == 0) {
|
||||
int read = this->recv_pos + res;
|
||||
int end = min(read, lengthof(this->recv_buffer) - 1);
|
||||
|
||||
/* Do a 'safe' search for the end of the header. */
|
||||
char prev = this->recv_buffer[end];
|
||||
this->recv_buffer[end] = '\0';
|
||||
char *end_of_header = strstr(this->recv_buffer, END_OF_HEADER);
|
||||
this->recv_buffer[end] = prev;
|
||||
|
||||
if (end_of_header == NULL) {
|
||||
if (read == lengthof(this->recv_buffer)) {
|
||||
DEBUG(net, 0, "[tcp/http] header too big");
|
||||
return -1;
|
||||
}
|
||||
this->recv_pos = read;
|
||||
} else {
|
||||
int ret = this->HandleHeader();
|
||||
if (ret <= 0) return ret;
|
||||
|
||||
this->recv_length = ret;
|
||||
|
||||
end_of_header += strlen(END_OF_HEADER);
|
||||
int len = min(read - (end_of_header - this->recv_buffer), res);
|
||||
if (len != 0) {
|
||||
this->callback->OnReceiveData(end_of_header, len);
|
||||
this->recv_length -= len;
|
||||
}
|
||||
|
||||
this->recv_pos = 0;
|
||||
}
|
||||
} else {
|
||||
res = min(this->recv_length, res);
|
||||
/* Receive whatever we're expecting. */
|
||||
this->callback->OnReceiveData(this->recv_buffer, res);
|
||||
this->recv_length -= res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the receiving for all HTTP connections.
|
||||
*/
|
||||
/* static */ void NetworkHTTPSocketHandler::HTTPReceive()
|
||||
{
|
||||
/* No connections, just bail out. */
|
||||
if (_http_connections.Length() == 0) return;
|
||||
|
||||
fd_set read_fd;
|
||||
struct timeval tv;
|
||||
|
||||
FD_ZERO(&read_fd);
|
||||
for (NetworkHTTPSocketHandler **iter = _http_connections.Begin(); iter < _http_connections.End(); iter++) {
|
||||
FD_SET((*iter)->sock, &read_fd);
|
||||
}
|
||||
|
||||
tv.tv_sec = tv.tv_usec = 0; // don't block at all.
|
||||
#if !defined(__MORPHOS__) && !defined(__AMIGA__)
|
||||
int n = select(FD_SETSIZE, &read_fd, NULL, NULL, &tv);
|
||||
#else
|
||||
int n = WaitSelect(FD_SETSIZE, &read_fd, NULL, NULL, &tv, NULL);
|
||||
#endif
|
||||
if (n == -1) return;
|
||||
|
||||
for (NetworkHTTPSocketHandler **iter = _http_connections.Begin(); iter < _http_connections.End(); /* nothing */) {
|
||||
NetworkHTTPSocketHandler *cur = *iter;
|
||||
|
||||
if (FD_ISSET(cur->sock, &read_fd)) {
|
||||
int ret = cur->Receive();
|
||||
/* First send the failure. */
|
||||
if (ret < 0) cur->callback->OnFailure();
|
||||
if (ret <= 0) {
|
||||
/* Then... the connection can be closed */
|
||||
cur->CloseConnection();
|
||||
_http_connections.Erase(iter);
|
||||
delete cur;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
127
src/network/core/tcp_http.h
Normal file
127
src/network/core/tcp_http.h
Normal file
@@ -0,0 +1,127 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file tcp_http.h Basic functions to receive and send HTTP TCP packets.
|
||||
*/
|
||||
|
||||
#ifndef NETWORK_CORE_TCP_HTTP_H
|
||||
#define NETWORK_CORE_TCP_HTTP_H
|
||||
|
||||
#include "tcp.h"
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
/** Callback for when the HTTP handler has something to tell us. */
|
||||
struct HTTPCallback {
|
||||
/**
|
||||
* An error has occurred and the connection has been closed.
|
||||
* @note HTTP socket handler is closed/freed.
|
||||
*/
|
||||
virtual void OnFailure() = 0;
|
||||
|
||||
/**
|
||||
* We're receiving data.
|
||||
* @param data the received data, NULL when all data has been received.
|
||||
* @param length the amount of received data, 0 when all data has been received.
|
||||
* @note When NULL is sent the HTTP socket handler is closed/freed.
|
||||
*/
|
||||
virtual void OnReceiveData(const char *data, size_t length) = 0;
|
||||
|
||||
/** Silentium */
|
||||
virtual ~HTTPCallback() {}
|
||||
};
|
||||
|
||||
/** Base socket handler for HTTP traffic. */
|
||||
class NetworkHTTPSocketHandler : public NetworkSocketHandler {
|
||||
private:
|
||||
char recv_buffer[4096]; ///< Partially received message.
|
||||
int recv_pos; ///< Current position in buffer.
|
||||
int recv_length; ///< Length of the data still retrieving.
|
||||
HTTPCallback *callback; ///< The callback to call for the incoming data.
|
||||
const char *data; ///< The (POST) data we might want to forward (to a redirect).
|
||||
int redirect_depth; ///< The depth of the redirection.
|
||||
|
||||
int HandleHeader();
|
||||
int Receive();
|
||||
public:
|
||||
SOCKET sock; ///< The socket currently connected to
|
||||
|
||||
/**
|
||||
* Whether this socket is currently bound to a socket.
|
||||
* @return true when the socket is bound, false otherwise
|
||||
*/
|
||||
bool IsConnected() const
|
||||
{
|
||||
return this->sock != INVALID_SOCKET;
|
||||
}
|
||||
|
||||
virtual NetworkRecvStatus CloseConnection(bool error = true);
|
||||
|
||||
NetworkHTTPSocketHandler(SOCKET sock, HTTPCallback *callback,
|
||||
const char *host, const char *url, const char *data, int depth);
|
||||
|
||||
~NetworkHTTPSocketHandler();
|
||||
|
||||
static int Connect(char *uri, HTTPCallback *callback,
|
||||
const char *data = NULL, int depth = 0);
|
||||
|
||||
static void HTTPReceive();
|
||||
};
|
||||
|
||||
/** Connect with a HTTP server and do ONE query. */
|
||||
class NetworkHTTPContentConnecter : TCPConnecter {
|
||||
HTTPCallback *callback; ///< Callback to tell that we received some data (or won't).
|
||||
const char *url; ///< The URL we want to get at the server.
|
||||
const char *data; ///< The data to send
|
||||
int depth; ///< How far we have recursed
|
||||
|
||||
public:
|
||||
/**
|
||||
* Start the connecting.
|
||||
* @param address the address to connect to
|
||||
* @param callback the callback for HTTP retrieval
|
||||
* @param url the url at the server
|
||||
* @param data the data to send
|
||||
* @param depth the depth (redirect recursion) of the queries
|
||||
*/
|
||||
NetworkHTTPContentConnecter(const NetworkAddress &address,
|
||||
HTTPCallback *callback, const char *url,
|
||||
const char *data = NULL, int depth = 0) :
|
||||
TCPConnecter(address),
|
||||
callback(callback),
|
||||
url(stredup(url)),
|
||||
data(data),
|
||||
depth(depth)
|
||||
{
|
||||
}
|
||||
|
||||
/** Free all our allocated data. */
|
||||
~NetworkHTTPContentConnecter()
|
||||
{
|
||||
free(this->url);
|
||||
}
|
||||
|
||||
virtual void OnFailure()
|
||||
{
|
||||
this->callback->OnFailure();
|
||||
free(this->data);
|
||||
}
|
||||
|
||||
virtual void OnConnect(SOCKET s)
|
||||
{
|
||||
new NetworkHTTPSocketHandler(s, this->callback, this->address.GetHostname(), this->url, this->data, this->depth);
|
||||
/* We've relinquished control of data now. */
|
||||
this->data = NULL;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
|
||||
#endif /* NETWORK_CORE_TCP_HTTP_H */
|
||||
182
src/network/core/tcp_listen.h
Normal file
182
src/network/core/tcp_listen.h
Normal file
@@ -0,0 +1,182 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file tcp_listen.h Basic functions to listen for TCP connections.
|
||||
*/
|
||||
|
||||
#ifndef NETWORK_CORE_TCP_LISTEN_H
|
||||
#define NETWORK_CORE_TCP_LISTEN_H
|
||||
|
||||
#include "tcp.h"
|
||||
#include "../network.h"
|
||||
#include "../../core/pool_type.hpp"
|
||||
#include "../../debug.h"
|
||||
#include "table/strings.h"
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
/**
|
||||
* Template for TCP listeners.
|
||||
* @param Tsocket The class we create sockets for.
|
||||
* @param Tfull_packet The packet type to return when we don't allow more sockets.
|
||||
* @param Tban_packet The packet type to return when the client is banned.
|
||||
*/
|
||||
template <class Tsocket, PacketType Tfull_packet, PacketType Tban_packet>
|
||||
class TCPListenHandler {
|
||||
/** List of sockets we listen on. */
|
||||
static SocketList sockets;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Accepts clients from the sockets.
|
||||
* @param ls Socket to accept clients from.
|
||||
*/
|
||||
static void AcceptClient(SOCKET ls)
|
||||
{
|
||||
for (;;) {
|
||||
struct sockaddr_storage sin;
|
||||
memset(&sin, 0, sizeof(sin));
|
||||
socklen_t sin_len = sizeof(sin);
|
||||
SOCKET s = accept(ls, (struct sockaddr*)&sin, &sin_len);
|
||||
if (s == INVALID_SOCKET) return;
|
||||
|
||||
SetNonBlocking(s); // XXX error handling?
|
||||
|
||||
NetworkAddress address(sin, sin_len);
|
||||
DEBUG(net, 1, "[%s] Client connected from %s on frame %d", Tsocket::GetName(), address.GetHostname(), _frame_counter);
|
||||
|
||||
SetNoDelay(s); // XXX error handling?
|
||||
|
||||
/* Check if the client is banned */
|
||||
bool banned = false;
|
||||
for (char **iter = _network_ban_list.Begin(); iter != _network_ban_list.End(); iter++) {
|
||||
banned = address.IsInNetmask(*iter);
|
||||
if (banned) {
|
||||
Packet p(Tban_packet);
|
||||
p.PrepareToSend();
|
||||
|
||||
DEBUG(net, 1, "[%s] Banned ip tried to join (%s), refused", Tsocket::GetName(), *iter);
|
||||
|
||||
if (send(s, (const char*)p.buffer, p.size, 0) < 0) {
|
||||
DEBUG(net, 0, "send failed with error %d", GET_LAST_ERROR());
|
||||
}
|
||||
closesocket(s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* If this client is banned, continue with next client */
|
||||
if (banned) continue;
|
||||
|
||||
/* Can we handle a new client? */
|
||||
if (!Tsocket::AllowConnection()) {
|
||||
/* no more clients allowed?
|
||||
* Send to the client that we are full! */
|
||||
Packet p(Tfull_packet);
|
||||
p.PrepareToSend();
|
||||
|
||||
if (send(s, (const char*)p.buffer, p.size, 0) < 0) {
|
||||
DEBUG(net, 0, "send failed with error %d", GET_LAST_ERROR());
|
||||
}
|
||||
closesocket(s);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
Tsocket::AcceptConnection(s, address);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the receiving of packets.
|
||||
* @return true if everything went okay.
|
||||
*/
|
||||
static bool Receive()
|
||||
{
|
||||
fd_set read_fd, write_fd;
|
||||
struct timeval tv;
|
||||
|
||||
FD_ZERO(&read_fd);
|
||||
FD_ZERO(&write_fd);
|
||||
|
||||
|
||||
Tsocket *cs;
|
||||
FOR_ALL_ITEMS_FROM(Tsocket, idx, cs, 0) {
|
||||
FD_SET(cs->sock, &read_fd);
|
||||
FD_SET(cs->sock, &write_fd);
|
||||
}
|
||||
|
||||
/* take care of listener port */
|
||||
for (SocketList::iterator s = sockets.Begin(); s != sockets.End(); s++) {
|
||||
FD_SET(s->second, &read_fd);
|
||||
}
|
||||
|
||||
tv.tv_sec = tv.tv_usec = 0; // don't block at all.
|
||||
#if !defined(__MORPHOS__) && !defined(__AMIGA__)
|
||||
if (select(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv) < 0) return false;
|
||||
#else
|
||||
if (WaitSelect(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv, NULL) < 0) return false;
|
||||
#endif
|
||||
|
||||
/* accept clients.. */
|
||||
for (SocketList::iterator s = sockets.Begin(); s != sockets.End(); s++) {
|
||||
if (FD_ISSET(s->second, &read_fd)) AcceptClient(s->second);
|
||||
}
|
||||
|
||||
/* read stuff from clients */
|
||||
FOR_ALL_ITEMS_FROM(Tsocket, idx, cs, 0) {
|
||||
cs->writable = !!FD_ISSET(cs->sock, &write_fd);
|
||||
if (FD_ISSET(cs->sock, &read_fd)) {
|
||||
cs->ReceivePackets();
|
||||
}
|
||||
}
|
||||
return _networking;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen on a particular port.
|
||||
* @param port The port to listen on.
|
||||
* @return true if listening succeeded.
|
||||
*/
|
||||
static bool Listen(uint16 port)
|
||||
{
|
||||
assert(sockets.Length() == 0);
|
||||
|
||||
NetworkAddressList addresses;
|
||||
GetBindAddresses(&addresses, port);
|
||||
|
||||
for (NetworkAddress *address = addresses.Begin(); address != addresses.End(); address++) {
|
||||
address->Listen(SOCK_STREAM, &sockets);
|
||||
}
|
||||
|
||||
if (sockets.Length() == 0) {
|
||||
DEBUG(net, 0, "[server] could not start network: could not create listening socket");
|
||||
NetworkError(STR_NETWORK_ERROR_SERVER_START);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Close the sockets we're listening on. */
|
||||
static void CloseListeners()
|
||||
{
|
||||
for (SocketList::iterator s = sockets.Begin(); s != sockets.End(); s++) {
|
||||
closesocket(s->second);
|
||||
}
|
||||
sockets.Clear();
|
||||
DEBUG(net, 1, "[%s] closed listeners", Tsocket::GetName());
|
||||
}
|
||||
};
|
||||
|
||||
template <class Tsocket, PacketType Tfull_packet, PacketType Tban_packet> SocketList TCPListenHandler<Tsocket, Tfull_packet, Tban_packet>::sockets;
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
|
||||
#endif /* NETWORK_CORE_TCP_LISTEN_H */
|
||||
353
src/network/core/udp.cpp
Normal file
353
src/network/core/udp.cpp
Normal file
@@ -0,0 +1,353 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file core/udp.cpp Basic functions to receive and send UDP packets.
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
#include "../../stdafx.h"
|
||||
#include "../../date_func.h"
|
||||
#include "../../debug.h"
|
||||
#include "udp.h"
|
||||
|
||||
#include "../../safeguards.h"
|
||||
|
||||
/**
|
||||
* Create an UDP socket but don't listen yet.
|
||||
* @param bind the addresses to bind to.
|
||||
*/
|
||||
NetworkUDPSocketHandler::NetworkUDPSocketHandler(NetworkAddressList *bind)
|
||||
{
|
||||
if (bind != NULL) {
|
||||
for (NetworkAddress *addr = bind->Begin(); addr != bind->End(); addr++) {
|
||||
*this->bind.Append() = *addr;
|
||||
}
|
||||
} else {
|
||||
/* As hostname NULL and port 0/NULL don't go well when
|
||||
* resolving it we need to add an address for each of
|
||||
* the address families we support. */
|
||||
*this->bind.Append() = NetworkAddress(NULL, 0, AF_INET);
|
||||
*this->bind.Append() = NetworkAddress(NULL, 0, AF_INET6);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Start listening on the given host and port.
|
||||
* @return true if at least one port is listening
|
||||
*/
|
||||
bool NetworkUDPSocketHandler::Listen()
|
||||
{
|
||||
/* Make sure socket is closed */
|
||||
this->Close();
|
||||
|
||||
for (NetworkAddress *addr = this->bind.Begin(); addr != this->bind.End(); addr++) {
|
||||
addr->Listen(SOCK_DGRAM, &this->sockets);
|
||||
}
|
||||
|
||||
return this->sockets.Length() != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the given UDP socket
|
||||
*/
|
||||
void NetworkUDPSocketHandler::Close()
|
||||
{
|
||||
for (SocketList::iterator s = this->sockets.Begin(); s != this->sockets.End(); s++) {
|
||||
closesocket(s->second);
|
||||
}
|
||||
this->sockets.Clear();
|
||||
}
|
||||
|
||||
NetworkRecvStatus NetworkUDPSocketHandler::CloseConnection(bool error)
|
||||
{
|
||||
NetworkSocketHandler::CloseConnection(error);
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a packet over UDP
|
||||
* @param p the packet to send
|
||||
* @param recv the receiver (target) of the packet
|
||||
* @param all send the packet using all sockets that can send it
|
||||
* @param broadcast whether to send a broadcast message
|
||||
*/
|
||||
void NetworkUDPSocketHandler::SendPacket(Packet *p, NetworkAddress *recv, bool all, bool broadcast)
|
||||
{
|
||||
if (this->sockets.Length() == 0) this->Listen();
|
||||
|
||||
for (SocketList::iterator s = this->sockets.Begin(); s != this->sockets.End(); s++) {
|
||||
/* Make a local copy because if we resolve it we cannot
|
||||
* easily unresolve it so we can resolve it later again. */
|
||||
NetworkAddress send(*recv);
|
||||
|
||||
/* Not the same type */
|
||||
if (!send.IsFamily(s->first.GetAddress()->ss_family)) continue;
|
||||
|
||||
p->PrepareToSend();
|
||||
|
||||
#ifndef BEOS_NET_SERVER /* will work around this, some day; maybe. */
|
||||
if (broadcast) {
|
||||
/* Enable broadcast */
|
||||
unsigned long val = 1;
|
||||
if (setsockopt(s->second, SOL_SOCKET, SO_BROADCAST, (char *) &val, sizeof(val)) < 0) {
|
||||
DEBUG(net, 1, "[udp] setting broadcast failed with: %i", GET_LAST_ERROR());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Send the buffer */
|
||||
int res = sendto(s->second, (const char*)p->buffer, p->size, 0, (const struct sockaddr *)send.GetAddress(), send.GetAddressLength());
|
||||
DEBUG(net, 7, "[udp] sendto(%s)", send.GetAddressAsString());
|
||||
|
||||
/* Check for any errors, but ignore it otherwise */
|
||||
if (res == -1) DEBUG(net, 1, "[udp] sendto(%s) failed with: %i", send.GetAddressAsString(), GET_LAST_ERROR());
|
||||
|
||||
if (!all) break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive a packet at UDP level
|
||||
*/
|
||||
void NetworkUDPSocketHandler::ReceivePackets()
|
||||
{
|
||||
for (SocketList::iterator s = this->sockets.Begin(); s != this->sockets.End(); s++) {
|
||||
for (int i = 0; i < 1000; i++) { // Do not infinitely loop when DoSing with UDP
|
||||
struct sockaddr_storage client_addr;
|
||||
memset(&client_addr, 0, sizeof(client_addr));
|
||||
|
||||
Packet p(this);
|
||||
socklen_t client_len = sizeof(client_addr);
|
||||
|
||||
/* Try to receive anything */
|
||||
SetNonBlocking(s->second); // Some OSes seem to lose the non-blocking status of the socket
|
||||
int nbytes = recvfrom(s->second, (char*)p.buffer, SEND_MTU, 0, (struct sockaddr *)&client_addr, &client_len);
|
||||
|
||||
/* Did we get the bytes for the base header of the packet? */
|
||||
if (nbytes <= 0) break; // No data, i.e. no packet
|
||||
if (nbytes <= 2) continue; // Invalid data; try next packet
|
||||
|
||||
NetworkAddress address(client_addr, client_len);
|
||||
p.PrepareToRead();
|
||||
|
||||
/* If the size does not match the packet must be corrupted.
|
||||
* Otherwise it will be marked as corrupted later on. */
|
||||
if (nbytes != p.size) {
|
||||
DEBUG(net, 1, "received a packet with mismatching size from %s", address.GetAddressAsString());
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Handle the packet */
|
||||
this->HandleUDPPacket(&p, &address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the NetworkGameInfo struct to the packet
|
||||
* @param p the packet to write the data to
|
||||
* @param info the NetworkGameInfo struct to serialize
|
||||
*/
|
||||
void NetworkUDPSocketHandler::SendNetworkGameInfo(Packet *p, const NetworkGameInfo *info)
|
||||
{
|
||||
p->Send_uint8 (NETWORK_GAME_INFO_VERSION);
|
||||
|
||||
/*
|
||||
* Please observe the order.
|
||||
* The parts must be read in the same order as they are sent!
|
||||
*/
|
||||
|
||||
/* Update the documentation in udp.h on changes
|
||||
* to the NetworkGameInfo wire-protocol! */
|
||||
|
||||
/* NETWORK_GAME_INFO_VERSION = 4 */
|
||||
{
|
||||
/* Only send the GRF Identification (GRF_ID and MD5 checksum) of
|
||||
* the GRFs that are needed, i.e. the ones that the server has
|
||||
* selected in the NewGRF GUI and not the ones that are used due
|
||||
* to the fact that they are in [newgrf-static] in openttd.cfg */
|
||||
const GRFConfig *c;
|
||||
uint count = 0;
|
||||
|
||||
/* Count number of GRFs to send information about */
|
||||
for (c = info->grfconfig; c != NULL; c = c->next) {
|
||||
if (!HasBit(c->flags, GCF_STATIC)) count++;
|
||||
}
|
||||
p->Send_uint8 (count); // Send number of GRFs
|
||||
|
||||
/* Send actual GRF Identifications */
|
||||
for (c = info->grfconfig; c != NULL; c = c->next) {
|
||||
if (!HasBit(c->flags, GCF_STATIC)) this->SendGRFIdentifier(p, &c->ident);
|
||||
}
|
||||
}
|
||||
|
||||
/* NETWORK_GAME_INFO_VERSION = 3 */
|
||||
p->Send_uint32(info->game_date);
|
||||
p->Send_uint32(info->start_date);
|
||||
|
||||
/* NETWORK_GAME_INFO_VERSION = 2 */
|
||||
p->Send_uint8 (info->companies_max);
|
||||
p->Send_uint8 (info->companies_on);
|
||||
p->Send_uint8 (info->spectators_max);
|
||||
|
||||
/* NETWORK_GAME_INFO_VERSION = 1 */
|
||||
p->Send_string(info->server_name);
|
||||
p->Send_string(info->server_revision);
|
||||
p->Send_uint8 (info->server_lang);
|
||||
p->Send_bool (info->use_password);
|
||||
p->Send_uint8 (info->clients_max);
|
||||
p->Send_uint8 (info->clients_on);
|
||||
p->Send_uint8 (info->spectators_on);
|
||||
p->Send_string(info->map_name);
|
||||
p->Send_uint16(info->map_width);
|
||||
p->Send_uint16(info->map_height);
|
||||
p->Send_uint8 (info->map_set);
|
||||
p->Send_bool (info->dedicated);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes the NetworkGameInfo struct from the packet
|
||||
* @param p the packet to read the data from
|
||||
* @param info the NetworkGameInfo to deserialize into
|
||||
*/
|
||||
void NetworkUDPSocketHandler::ReceiveNetworkGameInfo(Packet *p, NetworkGameInfo *info)
|
||||
{
|
||||
static const Date MAX_DATE = ConvertYMDToDate(MAX_YEAR, 11, 31); // December is month 11
|
||||
|
||||
info->game_info_version = p->Recv_uint8();
|
||||
|
||||
/*
|
||||
* Please observe the order.
|
||||
* The parts must be read in the same order as they are sent!
|
||||
*/
|
||||
|
||||
/* Update the documentation in udp.h on changes
|
||||
* to the NetworkGameInfo wire-protocol! */
|
||||
|
||||
switch (info->game_info_version) {
|
||||
case 4: {
|
||||
GRFConfig **dst = &info->grfconfig;
|
||||
uint i;
|
||||
uint num_grfs = p->Recv_uint8();
|
||||
|
||||
/* Broken/bad data. It cannot have that many NewGRFs. */
|
||||
if (num_grfs > NETWORK_MAX_GRF_COUNT) return;
|
||||
|
||||
for (i = 0; i < num_grfs; i++) {
|
||||
GRFConfig *c = new GRFConfig();
|
||||
this->ReceiveGRFIdentifier(p, &c->ident);
|
||||
this->HandleIncomingNetworkGameInfoGRFConfig(c);
|
||||
|
||||
/* Append GRFConfig to the list */
|
||||
*dst = c;
|
||||
dst = &c->next;
|
||||
}
|
||||
FALLTHROUGH;
|
||||
}
|
||||
|
||||
case 3:
|
||||
info->game_date = Clamp(p->Recv_uint32(), 0, MAX_DATE);
|
||||
info->start_date = Clamp(p->Recv_uint32(), 0, MAX_DATE);
|
||||
FALLTHROUGH;
|
||||
|
||||
case 2:
|
||||
info->companies_max = p->Recv_uint8 ();
|
||||
info->companies_on = p->Recv_uint8 ();
|
||||
info->spectators_max = p->Recv_uint8 ();
|
||||
FALLTHROUGH;
|
||||
|
||||
case 1:
|
||||
p->Recv_string(info->server_name, sizeof(info->server_name));
|
||||
p->Recv_string(info->server_revision, sizeof(info->server_revision));
|
||||
info->server_lang = p->Recv_uint8 ();
|
||||
info->use_password = p->Recv_bool ();
|
||||
info->clients_max = p->Recv_uint8 ();
|
||||
info->clients_on = p->Recv_uint8 ();
|
||||
info->spectators_on = p->Recv_uint8 ();
|
||||
if (info->game_info_version < 3) { // 16 bits dates got scrapped and are read earlier
|
||||
info->game_date = p->Recv_uint16() + DAYS_TILL_ORIGINAL_BASE_YEAR;
|
||||
info->start_date = p->Recv_uint16() + DAYS_TILL_ORIGINAL_BASE_YEAR;
|
||||
}
|
||||
p->Recv_string(info->map_name, sizeof(info->map_name));
|
||||
info->map_width = p->Recv_uint16();
|
||||
info->map_height = p->Recv_uint16();
|
||||
info->map_set = p->Recv_uint8 ();
|
||||
info->dedicated = p->Recv_bool ();
|
||||
|
||||
if (info->server_lang >= NETWORK_NUM_LANGUAGES) info->server_lang = 0;
|
||||
if (info->map_set >= NETWORK_NUM_LANDSCAPES) info->map_set = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming packets by sending it to the correct function.
|
||||
* @param p the received packet
|
||||
* @param client_addr the sender of the packet
|
||||
*/
|
||||
void NetworkUDPSocketHandler::HandleUDPPacket(Packet *p, NetworkAddress *client_addr)
|
||||
{
|
||||
PacketUDPType type;
|
||||
|
||||
/* New packet == new client, which has not quit yet */
|
||||
this->Reopen();
|
||||
|
||||
type = (PacketUDPType)p->Recv_uint8();
|
||||
|
||||
switch (this->HasClientQuit() ? PACKET_UDP_END : type) {
|
||||
case PACKET_UDP_CLIENT_FIND_SERVER: this->Receive_CLIENT_FIND_SERVER(p, client_addr); break;
|
||||
case PACKET_UDP_SERVER_RESPONSE: this->Receive_SERVER_RESPONSE(p, client_addr); break;
|
||||
case PACKET_UDP_CLIENT_DETAIL_INFO: this->Receive_CLIENT_DETAIL_INFO(p, client_addr); break;
|
||||
case PACKET_UDP_SERVER_DETAIL_INFO: this->Receive_SERVER_DETAIL_INFO(p, client_addr); break;
|
||||
case PACKET_UDP_SERVER_REGISTER: this->Receive_SERVER_REGISTER(p, client_addr); break;
|
||||
case PACKET_UDP_MASTER_ACK_REGISTER: this->Receive_MASTER_ACK_REGISTER(p, client_addr); break;
|
||||
case PACKET_UDP_CLIENT_GET_LIST: this->Receive_CLIENT_GET_LIST(p, client_addr); break;
|
||||
case PACKET_UDP_MASTER_RESPONSE_LIST: this->Receive_MASTER_RESPONSE_LIST(p, client_addr); break;
|
||||
case PACKET_UDP_SERVER_UNREGISTER: this->Receive_SERVER_UNREGISTER(p, client_addr); break;
|
||||
case PACKET_UDP_CLIENT_GET_NEWGRFS: this->Receive_CLIENT_GET_NEWGRFS(p, client_addr); break;
|
||||
case PACKET_UDP_SERVER_NEWGRFS: this->Receive_SERVER_NEWGRFS(p, client_addr); break;
|
||||
case PACKET_UDP_MASTER_SESSION_KEY: this->Receive_MASTER_SESSION_KEY(p, client_addr); break;
|
||||
|
||||
default:
|
||||
if (this->HasClientQuit()) {
|
||||
DEBUG(net, 0, "[udp] received invalid packet type %d from %s", type, client_addr->GetAddressAsString());
|
||||
} else {
|
||||
DEBUG(net, 0, "[udp] received illegal packet from %s", client_addr->GetAddressAsString());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for logging receiving invalid packets.
|
||||
* @param type The received packet type.
|
||||
* @param client_addr The address we received the packet from.
|
||||
*/
|
||||
void NetworkUDPSocketHandler::ReceiveInvalidPacket(PacketUDPType type, NetworkAddress *client_addr)
|
||||
{
|
||||
DEBUG(net, 0, "[udp] received packet type %d on wrong port from %s", type, client_addr->GetAddressAsString());
|
||||
}
|
||||
|
||||
void NetworkUDPSocketHandler::Receive_CLIENT_FIND_SERVER(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_CLIENT_FIND_SERVER, client_addr); }
|
||||
void NetworkUDPSocketHandler::Receive_SERVER_RESPONSE(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_SERVER_RESPONSE, client_addr); }
|
||||
void NetworkUDPSocketHandler::Receive_CLIENT_DETAIL_INFO(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_CLIENT_DETAIL_INFO, client_addr); }
|
||||
void NetworkUDPSocketHandler::Receive_SERVER_DETAIL_INFO(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_SERVER_DETAIL_INFO, client_addr); }
|
||||
void NetworkUDPSocketHandler::Receive_SERVER_REGISTER(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_SERVER_REGISTER, client_addr); }
|
||||
void NetworkUDPSocketHandler::Receive_MASTER_ACK_REGISTER(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_MASTER_ACK_REGISTER, client_addr); }
|
||||
void NetworkUDPSocketHandler::Receive_CLIENT_GET_LIST(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_CLIENT_GET_LIST, client_addr); }
|
||||
void NetworkUDPSocketHandler::Receive_MASTER_RESPONSE_LIST(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_MASTER_RESPONSE_LIST, client_addr); }
|
||||
void NetworkUDPSocketHandler::Receive_SERVER_UNREGISTER(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_SERVER_UNREGISTER, client_addr); }
|
||||
void NetworkUDPSocketHandler::Receive_CLIENT_GET_NEWGRFS(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_CLIENT_GET_NEWGRFS, client_addr); }
|
||||
void NetworkUDPSocketHandler::Receive_SERVER_NEWGRFS(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_SERVER_NEWGRFS, client_addr); }
|
||||
void NetworkUDPSocketHandler::Receive_MASTER_SESSION_KEY(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_MASTER_SESSION_KEY, client_addr); }
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
251
src/network/core/udp.h
Normal file
251
src/network/core/udp.h
Normal file
@@ -0,0 +1,251 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file core/udp.h Basic functions to receive and send UDP packets.
|
||||
*/
|
||||
|
||||
#ifndef NETWORK_CORE_UDP_H
|
||||
#define NETWORK_CORE_UDP_H
|
||||
|
||||
#include "address.h"
|
||||
#include "game.h"
|
||||
#include "packet.h"
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
/** Enum with all types of UDP packets. The order MUST not be changed **/
|
||||
enum PacketUDPType {
|
||||
PACKET_UDP_CLIENT_FIND_SERVER, ///< Queries a game server for game information
|
||||
PACKET_UDP_SERVER_RESPONSE, ///< Reply of the game server with game information
|
||||
PACKET_UDP_CLIENT_DETAIL_INFO, ///< Queries a game server about details of the game, such as companies
|
||||
PACKET_UDP_SERVER_DETAIL_INFO, ///< Reply of the game server about details of the game, such as companies
|
||||
PACKET_UDP_SERVER_REGISTER, ///< Packet to register itself to the master server
|
||||
PACKET_UDP_MASTER_ACK_REGISTER, ///< Packet indicating registration has succeeded
|
||||
PACKET_UDP_CLIENT_GET_LIST, ///< Request for serverlist from master server
|
||||
PACKET_UDP_MASTER_RESPONSE_LIST, ///< Response from master server with server ip's + port's
|
||||
PACKET_UDP_SERVER_UNREGISTER, ///< Request to be removed from the server-list
|
||||
PACKET_UDP_CLIENT_GET_NEWGRFS, ///< Requests the name for a list of GRFs (GRF_ID and MD5)
|
||||
PACKET_UDP_SERVER_NEWGRFS, ///< Sends the list of NewGRF's requested.
|
||||
PACKET_UDP_MASTER_SESSION_KEY, ///< Sends a fresh session key to the client
|
||||
PACKET_UDP_END, ///< Must ALWAYS be on the end of this list!! (period)
|
||||
};
|
||||
|
||||
/** The types of server lists we can get */
|
||||
enum ServerListType {
|
||||
SLT_IPv4 = 0, ///< Get the IPv4 addresses
|
||||
SLT_IPv6 = 1, ///< Get the IPv6 addresses
|
||||
SLT_AUTODETECT, ///< Autodetect the type based on the connection
|
||||
|
||||
SLT_END = SLT_AUTODETECT, ///< End of 'arrays' marker
|
||||
};
|
||||
|
||||
/** Base socket handler for all UDP sockets */
|
||||
class NetworkUDPSocketHandler : public NetworkSocketHandler {
|
||||
protected:
|
||||
/** The address to bind to. */
|
||||
NetworkAddressList bind;
|
||||
/** The opened sockets. */
|
||||
SocketList sockets;
|
||||
|
||||
NetworkRecvStatus CloseConnection(bool error = true);
|
||||
|
||||
void ReceiveInvalidPacket(PacketUDPType, NetworkAddress *client_addr);
|
||||
|
||||
/**
|
||||
* Queries to the server for information about the game.
|
||||
* @param p The received packet.
|
||||
* @param client_addr The origin of the packet.
|
||||
*/
|
||||
virtual void Receive_CLIENT_FIND_SERVER(Packet *p, NetworkAddress *client_addr);
|
||||
|
||||
/**
|
||||
* Return of server information to the client.
|
||||
* This packet has several legacy versions, so we list the version and size of each "field":
|
||||
*
|
||||
* Version: Bytes: Description:
|
||||
* all 1 the version of this packet's structure
|
||||
*
|
||||
* 4+ 1 number of GRFs attached (n)
|
||||
* 4+ n * 20 unique identifier for GRF files. Consists of:
|
||||
* - one 4 byte variable with the GRF ID
|
||||
* - 16 bytes (sent sequentially) for the MD5 checksum
|
||||
* of the GRF
|
||||
*
|
||||
* 3+ 4 current game date in days since 1-1-0 (DMY)
|
||||
* 3+ 4 game introduction date in days since 1-1-0 (DMY)
|
||||
*
|
||||
* 2+ 1 maximum number of companies allowed on the server
|
||||
* 2+ 1 number of companies on the server
|
||||
* 2+ 1 maximum number of spectators allowed on the server
|
||||
*
|
||||
* 1+ var string with the name of the server
|
||||
* 1+ var string with the revision of the server
|
||||
* 1+ 1 the language run on the server
|
||||
* (0 = any, 1 = English, 2 = German, 3 = French)
|
||||
* 1+ 1 whether the server uses a password (0 = no, 1 = yes)
|
||||
* 1+ 1 maximum number of clients allowed on the server
|
||||
* 1+ 1 number of clients on the server
|
||||
* 1+ 1 number of spectators on the server
|
||||
* 1 & 2 2 current game date in days since 1-1-1920 (DMY)
|
||||
* 1 & 2 2 game introduction date in days since 1-1-1920 (DMY)
|
||||
* 1+ var string with the name of the map
|
||||
* 1+ 2 width of the map in tiles
|
||||
* 1+ 2 height of the map in tiles
|
||||
* 1+ 1 type of map:
|
||||
* (0 = temperate, 1 = arctic, 2 = desert, 3 = toyland)
|
||||
* 1+ 1 whether the server is dedicated (0 = no, 1 = yes)
|
||||
* @param p The received packet.
|
||||
* @param client_addr The origin of the packet.
|
||||
*/
|
||||
virtual void Receive_SERVER_RESPONSE(Packet *p, NetworkAddress *client_addr);
|
||||
|
||||
/**
|
||||
* Query for detailed information about companies.
|
||||
* @param p The received packet.
|
||||
* @param client_addr The origin of the packet.
|
||||
*/
|
||||
virtual void Receive_CLIENT_DETAIL_INFO(Packet *p, NetworkAddress *client_addr);
|
||||
|
||||
/**
|
||||
* Reply with detailed company information.
|
||||
* uint8 Version of the packet.
|
||||
* uint8 Number of companies.
|
||||
* For each company:
|
||||
* uint8 ID of the company.
|
||||
* string Name of the company.
|
||||
* uint32 Year the company was inaugurated.
|
||||
* uint64 Value.
|
||||
* uint64 Money.
|
||||
* uint64 Income.
|
||||
* uint16 Performance (last quarter).
|
||||
* bool Company is password protected.
|
||||
* uint16 Number of trains.
|
||||
* uint16 Number of lorries.
|
||||
* uint16 Number of busses.
|
||||
* uint16 Number of planes.
|
||||
* uint16 Number of ships.
|
||||
* uint16 Number of train stations.
|
||||
* uint16 Number of lorry stations.
|
||||
* uint16 Number of bus stops.
|
||||
* uint16 Number of airports and heliports.
|
||||
* uint16 Number of harbours.
|
||||
* bool Company is an AI.
|
||||
* @param p The received packet.
|
||||
* @param client_addr The origin of the packet.
|
||||
*/
|
||||
virtual void Receive_SERVER_DETAIL_INFO(Packet *p, NetworkAddress *client_addr);
|
||||
|
||||
/**
|
||||
* Registers the server to the master server.
|
||||
* string The "welcome" message to root out other binary packets.
|
||||
* uint8 Version of the protocol.
|
||||
* uint16 The port to unregister.
|
||||
* uint64 The session key.
|
||||
* @param p The received packet.
|
||||
* @param client_addr The origin of the packet.
|
||||
*/
|
||||
virtual void Receive_SERVER_REGISTER(Packet *p, NetworkAddress *client_addr);
|
||||
|
||||
/**
|
||||
* The master server acknowledges the registration.
|
||||
* @param p The received packet.
|
||||
* @param client_addr The origin of the packet.
|
||||
*/
|
||||
virtual void Receive_MASTER_ACK_REGISTER(Packet *p, NetworkAddress *client_addr);
|
||||
|
||||
/**
|
||||
* The client requests a list of servers.
|
||||
* uint8 The protocol version.
|
||||
* uint8 The type of server to look for: IPv4, IPv6 or based on the received packet.
|
||||
* @param p The received packet.
|
||||
* @param client_addr The origin of the packet.
|
||||
*/
|
||||
virtual void Receive_CLIENT_GET_LIST(Packet *p, NetworkAddress *client_addr);
|
||||
|
||||
/**
|
||||
* The server sends a list of servers.
|
||||
* uint8 The protocol version.
|
||||
* For each server:
|
||||
* 4 or 16 bytes of IPv4 or IPv6 address.
|
||||
* uint8 The port.
|
||||
* @param p The received packet.
|
||||
* @param client_addr The origin of the packet.
|
||||
*/
|
||||
virtual void Receive_MASTER_RESPONSE_LIST(Packet *p, NetworkAddress *client_addr);
|
||||
|
||||
/**
|
||||
* A server unregisters itself at the master server.
|
||||
* uint8 Version of the protocol.
|
||||
* uint16 The port to unregister.
|
||||
* @param p The received packet.
|
||||
* @param client_addr The origin of the packet.
|
||||
*/
|
||||
virtual void Receive_SERVER_UNREGISTER(Packet *p, NetworkAddress *client_addr);
|
||||
|
||||
/**
|
||||
* The client requests information about some NewGRFs.
|
||||
* uint8 The number of NewGRFs information is requested about.
|
||||
* For each NewGRF:
|
||||
* uint32 The GRFID.
|
||||
* 16 * uint8 MD5 checksum of the GRF.
|
||||
* @param p The received packet.
|
||||
* @param client_addr The origin of the packet.
|
||||
*/
|
||||
virtual void Receive_CLIENT_GET_NEWGRFS(Packet *p, NetworkAddress *client_addr);
|
||||
|
||||
/**
|
||||
* The server returns information about some NewGRFs.
|
||||
* uint8 The number of NewGRFs information is requested about.
|
||||
* For each NewGRF:
|
||||
* uint32 The GRFID.
|
||||
* 16 * uint8 MD5 checksum of the GRF.
|
||||
* string The name of the NewGRF.
|
||||
* @param p The received packet.
|
||||
* @param client_addr The origin of the packet.
|
||||
*/
|
||||
virtual void Receive_SERVER_NEWGRFS(Packet *p, NetworkAddress *client_addr);
|
||||
|
||||
/**
|
||||
* The master server sends us a session key.
|
||||
* uint64 The session key.
|
||||
* @param p The received packet.
|
||||
* @param client_addr The origin of the packet.
|
||||
*/
|
||||
virtual void Receive_MASTER_SESSION_KEY(Packet *p, NetworkAddress *client_addr);
|
||||
|
||||
void HandleUDPPacket(Packet *p, NetworkAddress *client_addr);
|
||||
|
||||
/**
|
||||
* Function that is called for every GRFConfig that is read when receiving
|
||||
* a NetworkGameInfo. Only grfid and md5sum are set, the rest is zero. This
|
||||
* function must set all appropriate fields. This GRF is later appended to
|
||||
* the grfconfig list of the NetworkGameInfo.
|
||||
* @param config the GRF to handle
|
||||
*/
|
||||
virtual void HandleIncomingNetworkGameInfoGRFConfig(GRFConfig *config) { NOT_REACHED(); }
|
||||
public:
|
||||
NetworkUDPSocketHandler(NetworkAddressList *bind = NULL);
|
||||
|
||||
/** On destructing of this class, the socket needs to be closed */
|
||||
virtual ~NetworkUDPSocketHandler() { this->Close(); }
|
||||
|
||||
bool Listen();
|
||||
void Close();
|
||||
|
||||
void SendPacket(Packet *p, NetworkAddress *recv, bool all = false, bool broadcast = false);
|
||||
void ReceivePackets();
|
||||
|
||||
void SendNetworkGameInfo(Packet *p, const NetworkGameInfo *info);
|
||||
void ReceiveNetworkGameInfo(Packet *p, NetworkGameInfo *info);
|
||||
};
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
|
||||
#endif /* NETWORK_CORE_UDP_H */
|
||||
Reference in New Issue
Block a user