src/network/core/udp.cpp
changeset 5835 e0ff603ae0b7
parent 5823 7cd91e6e5c71
child 5838 9c3129cb019b
equal deleted inserted replaced
5834:7bf92d5a5a0f 5835:e0ff603ae0b7
       
     1 /* $Id$ */
       
     2 
       
     3 #ifdef ENABLE_NETWORK
       
     4 
       
     5 #include "../../stdafx.h"
       
     6 #include "../../debug.h"
       
     7 #include "../../macros.h"
       
     8 #include "packet.h"
       
     9 #include "udp.h"
       
    10 
       
    11 /**
       
    12  * @file udp.c Basic functions to receive and send UDP packets.
       
    13  */
       
    14 
       
    15 /**
       
    16  * Start listening on the given host and port.
       
    17  * @param udp       the place where the (references to the) UDP are stored
       
    18  * @param host      the host (ip) to listen on
       
    19  * @param port      the port to listen on
       
    20  * @param broadcast whether to allow broadcast sending/receiving
       
    21  * @return true if the listening succeeded
       
    22  */
       
    23 bool NetworkUDPListen(SOCKET *udp, const uint32 host, const uint16 port, const bool broadcast)
       
    24 {
       
    25 	struct sockaddr_in sin;
       
    26 
       
    27 	/* Make sure socket is closed */
       
    28 	NetworkUDPClose(udp);
       
    29 
       
    30 	*udp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
       
    31 	if (*udp == INVALID_SOCKET) {
       
    32 		DEBUG(net, 0, "[udp] failed to start UDP listener");
       
    33 		return false;
       
    34 	}
       
    35 
       
    36 	/* set nonblocking mode for socket */
       
    37 	{
       
    38 		unsigned long blocking = 1;
       
    39 #ifndef BEOS_NET_SERVER
       
    40 		ioctlsocket(*udp, FIONBIO, &blocking);
       
    41 #else
       
    42 		setsockopt(*udp, SOL_SOCKET, SO_NONBLOCK, &blocking, NULL);
       
    43 #endif
       
    44 	}
       
    45 
       
    46 	sin.sin_family = AF_INET;
       
    47 	/* Listen on all IPs */
       
    48 	sin.sin_addr.s_addr = host;
       
    49 	sin.sin_port = htons(port);
       
    50 
       
    51 	if (bind(*udp, (struct sockaddr*)&sin, sizeof(sin)) != 0) {
       
    52 		DEBUG(net, 0, "[udp] bind failed on %s:%i", inet_ntoa(*(struct in_addr *)&host), port);
       
    53 		return false;
       
    54 	}
       
    55 
       
    56 	if (broadcast) {
       
    57 		/* Enable broadcast */
       
    58 		unsigned long val = 1;
       
    59 #ifndef BEOS_NET_SERVER // will work around this, some day; maybe.
       
    60 		setsockopt(*udp, SOL_SOCKET, SO_BROADCAST, (char *) &val , sizeof(val));
       
    61 #endif
       
    62 	}
       
    63 
       
    64 	DEBUG(net, 1, "[udp] listening on port %s:%d", inet_ntoa(*(struct in_addr *)&host), port);
       
    65 
       
    66 	return true;
       
    67 }
       
    68 
       
    69 /**
       
    70  * Close the given UDP socket
       
    71  * @param udp the socket to close
       
    72  */
       
    73 void NetworkUDPClose(SOCKET *udp)
       
    74 {
       
    75 	if (*udp == INVALID_SOCKET) return;
       
    76 
       
    77 	closesocket(*udp);
       
    78 	*udp = INVALID_SOCKET;
       
    79 }
       
    80 
       
    81 
       
    82 /**
       
    83  * Send a packet over UDP
       
    84  * @param udp  the socket to send over
       
    85  * @param p    the packet to send
       
    86  * @param recv the receiver (target) of the packet
       
    87  */
       
    88 void NetworkSendUDP_Packet(const SOCKET udp, Packet *p, const struct sockaddr_in *recv)
       
    89 {
       
    90 	int res;
       
    91 
       
    92 	NetworkSend_FillPacketSize(p);
       
    93 
       
    94 	/* Send the buffer */
       
    95 	res = sendto(udp, p->buffer, p->size, 0, (struct sockaddr *)recv, sizeof(*recv));
       
    96 
       
    97 	/* Check for any errors, but ignore it otherwise */
       
    98 	if (res == -1) DEBUG(net, 1, "[udp] sendto failed with: %i", GET_LAST_ERROR());
       
    99 }
       
   100 
       
   101 /**
       
   102  * Receive a packet at UDP level
       
   103  * @param udp the socket to receive the packet on
       
   104  */
       
   105 void NetworkUDPReceive(const SOCKET udp)
       
   106 {
       
   107 	struct sockaddr_in client_addr;
       
   108 	socklen_t client_len;
       
   109 	int nbytes;
       
   110 	Packet p;
       
   111 	int packet_len;
       
   112 
       
   113 	packet_len = sizeof(p.buffer);
       
   114 	client_len = sizeof(client_addr);
       
   115 
       
   116 	/* Try to receive anything */
       
   117 	nbytes = recvfrom(udp, p.buffer, packet_len, 0, (struct sockaddr *)&client_addr, &client_len);
       
   118 
       
   119 	/* We got some bytes for the base header of the packet. */
       
   120 	if (nbytes > 2) {
       
   121 		NetworkRecv_ReadPacketSize(&p);
       
   122 
       
   123 		/* If the size does not match the packet must be corrupted.
       
   124 		 * Otherwise it will be marked as corrupted later on. */
       
   125 		if (nbytes != p.size) {
       
   126 			DEBUG(net, 1, "received a packet with mismatching size from %s:%d",
       
   127 					inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
       
   128 
       
   129 			return;
       
   130 		}
       
   131 
       
   132 		/* Put the position on the right place */
       
   133 		p.pos = 2;
       
   134 		p.next = NULL;
       
   135 
       
   136 		/* Handle the packet */
       
   137 		NetworkHandleUDPPacket(udp, &p, &client_addr);
       
   138 	}
       
   139 }
       
   140 
       
   141 
       
   142 /**
       
   143  * Serializes the GRFIdentifier (GRF ID and MD5 checksum) to the packet
       
   144  * @param p the packet to write the data to
       
   145  * @param c the configuration to write the GRF ID and MD5 checksum from
       
   146  */
       
   147 void NetworkSend_GRFIdentifier(Packet *p, const GRFConfig *c)
       
   148 {
       
   149 	uint j;
       
   150 	NetworkSend_uint32(p, c->grfid);
       
   151 	for (j = 0; j < sizeof(c->md5sum); j++) {
       
   152 		NetworkSend_uint8 (p, c->md5sum[j]);
       
   153 	}
       
   154 }
       
   155 
       
   156 /**
       
   157  * Deserializes the GRFIdentifier (GRF ID and MD5 checksum) from the packet
       
   158  * @param cs the client state (for closing connect on out-of-bounds reading etc)
       
   159  * @param p  the packet to read the data from
       
   160  * @param c  the configuration to write the GRF ID and MD5 checksum to
       
   161  */
       
   162 void NetworkRecv_GRFIdentifier(NetworkClientState *cs, Packet *p, GRFConfig *c)
       
   163 {
       
   164 	uint j;
       
   165 	c->grfid = NetworkRecv_uint32(cs, p);
       
   166 	for (j = 0; j < sizeof(c->md5sum); j++) {
       
   167 		c->md5sum[j] = NetworkRecv_uint8(cs, p);
       
   168 	}
       
   169 }
       
   170 
       
   171 
       
   172 /**
       
   173  * Serializes the NetworkGameInfo struct to the packet
       
   174  * @param p    the packet to write the data to
       
   175  * @param info the NetworkGameInfo struct to serialize
       
   176  */
       
   177 void NetworkSend_NetworkGameInfo(Packet *p, const NetworkGameInfo *info)
       
   178 {
       
   179 	NetworkSend_uint8 (p, NETWORK_GAME_INFO_VERSION);
       
   180 
       
   181 	/*
       
   182 	 *              Please observe the order.
       
   183 	 * The parts must be read in the same order as they are sent!
       
   184 	 */
       
   185 
       
   186 	/* Update the documentation in udp.h on changes
       
   187 	 * to the NetworkGameInfo wire-protocol! */
       
   188 
       
   189 	/* NETWORK_GAME_INFO_VERSION = 4 */
       
   190 	{
       
   191 		/* Only send the GRF Identification (GRF_ID and MD5 checksum) of
       
   192 		 * the GRFs that are needed, i.e. the ones that the server has
       
   193 		 * selected in the NewGRF GUI and not the ones that are used due
       
   194 		 * to the fact that they are in [newgrf-static] in openttd.cfg */
       
   195 		const GRFConfig *c;
       
   196 		uint count = 0;
       
   197 
       
   198 		/* Count number of GRFs to send information about */
       
   199 		for (c = info->grfconfig; c != NULL; c = c->next) {
       
   200 			if (!HASBIT(c->flags, GCF_STATIC)) count++;
       
   201 		}
       
   202 		NetworkSend_uint8 (p, count); // Send number of GRFs
       
   203 
       
   204 		/* Send actual GRF Identifications */
       
   205 		for (c = info->grfconfig; c != NULL; c = c->next) {
       
   206 			if (!HASBIT(c->flags, GCF_STATIC)) NetworkSend_GRFIdentifier(p, c);
       
   207 		}
       
   208 	}
       
   209 
       
   210 	/* NETWORK_GAME_INFO_VERSION = 3 */
       
   211 	NetworkSend_uint32(p, info->game_date);
       
   212 	NetworkSend_uint32(p, info->start_date);
       
   213 
       
   214 	/* NETWORK_GAME_INFO_VERSION = 2 */
       
   215 	NetworkSend_uint8 (p, info->companies_max);
       
   216 	NetworkSend_uint8 (p, info->companies_on);
       
   217 	NetworkSend_uint8 (p, info->spectators_max);
       
   218 
       
   219 	/* NETWORK_GAME_INFO_VERSION = 1 */
       
   220 	NetworkSend_string(p, info->server_name);
       
   221 	NetworkSend_string(p, info->server_revision);
       
   222 	NetworkSend_uint8 (p, info->server_lang);
       
   223 	NetworkSend_uint8 (p, info->use_password);
       
   224 	NetworkSend_uint8 (p, info->clients_max);
       
   225 	NetworkSend_uint8 (p, info->clients_on);
       
   226 	NetworkSend_uint8 (p, info->spectators_on);
       
   227 	NetworkSend_string(p, info->map_name);
       
   228 	NetworkSend_uint16(p, info->map_width);
       
   229 	NetworkSend_uint16(p, info->map_height);
       
   230 	NetworkSend_uint8 (p, info->map_set);
       
   231 	NetworkSend_uint8 (p, info->dedicated);
       
   232 }
       
   233 
       
   234 /**
       
   235  * Deserializes the NetworkGameInfo struct from the packet
       
   236  * @param cs   the client state (for closing connect on out-of-bounds reading etc)
       
   237  * @param p    the packet to read the data from
       
   238  * @param info the NetworkGameInfo to deserialize into
       
   239  */
       
   240 void NetworkRecv_NetworkGameInfo(NetworkClientState *cs, Packet *p, NetworkGameInfo *info)
       
   241 {
       
   242 	info->game_info_version = NetworkRecv_uint8(cs, p);
       
   243 
       
   244 	/*
       
   245 	 *              Please observe the order.
       
   246 	 * The parts must be read in the same order as they are sent!
       
   247 	 */
       
   248 
       
   249 	/* Update the documentation in udp.h on changes
       
   250 	 * to the NetworkGameInfo wire-protocol! */
       
   251 
       
   252 	switch (info->game_info_version) {
       
   253 		case 4: {
       
   254 			GRFConfig *c, **dst = &info->grfconfig;
       
   255 			uint i;
       
   256 			uint num_grfs = NetworkRecv_uint8(cs, p);
       
   257 
       
   258 			for (i = 0; i < num_grfs; i++) {
       
   259 				c = calloc(1, sizeof(*c));
       
   260 				NetworkRecv_GRFIdentifier(cs, p, c);
       
   261 				HandleIncomingNetworkGameInfoGRFConfig(c);
       
   262 
       
   263 				/* Append GRFConfig to the list */
       
   264 				*dst = c;
       
   265 				dst = &c->next;
       
   266 			}
       
   267 		} /* Fallthrough */
       
   268 		case 3:
       
   269 			info->game_date      = NetworkRecv_uint32(cs, p);
       
   270 			info->start_date     = NetworkRecv_uint32(cs, p);
       
   271 			/* Fallthrough */
       
   272 		case 2:
       
   273 			info->companies_max  = NetworkRecv_uint8 (cs, p);
       
   274 			info->companies_on   = NetworkRecv_uint8 (cs, p);
       
   275 			info->spectators_max = NetworkRecv_uint8 (cs, p);
       
   276 			/* Fallthrough */
       
   277 		case 1:
       
   278 			NetworkRecv_string(cs, p, info->server_name,     sizeof(info->server_name));
       
   279 			NetworkRecv_string(cs, p, info->server_revision, sizeof(info->server_revision));
       
   280 			info->server_lang    = NetworkRecv_uint8 (cs, p);
       
   281 			info->use_password   = NetworkRecv_uint8 (cs, p);
       
   282 			info->clients_max    = NetworkRecv_uint8 (cs, p);
       
   283 			info->clients_on     = NetworkRecv_uint8 (cs, p);
       
   284 			info->spectators_on  = NetworkRecv_uint8 (cs, p);
       
   285 			if (info->game_info_version < 3) { // 16 bits dates got scrapped and are read earlier
       
   286 				info->game_date    = NetworkRecv_uint16(cs, p) + DAYS_TILL_ORIGINAL_BASE_YEAR;
       
   287 				info->start_date   = NetworkRecv_uint16(cs, p) + DAYS_TILL_ORIGINAL_BASE_YEAR;
       
   288 			}
       
   289 			NetworkRecv_string(cs, p, info->map_name, sizeof(info->map_name));
       
   290 			info->map_width      = NetworkRecv_uint16(cs, p);
       
   291 			info->map_height     = NetworkRecv_uint16(cs, p);
       
   292 			info->map_set        = NetworkRecv_uint8 (cs, p);
       
   293 			info->dedicated      = NetworkRecv_uint8 (cs, p);
       
   294 	}
       
   295 }
       
   296 
       
   297 #endif /* ENABLE_NETWORK */