rubidium@5720: /* $Id$ */ rubidium@5720: rubidium@5720: #ifdef ENABLE_NETWORK rubidium@5720: rubidium@5720: #include "../../stdafx.h" rubidium@5720: #include "../../debug.h" rubidium@5720: #include "../../macros.h" rubidium@5838: #include "../../helpers.hpp" rubidium@5720: #include "packet.h" rubidium@5720: #include "udp.h" rubidium@5720: rubidium@5720: /** rubidium@5720: * @file udp.c Basic functions to receive and send UDP packets. rubidium@5720: */ rubidium@5720: rubidium@5720: /** rubidium@5720: * Start listening on the given host and port. rubidium@5720: * @param udp the place where the (references to the) UDP are stored 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@5720: /* set nonblocking mode for socket */ rubidium@5720: { rubidium@5720: unsigned long blocking = 1; rubidium@5720: #ifndef BEOS_NET_SERVER rubidium@5875: ioctlsocket(this->sock, FIONBIO, &blocking); rubidium@5720: #else rubidium@5875: setsockopt(this->sock, SOL_SOCKET, SO_NONBLOCK, &blocking, NULL); rubidium@5720: #endif rubidium@5720: } 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: * @param udp the socket to close 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 udp the socket to send over 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@5772: NetworkSend_FillPacketSize(p); 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: * @param udp the socket to receive the packet on 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@5720: Packet p; 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@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@5720: NetworkRecv_ReadPacketSize(&p); 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: /* Put the position on the right place */ rubidium@5720: p.pos = 2; rubidium@5720: p.next = NULL; rubidium@5720: 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 GRFIdentifier (GRF ID and MD5 checksum) to the packet rubidium@6016: * @param p the packet to write the data to rubidium@6016: * @param grf the GRFIdentifier to serialize rubidium@5720: */ rubidium@6016: void NetworkUDPSocketHandler::Send_GRFIdentifier(Packet *p, const GRFIdentifier *grf) rubidium@5720: { rubidium@5720: uint j; rubidium@6016: NetworkSend_uint32(p, grf->grfid); rubidium@6016: for (j = 0; j < sizeof(grf->md5sum); j++) { rubidium@6016: NetworkSend_uint8 (p, grf->md5sum[j]); rubidium@5720: } rubidium@5720: } rubidium@5720: rubidium@5720: /** rubidium@5720: * Deserializes the GRFIdentifier (GRF ID and MD5 checksum) from the packet rubidium@6016: * @param p the packet to read the data from rubidium@6016: * @param grf the GRFIdentifier to deserialize rubidium@5720: */ rubidium@6016: void NetworkUDPSocketHandler::Recv_GRFIdentifier(Packet *p, GRFIdentifier *grf) rubidium@5720: { rubidium@5720: uint j; rubidium@6016: grf->grfid = NetworkRecv_uint32(this, p); rubidium@6016: for (j = 0; j < sizeof(grf->md5sum); j++) { rubidium@6016: grf->md5sum[j] = NetworkRecv_uint8(this, p); 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@5720: NetworkSend_uint8 (p, 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) { rubidium@5720: if (!HASBIT(c->flags, GCF_STATIC)) count++; rubidium@5720: } rubidium@5720: NetworkSend_uint8 (p, count); // Send number of GRFs rubidium@5720: rubidium@5720: /* Send actual GRF Identifications */ rubidium@5720: for (c = info->grfconfig; c != NULL; c = c->next) { rubidium@5870: 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@5720: NetworkSend_uint32(p, info->game_date); rubidium@5720: NetworkSend_uint32(p, info->start_date); rubidium@5720: rubidium@5720: /* NETWORK_GAME_INFO_VERSION = 2 */ rubidium@5720: NetworkSend_uint8 (p, info->companies_max); rubidium@5720: NetworkSend_uint8 (p, info->companies_on); rubidium@5720: NetworkSend_uint8 (p, info->spectators_max); rubidium@5720: rubidium@5720: /* NETWORK_GAME_INFO_VERSION = 1 */ rubidium@5720: NetworkSend_string(p, info->server_name); rubidium@5720: NetworkSend_string(p, info->server_revision); rubidium@5720: NetworkSend_uint8 (p, info->server_lang); rubidium@5720: NetworkSend_uint8 (p, info->use_password); rubidium@5720: NetworkSend_uint8 (p, info->clients_max); rubidium@5720: NetworkSend_uint8 (p, info->clients_on); rubidium@5720: NetworkSend_uint8 (p, info->spectators_on); rubidium@5720: NetworkSend_string(p, info->map_name); rubidium@5720: NetworkSend_uint16(p, info->map_width); rubidium@5720: NetworkSend_uint16(p, info->map_height); rubidium@5720: NetworkSend_uint8 (p, info->map_set); rubidium@5720: NetworkSend_uint8 (p, 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@5875: info->game_info_version = NetworkRecv_uint8(this, p); 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@5875: uint num_grfs = NetworkRecv_uint8(this, p); 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: rubidium@6050: info->game_date = clamp(NetworkRecv_uint32(this, p), 0, MAX_DATE); rubidium@6050: info->start_date = clamp(NetworkRecv_uint32(this, p), 0, MAX_DATE); rubidium@5720: /* Fallthrough */ rubidium@5720: case 2: rubidium@5875: info->companies_max = NetworkRecv_uint8 (this, p); rubidium@5875: info->companies_on = NetworkRecv_uint8 (this, p); rubidium@5875: info->spectators_max = NetworkRecv_uint8 (this, p); rubidium@5720: /* Fallthrough */ rubidium@5720: case 1: rubidium@5875: NetworkRecv_string(this, p, info->server_name, sizeof(info->server_name)); rubidium@5875: NetworkRecv_string(this, p, info->server_revision, sizeof(info->server_revision)); rubidium@5875: info->server_lang = NetworkRecv_uint8 (this, p); rubidium@6050: info->use_password = (NetworkRecv_uint8 (this, p) != 0); rubidium@5875: info->clients_max = NetworkRecv_uint8 (this, p); rubidium@5875: info->clients_on = NetworkRecv_uint8 (this, p); rubidium@5875: info->spectators_on = NetworkRecv_uint8 (this, p); rubidium@5720: if (info->game_info_version < 3) { // 16 bits dates got scrapped and are read earlier rubidium@5875: info->game_date = NetworkRecv_uint16(this, p) + DAYS_TILL_ORIGINAL_BASE_YEAR; rubidium@5875: info->start_date = NetworkRecv_uint16(this, p) + DAYS_TILL_ORIGINAL_BASE_YEAR; rubidium@5720: } rubidium@5875: NetworkRecv_string(this, p, info->map_name, sizeof(info->map_name)); rubidium@5875: info->map_width = NetworkRecv_uint16(this, p); rubidium@5875: info->map_height = NetworkRecv_uint16(this, p); rubidium@5875: info->map_set = NetworkRecv_uint8 (this, p); rubidium@5875: info->dedicated = (NetworkRecv_uint8(this, p) != 0); rubidium@6050: rubidium@6050: if (info->server_lang >= NETWORK_NUM_LANGUAGES) info->server_lang = 0; rubidium@6050: if (info->map_set >= NUM_LANDSCAPE) info->map_set = 0; rubidium@5720: } rubidium@5720: } rubidium@5720: rubidium@5870: /* Defines a simple (switch) case for each network packet */ 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@5875: type = (PacketUDPType)NetworkRecv_uint8(this, p); 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@5870: /* 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@5870: */ 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 */