rubidium@5469: /* $Id$ */ rubidium@5469: rubidium@5469: #ifdef ENABLE_NETWORK rubidium@5469: rubidium@5469: #include "../../stdafx.h" rubidium@5469: #include "../../debug.h" rubidium@5469: #include "../../macros.h" rubidium@5587: #include "../../helpers.hpp" rubidium@5469: #include "packet.h" rubidium@5469: #include "udp.h" rubidium@5469: rubidium@5469: /** rubidium@5864: * @file core/udp.cpp Basic functions to receive and send UDP packets. rubidium@5469: */ rubidium@5469: rubidium@5469: /** rubidium@5469: * Start listening on the given host and port. rubidium@5469: * @param host the host (ip) to listen on rubidium@5469: * @param port the port to listen on rubidium@5469: * @param broadcast whether to allow broadcast sending/receiving rubidium@5469: * @return true if the listening succeeded rubidium@5469: */ rubidium@5619: bool NetworkUDPSocketHandler::Listen(const uint32 host, const uint16 port, const bool broadcast) rubidium@5469: { rubidium@5469: struct sockaddr_in sin; rubidium@5469: rubidium@5469: /* Make sure socket is closed */ rubidium@5619: this->Close(); rubidium@5469: rubidium@5624: this->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); rubidium@5624: if (!this->IsConnected()) { rubidium@5469: DEBUG(net, 0, "[udp] failed to start UDP listener"); rubidium@5469: return false; rubidium@5469: } rubidium@5469: rubidium@5469: /* set nonblocking mode for socket */ rubidium@5469: { rubidium@5469: unsigned long blocking = 1; rubidium@5469: #ifndef BEOS_NET_SERVER rubidium@5624: ioctlsocket(this->sock, FIONBIO, &blocking); rubidium@5469: #else rubidium@5624: setsockopt(this->sock, SOL_SOCKET, SO_NONBLOCK, &blocking, NULL); rubidium@5469: #endif rubidium@5469: } rubidium@5469: rubidium@5469: sin.sin_family = AF_INET; rubidium@5469: /* Listen on all IPs */ rubidium@5469: sin.sin_addr.s_addr = host; rubidium@5469: sin.sin_port = htons(port); rubidium@5469: rubidium@5624: if (bind(this->sock, (struct sockaddr*)&sin, sizeof(sin)) != 0) { rubidium@5469: DEBUG(net, 0, "[udp] bind failed on %s:%i", inet_ntoa(*(struct in_addr *)&host), port); rubidium@5469: return false; rubidium@5469: } rubidium@5469: rubidium@5469: if (broadcast) { rubidium@5469: /* Enable broadcast */ rubidium@5469: unsigned long val = 1; rubidium@5469: #ifndef BEOS_NET_SERVER // will work around this, some day; maybe. rubidium@5624: setsockopt(this->sock, SOL_SOCKET, SO_BROADCAST, (char *) &val , sizeof(val)); rubidium@5469: #endif rubidium@5469: } rubidium@5469: rubidium@5469: DEBUG(net, 1, "[udp] listening on port %s:%d", inet_ntoa(*(struct in_addr *)&host), port); rubidium@5469: rubidium@5469: return true; rubidium@5469: } rubidium@5469: rubidium@5469: /** rubidium@5521: * Close the given UDP socket rubidium@5521: */ rubidium@5619: void NetworkUDPSocketHandler::Close() rubidium@5521: { rubidium@5624: if (!this->IsConnected()) return; rubidium@5521: rubidium@5624: closesocket(this->sock); rubidium@5624: this->sock = INVALID_SOCKET; rubidium@5521: } rubidium@5521: rubidium@5624: NetworkRecvStatus NetworkUDPSocketHandler::CloseConnection() rubidium@5624: { rubidium@5624: this->has_quit = true; rubidium@5624: return NETWORK_RECV_STATUS_OKAY; rubidium@5624: } rubidium@5521: rubidium@5521: /** rubidium@5521: * Send a packet over UDP rubidium@5521: * @param p the packet to send rubidium@5521: * @param recv the receiver (target) of the packet rubidium@5521: */ rubidium@5619: void NetworkUDPSocketHandler::SendPacket(Packet *p, const struct sockaddr_in *recv) rubidium@5521: { rubidium@5521: int res; rubidium@5521: rubidium@5521: NetworkSend_FillPacketSize(p); rubidium@5521: rubidium@5521: /* Send the buffer */ rubidium@5624: res = sendto(this->sock, (const char*)p->buffer, p->size, 0, (struct sockaddr *)recv, sizeof(*recv)); rubidium@5521: rubidium@5521: /* Check for any errors, but ignore it otherwise */ rubidium@5521: if (res == -1) DEBUG(net, 1, "[udp] sendto failed with: %i", GET_LAST_ERROR()); rubidium@5521: } rubidium@5521: rubidium@5521: /** rubidium@5469: * Receive a packet at UDP level rubidium@5469: */ rubidium@5619: void NetworkUDPSocketHandler::ReceivePackets() rubidium@5469: { rubidium@5469: struct sockaddr_in client_addr; rubidium@5469: socklen_t client_len; rubidium@5469: int nbytes; rubidium@5469: Packet p; rubidium@5469: int packet_len; rubidium@5469: rubidium@5624: if (!this->IsConnected()) return; rubidium@5619: rubidium@5469: packet_len = sizeof(p.buffer); rubidium@5469: client_len = sizeof(client_addr); rubidium@5469: rubidium@5469: /* Try to receive anything */ rubidium@5624: nbytes = recvfrom(this->sock, (char*)p.buffer, packet_len, 0, (struct sockaddr *)&client_addr, &client_len); rubidium@5469: rubidium@5572: /* We got some bytes for the base header of the packet. */ rubidium@5469: if (nbytes > 2) { rubidium@5469: NetworkRecv_ReadPacketSize(&p); rubidium@5469: rubidium@5572: /* If the size does not match the packet must be corrupted. rubidium@5572: * Otherwise it will be marked as corrupted later on. */ rubidium@5572: if (nbytes != p.size) { rubidium@5572: DEBUG(net, 1, "received a packet with mismatching size from %s:%d", rubidium@5572: inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); rubidium@5572: rubidium@5572: return; rubidium@5572: } rubidium@5572: rubidium@5469: /* Put the position on the right place */ rubidium@5469: p.pos = 2; rubidium@5469: p.next = NULL; rubidium@5469: rubidium@5469: /* Handle the packet */ rubidium@5619: this->HandleUDPPacket(&p, &client_addr); rubidium@5469: } rubidium@5469: } rubidium@5469: rubidium@5469: rubidium@5469: /** rubidium@5469: * Serializes the NetworkGameInfo struct to the packet rubidium@5469: * @param p the packet to write the data to rubidium@5469: * @param info the NetworkGameInfo struct to serialize rubidium@5469: */ rubidium@5619: void NetworkUDPSocketHandler::Send_NetworkGameInfo(Packet *p, const NetworkGameInfo *info) rubidium@5469: { rubidium@5469: NetworkSend_uint8 (p, NETWORK_GAME_INFO_VERSION); rubidium@5469: rubidium@5469: /* rubidium@5469: * Please observe the order. rubidium@5469: * The parts must be read in the same order as they are sent! rubidium@5469: */ rubidium@5469: rubidium@5545: /* Update the documentation in udp.h on changes rubidium@5545: * to the NetworkGameInfo wire-protocol! */ rubidium@5469: rubidium@5469: /* NETWORK_GAME_INFO_VERSION = 4 */ rubidium@5469: { rubidium@5469: /* Only send the GRF Identification (GRF_ID and MD5 checksum) of rubidium@5469: * the GRFs that are needed, i.e. the ones that the server has rubidium@5469: * selected in the NewGRF GUI and not the ones that are used due rubidium@5469: * to the fact that they are in [newgrf-static] in openttd.cfg */ rubidium@5469: const GRFConfig *c; rubidium@5469: uint count = 0; rubidium@5469: rubidium@5469: /* Count number of GRFs to send information about */ rubidium@5469: for (c = info->grfconfig; c != NULL; c = c->next) { rubidium@5469: if (!HASBIT(c->flags, GCF_STATIC)) count++; rubidium@5469: } rubidium@5469: NetworkSend_uint8 (p, count); // Send number of GRFs rubidium@5469: rubidium@5469: /* Send actual GRF Identifications */ rubidium@5469: for (c = info->grfconfig; c != NULL; c = c->next) { rubidium@5619: if (!HASBIT(c->flags, GCF_STATIC)) this->Send_GRFIdentifier(p, c); rubidium@5469: } rubidium@5469: } rubidium@5469: rubidium@5469: /* NETWORK_GAME_INFO_VERSION = 3 */ rubidium@5469: NetworkSend_uint32(p, info->game_date); rubidium@5469: NetworkSend_uint32(p, info->start_date); rubidium@5469: rubidium@5469: /* NETWORK_GAME_INFO_VERSION = 2 */ rubidium@5469: NetworkSend_uint8 (p, info->companies_max); rubidium@5469: NetworkSend_uint8 (p, info->companies_on); rubidium@5469: NetworkSend_uint8 (p, info->spectators_max); rubidium@5469: rubidium@5469: /* NETWORK_GAME_INFO_VERSION = 1 */ rubidium@5469: NetworkSend_string(p, info->server_name); rubidium@5469: NetworkSend_string(p, info->server_revision); rubidium@5469: NetworkSend_uint8 (p, info->server_lang); rubidium@5469: NetworkSend_uint8 (p, info->use_password); rubidium@5469: NetworkSend_uint8 (p, info->clients_max); rubidium@5469: NetworkSend_uint8 (p, info->clients_on); rubidium@5469: NetworkSend_uint8 (p, info->spectators_on); rubidium@5469: NetworkSend_string(p, info->map_name); rubidium@5469: NetworkSend_uint16(p, info->map_width); rubidium@5469: NetworkSend_uint16(p, info->map_height); rubidium@5469: NetworkSend_uint8 (p, info->map_set); rubidium@5469: NetworkSend_uint8 (p, info->dedicated); rubidium@5469: } rubidium@5469: rubidium@5469: /** rubidium@5469: * Deserializes the NetworkGameInfo struct from the packet rubidium@5469: * @param p the packet to read the data from rubidium@5469: * @param info the NetworkGameInfo to deserialize into rubidium@5469: */ rubidium@5619: void NetworkUDPSocketHandler::Recv_NetworkGameInfo(Packet *p, NetworkGameInfo *info) rubidium@5469: { rubidium@5799: static const Date MAX_DATE = ConvertYMDToDate(MAX_YEAR, 11, 31); // December is month 11 rubidium@5799: rubidium@5624: info->game_info_version = NetworkRecv_uint8(this, p); rubidium@5469: rubidium@5469: /* rubidium@5469: * Please observe the order. rubidium@5469: * The parts must be read in the same order as they are sent! rubidium@5469: */ rubidium@5469: rubidium@5545: /* Update the documentation in udp.h on changes rubidium@5545: * to the NetworkGameInfo wire-protocol! */ rubidium@5545: rubidium@5469: switch (info->game_info_version) { rubidium@5469: case 4: { KUDr@5609: GRFConfig **dst = &info->grfconfig; rubidium@5469: uint i; rubidium@5624: uint num_grfs = NetworkRecv_uint8(this, p); rubidium@5469: rubidium@5469: for (i = 0; i < num_grfs; i++) { KUDr@5609: GRFConfig *c = CallocT(1); rubidium@5619: this->Recv_GRFIdentifier(p, c); rubidium@5619: this->HandleIncomingNetworkGameInfoGRFConfig(c); rubidium@5469: rubidium@5469: /* Append GRFConfig to the list */ rubidium@5469: *dst = c; rubidium@5469: dst = &c->next; rubidium@5469: } rubidium@5469: } /* Fallthrough */ rubidium@5469: case 3: rubidium@5799: info->game_date = clamp(NetworkRecv_uint32(this, p), 0, MAX_DATE); rubidium@5799: info->start_date = clamp(NetworkRecv_uint32(this, p), 0, MAX_DATE); rubidium@5469: /* Fallthrough */ rubidium@5469: case 2: rubidium@5624: info->companies_max = NetworkRecv_uint8 (this, p); rubidium@5624: info->companies_on = NetworkRecv_uint8 (this, p); rubidium@5624: info->spectators_max = NetworkRecv_uint8 (this, p); rubidium@5469: /* Fallthrough */ rubidium@5469: case 1: rubidium@5624: NetworkRecv_string(this, p, info->server_name, sizeof(info->server_name)); rubidium@5624: NetworkRecv_string(this, p, info->server_revision, sizeof(info->server_revision)); rubidium@5624: info->server_lang = NetworkRecv_uint8 (this, p); rubidium@5799: info->use_password = (NetworkRecv_uint8 (this, p) != 0); rubidium@5624: info->clients_max = NetworkRecv_uint8 (this, p); rubidium@5624: info->clients_on = NetworkRecv_uint8 (this, p); rubidium@5624: info->spectators_on = NetworkRecv_uint8 (this, p); rubidium@5469: if (info->game_info_version < 3) { // 16 bits dates got scrapped and are read earlier rubidium@5624: info->game_date = NetworkRecv_uint16(this, p) + DAYS_TILL_ORIGINAL_BASE_YEAR; rubidium@5624: info->start_date = NetworkRecv_uint16(this, p) + DAYS_TILL_ORIGINAL_BASE_YEAR; rubidium@5469: } rubidium@5624: NetworkRecv_string(this, p, info->map_name, sizeof(info->map_name)); rubidium@5624: info->map_width = NetworkRecv_uint16(this, p); rubidium@5624: info->map_height = NetworkRecv_uint16(this, p); rubidium@5624: info->map_set = NetworkRecv_uint8 (this, p); rubidium@5624: info->dedicated = (NetworkRecv_uint8(this, p) != 0); rubidium@5799: rubidium@5810: if (info->server_lang >= NETWORK_NUM_LANGUAGES) info->server_lang = 0; rubidium@5810: if (info->map_set >= NETWORK_NUM_LANDSCAPES) info->map_set = 0; rubidium@5469: } rubidium@5469: } rubidium@5469: rubidium@5864: /** rubidium@5864: * Defines a simple (switch) case for each network packet rubidium@5864: * @param type the packet type to create the case for rubidium@5864: */ rubidium@5619: #define UDP_COMMAND(type) case type: this->NetworkPacketReceive_ ## type ## _command(p, client_addr); break; rubidium@5619: rubidium@5619: /** rubidium@5619: * Handle an incoming packets by sending it to the correct function. rubidium@5619: * @param p the received packet rubidium@5619: * @param client_addr the sender of the packet rubidium@5619: */ rubidium@5619: void NetworkUDPSocketHandler::HandleUDPPacket(Packet *p, const struct sockaddr_in *client_addr) rubidium@5619: { rubidium@5619: PacketUDPType type; rubidium@5619: rubidium@5624: /* New packet == new client, which has not quit yet */ rubidium@5624: this->has_quit = false; rubidium@5619: rubidium@5624: type = (PacketUDPType)NetworkRecv_uint8(this, p); rubidium@5619: rubidium@5624: switch (this->HasClientQuit() ? PACKET_UDP_END : type) { rubidium@5619: UDP_COMMAND(PACKET_UDP_CLIENT_FIND_SERVER); rubidium@5619: UDP_COMMAND(PACKET_UDP_SERVER_RESPONSE); rubidium@5619: UDP_COMMAND(PACKET_UDP_CLIENT_DETAIL_INFO); rubidium@5619: UDP_COMMAND(PACKET_UDP_SERVER_DETAIL_INFO); rubidium@5619: UDP_COMMAND(PACKET_UDP_SERVER_REGISTER); rubidium@5619: UDP_COMMAND(PACKET_UDP_MASTER_ACK_REGISTER); rubidium@5619: UDP_COMMAND(PACKET_UDP_CLIENT_GET_LIST); rubidium@5619: UDP_COMMAND(PACKET_UDP_MASTER_RESPONSE_LIST); rubidium@5619: UDP_COMMAND(PACKET_UDP_SERVER_UNREGISTER); rubidium@5619: UDP_COMMAND(PACKET_UDP_CLIENT_GET_NEWGRFS); rubidium@5619: UDP_COMMAND(PACKET_UDP_SERVER_NEWGRFS); rubidium@5619: rubidium@5619: default: rubidium@5624: if (this->HasClientQuit()) { rubidium@5619: DEBUG(net, 0, "[udp] received invalid packet type %d from %s:%d", type, inet_ntoa(client_addr->sin_addr), ntohs(client_addr->sin_port)); rubidium@5619: } else { rubidium@5619: DEBUG(net, 0, "[udp] received illegal packet from %s:%d", inet_ntoa(client_addr->sin_addr), ntohs(client_addr->sin_port)); rubidium@5619: } rubidium@5619: break; rubidium@5619: } rubidium@5619: } rubidium@5619: rubidium@5864: /** rubidium@5619: * Create stub implementations for all receive commands that only rubidium@5619: * show a warning that the given command is not available for the rubidium@5619: * socket where the packet came from. rubidium@5864: * @param type the packet type to create the stub for rubidium@5619: */ rubidium@5619: #define DEFINE_UNAVAILABLE_UDP_RECEIVE_COMMAND(type) \ rubidium@5619: void NetworkUDPSocketHandler::NetworkPacketReceive_## type ##_command(\ rubidium@5619: Packet *p, const struct sockaddr_in *client_addr) { \ rubidium@5624: DEBUG(net, 0, "[udp] received packet type %d on wrong port from %s:%d", \ rubidium@5624: type, inet_ntoa(client_addr->sin_addr), ntohs(client_addr->sin_port)); \ rubidium@5619: } rubidium@5619: rubidium@5619: DEFINE_UNAVAILABLE_UDP_RECEIVE_COMMAND(PACKET_UDP_CLIENT_FIND_SERVER); rubidium@5619: DEFINE_UNAVAILABLE_UDP_RECEIVE_COMMAND(PACKET_UDP_SERVER_RESPONSE); rubidium@5619: DEFINE_UNAVAILABLE_UDP_RECEIVE_COMMAND(PACKET_UDP_CLIENT_DETAIL_INFO); rubidium@5619: DEFINE_UNAVAILABLE_UDP_RECEIVE_COMMAND(PACKET_UDP_SERVER_DETAIL_INFO); rubidium@5619: DEFINE_UNAVAILABLE_UDP_RECEIVE_COMMAND(PACKET_UDP_SERVER_REGISTER); rubidium@5619: DEFINE_UNAVAILABLE_UDP_RECEIVE_COMMAND(PACKET_UDP_MASTER_ACK_REGISTER); rubidium@5619: DEFINE_UNAVAILABLE_UDP_RECEIVE_COMMAND(PACKET_UDP_CLIENT_GET_LIST); rubidium@5619: DEFINE_UNAVAILABLE_UDP_RECEIVE_COMMAND(PACKET_UDP_MASTER_RESPONSE_LIST); rubidium@5619: DEFINE_UNAVAILABLE_UDP_RECEIVE_COMMAND(PACKET_UDP_SERVER_UNREGISTER); rubidium@5619: DEFINE_UNAVAILABLE_UDP_RECEIVE_COMMAND(PACKET_UDP_CLIENT_GET_NEWGRFS); rubidium@5619: DEFINE_UNAVAILABLE_UDP_RECEIVE_COMMAND(PACKET_UDP_SERVER_NEWGRFS); rubidium@5619: rubidium@5469: #endif /* ENABLE_NETWORK */