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