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@5469: * @file udp.c 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 udp the place where the (references to the) UDP are stored 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@5528: bool NetworkUDPListen(SOCKET *udp, 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@5531: NetworkUDPClose(udp); rubidium@5469: rubidium@5469: *udp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); rubidium@5469: if (*udp == INVALID_SOCKET) { 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@5469: ioctlsocket(*udp, FIONBIO, &blocking); rubidium@5469: #else rubidium@5469: setsockopt(*udp, 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@5469: if (bind(*udp, (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@5469: setsockopt(*udp, 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: * @param udp the socket to close rubidium@5521: */ rubidium@5521: void NetworkUDPClose(SOCKET *udp) rubidium@5521: { rubidium@5521: if (*udp == INVALID_SOCKET) return; rubidium@5521: rubidium@5521: closesocket(*udp); rubidium@5521: *udp = INVALID_SOCKET; rubidium@5521: } rubidium@5521: rubidium@5521: rubidium@5521: /** rubidium@5521: * Send a packet over UDP rubidium@5521: * @param udp the socket to send over rubidium@5521: * @param p the packet to send rubidium@5521: * @param recv the receiver (target) of the packet rubidium@5521: */ rubidium@5528: void NetworkSendUDP_Packet(const SOCKET udp, 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@5587: res = sendto(udp, (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: * @param udp the socket to receive the packet on rubidium@5469: */ rubidium@5528: void NetworkUDPReceive(const SOCKET udp) 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@5469: packet_len = sizeof(p.buffer); rubidium@5469: client_len = sizeof(client_addr); rubidium@5469: rubidium@5469: /* Try to receive anything */ rubidium@5587: nbytes = recvfrom(udp, (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@5519: NetworkHandleUDPPacket(udp, &p, &client_addr); rubidium@5469: } rubidium@5469: } rubidium@5469: rubidium@5469: rubidium@5469: /** rubidium@5469: * Serializes the GRFIdentifier (GRF ID and MD5 checksum) to the packet rubidium@5469: * @param p the packet to write the data to rubidium@5469: * @param c the configuration to write the GRF ID and MD5 checksum from rubidium@5469: */ rubidium@5469: void NetworkSend_GRFIdentifier(Packet *p, const GRFConfig *c) rubidium@5469: { rubidium@5469: uint j; rubidium@5469: NetworkSend_uint32(p, c->grfid); rubidium@5469: for (j = 0; j < sizeof(c->md5sum); j++) { rubidium@5469: NetworkSend_uint8 (p, c->md5sum[j]); rubidium@5469: } rubidium@5469: } rubidium@5469: rubidium@5469: /** rubidium@5469: * Deserializes the GRFIdentifier (GRF ID and MD5 checksum) from the packet rubidium@5469: * @param cs the client state (for closing connect on out-of-bounds reading etc) rubidium@5469: * @param p the packet to read the data from rubidium@5469: * @param c the configuration to write the GRF ID and MD5 checksum to rubidium@5469: */ rubidium@5469: void NetworkRecv_GRFIdentifier(NetworkClientState *cs, Packet *p, GRFConfig *c) rubidium@5469: { rubidium@5469: uint j; rubidium@5469: c->grfid = NetworkRecv_uint32(cs, p); rubidium@5469: for (j = 0; j < sizeof(c->md5sum); j++) { rubidium@5469: c->md5sum[j] = NetworkRecv_uint8(cs, p); 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@5469: void NetworkSend_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@5469: if (!HASBIT(c->flags, GCF_STATIC)) NetworkSend_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 cs the client state (for closing connect on out-of-bounds reading etc) rubidium@5469: * @param p the packet to read the data from rubidium@5469: * @param info the NetworkGameInfo to deserialize into rubidium@5469: */ rubidium@5469: void NetworkRecv_NetworkGameInfo(NetworkClientState *cs, Packet *p, NetworkGameInfo *info) rubidium@5469: { rubidium@5469: info->game_info_version = NetworkRecv_uint8(cs, 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: { rubidium@5469: GRFConfig *c, **dst = &info->grfconfig; rubidium@5469: uint i; rubidium@5469: uint num_grfs = NetworkRecv_uint8(cs, p); rubidium@5469: rubidium@5469: for (i = 0; i < num_grfs; i++) { rubidium@5587: CallocT(&c, 1); rubidium@5469: NetworkRecv_GRFIdentifier(cs, p, c); rubidium@5469: 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@5469: info->game_date = NetworkRecv_uint32(cs, p); rubidium@5469: info->start_date = NetworkRecv_uint32(cs, p); rubidium@5469: /* Fallthrough */ rubidium@5469: case 2: rubidium@5469: info->companies_max = NetworkRecv_uint8 (cs, p); rubidium@5469: info->companies_on = NetworkRecv_uint8 (cs, p); rubidium@5469: info->spectators_max = NetworkRecv_uint8 (cs, p); rubidium@5469: /* Fallthrough */ rubidium@5469: case 1: rubidium@5469: NetworkRecv_string(cs, p, info->server_name, sizeof(info->server_name)); rubidium@5469: NetworkRecv_string(cs, p, info->server_revision, sizeof(info->server_revision)); rubidium@5469: info->server_lang = NetworkRecv_uint8 (cs, p); rubidium@5469: info->use_password = NetworkRecv_uint8 (cs, p); rubidium@5469: info->clients_max = NetworkRecv_uint8 (cs, p); rubidium@5469: info->clients_on = NetworkRecv_uint8 (cs, p); rubidium@5469: info->spectators_on = NetworkRecv_uint8 (cs, p); rubidium@5469: if (info->game_info_version < 3) { // 16 bits dates got scrapped and are read earlier rubidium@5469: info->game_date = NetworkRecv_uint16(cs, p) + DAYS_TILL_ORIGINAL_BASE_YEAR; rubidium@5469: info->start_date = NetworkRecv_uint16(cs, p) + DAYS_TILL_ORIGINAL_BASE_YEAR; rubidium@5469: } rubidium@5469: NetworkRecv_string(cs, p, info->map_name, sizeof(info->map_name)); rubidium@5469: info->map_width = NetworkRecv_uint16(cs, p); rubidium@5469: info->map_height = NetworkRecv_uint16(cs, p); rubidium@5469: info->map_set = NetworkRecv_uint8 (cs, p); rubidium@5587: info->dedicated = (NetworkRecv_uint8 (cs, p) != 0); rubidium@5469: } rubidium@5469: } rubidium@5469: rubidium@5469: #endif /* ENABLE_NETWORK */