IPXGCONN.CPP (22629B)
1 // 2 // Copyright 2020 Electronic Arts Inc. 3 // 4 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free 5 // software: you can redistribute it and/or modify it under the terms of 6 // the GNU General Public License as published by the Free Software Foundation, 7 // either version 3 of the License, or (at your option) any later version. 8 9 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed 10 // in the hope that it will be useful, but with permitted additional restrictions 11 // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 12 // distributed with this program. You should have received a copy of the 13 // GNU General Public License along with permitted additional restrictions 14 // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection 15 16 /* $Header: F:\projects\c&c\vcs\code\ipxgconn.cpv 1.9 16 Oct 1995 16:51:00 JOE_BOSTIC $ */ 17 /*************************************************************************** 18 ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** 19 *************************************************************************** 20 * * 21 * Project Name : Command & Conquer * 22 * * 23 * File Name : IPXGCONN.CPP * 24 * * 25 * Programmer : Bill Randolph * 26 * * 27 * Start Date : December 20, 1994 * 28 * * 29 * Last Update : July 6, 1995 [BRR] * 30 *-------------------------------------------------------------------------* 31 * Functions: * 32 * IPXGlobalConnClass::IPXGlobalConnClass -- class constructor * 33 * IPXGlobalConnClass::~IPXGlobalConnClass -- class destructor * 34 * IPXGlobalConnClass::Send_Packet -- adds a packet to the send queue * 35 * IPXGlobalConnClass::Receive_Packet -- adds packet to the receive queue* 36 * IPXGlobalConnClass::Get_Packet -- gets a packet from the receive queue* 37 * IPXGlobalConnClass::Send -- sends a packet * 38 * IPXGlobalConnClass::Service_Receive_Queue -- services recieve queue * 39 * IPXGlobalConnClass::Set_Bridge -- Sets up connection to cross a bridge* 40 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 41 42 #include "function.h" 43 44 45 /*************************************************************************** 46 * IPXGlobalConnClass::IPXGlobalConnClass -- class constructor * 47 * * 48 * This routine chains to the parent constructor, but it adjusts the size * 49 * of the packet by the added bytes in the GlobalHeaderType structure. * 50 * This forces the parent classes to allocate the proper sized PacketBuf * 51 * for outgoing packets, and to set MaxPacketLen to the proper value. * 52 * * 53 * INPUT: * 54 * numsend desired # of entries for the send queue * 55 * numreceive desired # of entries for the recieve queue * 56 * maxlen max length of an application packet * 57 * product_id unique ID for this product * 58 * * 59 * OUTPUT: * 60 * none. * 61 * * 62 * WARNINGS: * 63 * none. * 64 * * 65 * HISTORY: * 66 * 12/20/1994 BR : Created. * 67 *=========================================================================*/ 68 IPXGlobalConnClass::IPXGlobalConnClass (int numsend, int numreceive, int maxlen, 69 unsigned short product_id) : 70 IPXConnClass (numsend, numreceive, 71 maxlen + sizeof(GlobalHeaderType) - sizeof(CommHeaderType), 72 GLOBAL_MAGICNUM, // magic number for this connection 73 NULL, // IPX Address (none) 74 0, // Connection ID 75 "") // Connection Name 76 { 77 ProductID = product_id; 78 IsBridge = 0; 79 80 } /* end of IPXGlobalConnClass */ 81 82 83 /*************************************************************************** 84 * IPXGlobalConnClass::Send_Packet -- adds a packet to the send queue * 85 * * 86 * This routine prefixes the given buffer with a GlobalHeaderType and * 87 * queues the resulting packet into the Send Queue. The packet's * 88 * MagicNumber, Code, PacketID, destination Address and ProductID are set * 89 * here. * 90 * * 91 * INPUT: * 92 * buf buffer to send * 93 * buflen length of buffer * 94 * address address to send the packet to (NULL = Broadcast) * 95 * ack_req true = ACK is required for this packet; false = isn't * 96 * * 97 * OUTPUT: * 98 * 1 = OK, 0 = error * 99 * * 100 * WARNINGS: * 101 * none. * 102 * * 103 * HISTORY: * 104 * 12/20/1994 BR : Created. * 105 *=========================================================================*/ 106 int IPXGlobalConnClass::Send_Packet (void * buf, int buflen, 107 IPXAddressClass *address, int ack_req) 108 { 109 /*------------------------------------------------------------------------ 110 Store the packet's Magic Number 111 ------------------------------------------------------------------------*/ 112 ((GlobalHeaderType *)PacketBuf)->Header.MagicNumber = MagicNum; 113 114 /*------------------------------------------------------------------------ 115 If this is a ACK-required packet, sent to a specific system, mark it as 116 ACK-required; otherwise, mark as no-ACK-required. 117 ------------------------------------------------------------------------*/ 118 if (ack_req && address != NULL) { 119 ((GlobalHeaderType *)PacketBuf)->Header.Code = PACKET_DATA_ACK; 120 } else { 121 ((GlobalHeaderType *)PacketBuf)->Header.Code = PACKET_DATA_NOACK; 122 } 123 124 /*------------------------------------------------------------------------ 125 Fill in the packet ID. This will have very limited meaning; it only 126 allows us to determine if an ACK packet we receive later goes with this 127 packet; it doesn't let us detect re-sends of other systems' packets. 128 ------------------------------------------------------------------------*/ 129 ((GlobalHeaderType *)PacketBuf)->Header.PacketID = Queue->Send_Total(); 130 131 /*------------------------------------------------------------------------ 132 Set the product ID for this packet. 133 ------------------------------------------------------------------------*/ 134 ((GlobalHeaderType *)PacketBuf)->ProductID = ProductID; 135 136 /*------------------------------------------------------------------------ 137 Set this packet's destination address. If no address is specified, use 138 a Broadcast address (which IPXAddressClass's default constructor creates). 139 ------------------------------------------------------------------------*/ 140 if (address != NULL) { 141 ((GlobalHeaderType *)PacketBuf)->Address = (*address); 142 } else { 143 ((GlobalHeaderType *)PacketBuf)->Address = IPXAddressClass(); 144 } 145 146 /*------------------------------------------------------------------------ 147 Copy the application's data 148 ------------------------------------------------------------------------*/ 149 memcpy(PacketBuf + sizeof(GlobalHeaderType), buf, buflen); 150 151 /*------------------------------------------------------------------------ 152 Queue it 153 ------------------------------------------------------------------------*/ 154 return(Queue->Queue_Send(PacketBuf,buflen + sizeof(GlobalHeaderType))); 155 156 } /* end of Send_Packet */ 157 158 159 /*************************************************************************** 160 * IPXGlobalConnClass::Receive_Packet -- adds packet to the receive queue * 161 * * 162 * INPUT: * 163 * buf buffer to process (already includes GlobalHeaderType) * 164 * buflen length of buffer to process * 165 * address the address of the sender (the IPX Manager class must * 166 * extract this from the IPX Header of the received packet.) * 167 * * 168 * OUTPUT: * 169 * 1 = OK, 0 = error * 170 * * 171 * WARNINGS: * 172 * none. * 173 * * 174 * HISTORY: * 175 * 12/20/1994 BR : Created. * 176 *=========================================================================*/ 177 int IPXGlobalConnClass::Receive_Packet (void * buf, int buflen, 178 IPXAddressClass *address) 179 { 180 GlobalHeaderType *packet; // ptr to this packet 181 SendQueueType *send_entry; // ptr to send entry header 182 GlobalHeaderType *entry_data; // ptr to queue entry data 183 int i; 184 185 /* 186 --------------------------- Check the magic # ---------------------------- 187 */ 188 packet = (GlobalHeaderType *)buf; 189 if (packet->Header.MagicNumber!=MagicNum) { 190 return(false); 191 } 192 193 /*------------------------------------------------------------------------ 194 Process the packet based on its Code 195 ------------------------------------------------------------------------*/ 196 switch (packet->Header.Code) { 197 /*..................................................................... 198 DATA: Save the given address in the message buffer (so Get_Message() 199 can extract it later), and queue this message. 200 Don't bother checking for a Re-Send; since this queue is receiving data 201 from multiple systems, the Total_Receive() value for this queue will 202 have nothing to do with the packet's ID. The application must deal 203 with this by being able to handle multiple receipts of the same packet. 204 .....................................................................*/ 205 case PACKET_DATA_ACK: 206 case PACKET_DATA_NOACK: 207 packet->Address = (*address); 208 Queue->Queue_Receive (buf, buflen); 209 break; 210 211 /*..................................................................... 212 ACK: If this ACK is for any of my packets, mark that packet as 213 acknowledged, then throw this packet away. Otherwise, ignore the ACK 214 (if we re-sent before we received the other system's first ACK, this 215 ACK will be a leftover) 216 .....................................................................*/ 217 case PACKET_ACK: 218 for (i = 0; i < Queue->Num_Send(); i++) { 219 /* 220 ..................... Get queue entry ptr ....................... 221 */ 222 send_entry = Queue->Get_Send(i); 223 /* 224 ............. If ptr is valid, get ptr to its data .............. 225 */ 226 entry_data = (GlobalHeaderType *)(send_entry->Buffer); 227 /* 228 .............. If ACK is for this entry, mark it ................ 229 */ 230 if (packet->Header.PacketID==entry_data->Header.PacketID && 231 entry_data->Header.Code == PACKET_DATA_ACK) { 232 send_entry->IsACK = 1; 233 break; 234 } 235 } 236 break; 237 238 /*..................................................................... 239 Default: ignore the packet 240 .....................................................................*/ 241 default: 242 break; 243 244 } /* end of switch */ 245 246 return(true); 247 } 248 249 250 /*************************************************************************** 251 * IPXGlobalConnClass::Get_Packet -- gets a packet from the receive queue * 252 * * 253 * INPUT: * 254 * buf location to store buffer * 255 * buflen filled in with length of 'buf' * 256 * address filled in with sender's address * 257 * product_id filled in with sender's ProductID * 258 * * 259 * OUTPUT: * 260 * 1 = OK, 0 = error * 261 * * 262 * WARNINGS: * 263 * none. * 264 * * 265 * HISTORY: * 266 * 12/20/1994 BR : Created. * 267 *=========================================================================*/ 268 int IPXGlobalConnClass::Get_Packet (void * buf, int *buflen, 269 IPXAddressClass *address, unsigned short *product_id) 270 { 271 ReceiveQueueType *rec_entry; // ptr to receive entry header 272 GlobalHeaderType *packet; 273 int packetlen; // size of received packet 274 275 /* 276 ------------------------ Return if nothing to do ------------------------- 277 */ 278 if (Queue->Num_Receive() == 0) { 279 return(false); 280 } 281 282 /* 283 ------------------ Get ptr to the next available entry ------------------- 284 */ 285 rec_entry = Queue->Get_Receive(0); 286 287 /* 288 ------------------------ Read it if it's un-read ------------------------- 289 */ 290 if (rec_entry!=NULL && rec_entry->IsRead==0) { 291 /* 292 ........................... Mark as read .............................. 293 */ 294 rec_entry->IsRead = 1; 295 296 /* 297 .......................... Copy data packet ........................... 298 */ 299 packet = (GlobalHeaderType *)(rec_entry->Buffer); 300 packetlen = rec_entry->BufLen - sizeof(GlobalHeaderType); 301 if (packetlen > 0) 302 memcpy(buf, rec_entry->Buffer + sizeof(GlobalHeaderType), packetlen); 303 (*buflen) = packetlen; 304 (*address) = packet->Address; 305 (*product_id) = packet->ProductID; 306 307 return(true); 308 } 309 310 return(false); 311 } 312 313 314 /*************************************************************************** 315 * IPXGlobalConnClass::Send -- sends a packet * 316 * * 317 * This routine gets invoked by NonSequencedConn, when it's processing * 318 * the Send & Receive Queues. The buffer provided will already have the * 319 * GlobalHeaderType header embedded in it. * 320 * * 321 * INPUT: * 322 * buf buffer to send * 323 * buflen length of buffer * 324 * * 325 * OUTPUT: * 326 * 1 = OK, 0 = error * 327 * * 328 * WARNINGS: * 329 * none. * 330 * * 331 * HISTORY: * 332 * 12/20/1994 BR : Created. * 333 *=========================================================================*/ 334 int IPXGlobalConnClass::Send(char *buf, int buflen) 335 { 336 IPXAddressClass *addr; 337 int rc; 338 339 /*------------------------------------------------------------------------ 340 Extract the packet's embedded IPX address 341 ------------------------------------------------------------------------*/ 342 addr = &(((GlobalHeaderType *)buf)->Address); 343 344 /*------------------------------------------------------------------------ 345 If it's a broadcast address, broadcast it 346 ------------------------------------------------------------------------*/ 347 if (addr->Is_Broadcast()) { 348 return(Broadcast (buf, buflen)); 349 } else { 350 /*------------------------------------------------------------------------ 351 Otherwise, send it 352 ------------------------------------------------------------------------*/ 353 if (IsBridge && !memcmp (addr, BridgeNet, 4)) { 354 rc = Send_To (buf, buflen, &(((GlobalHeaderType *)buf)->Address), 355 BridgeNode); 356 } else { 357 rc = Send_To (buf, buflen, &(((GlobalHeaderType *)buf)->Address), NULL); 358 } 359 return (rc); 360 } 361 362 } /* end of Send */ 363 364 365 /*************************************************************************** 366 * IPXGlobalConnClass::Service_Receive_Queue -- services the recieve queue * 367 * * 368 * This routine is necessary because the Global Connection has to ACK * 369 * a packet differently from other types of connections; its Send routine * 370 * assumes that the destination address is embedded within the outgoing * 371 * packet, so we have to create our ACK Packet using the GlobalHeaderType, * 372 * not the CommHeaderType. * 373 * * 374 * INPUT: * 375 * none. * 376 * * 377 * OUTPUT: * 378 * 1 = OK, 0 = error * 379 * * 380 * WARNINGS: * 381 * none. * 382 * * 383 * HISTORY: * 384 * 12/20/1994 BR : Created. * 385 *=========================================================================*/ 386 int IPXGlobalConnClass::Service_Receive_Queue (void) 387 { 388 GlobalHeaderType ackpacket; // ACK packet to send 389 ReceiveQueueType *rec_entry; // ptr to receive entry header 390 GlobalHeaderType *packet_hdr; // packet header 391 392 /*------------------------------------------------------------------------ 393 Get a pointer to the next received entry 394 ------------------------------------------------------------------------*/ 395 rec_entry = Queue->Get_Receive(0); 396 if (rec_entry==NULL) 397 return(1); 398 399 /*------------------------------------------------------------------------ 400 If this packet doesn't require an ACK, mark it as ACK'd. 401 ------------------------------------------------------------------------*/ 402 packet_hdr = (GlobalHeaderType *)(rec_entry->Buffer); 403 if (packet_hdr->Header.Code==PACKET_DATA_NOACK) 404 rec_entry->IsACK = 1; 405 406 /*------------------------------------------------------------------------ 407 If this packet hasn't been ACK'd, send an ACK: 408 - Fill in the MagicNum & the Code 409 - Set the PacketID to the same ID that the sending system used, so the 410 sending system knows which packet the ACK is for 411 ------------------------------------------------------------------------*/ 412 if (rec_entry->IsACK==0) { 413 ackpacket.Header.MagicNumber = MagicNum; 414 ackpacket.Header.Code = PACKET_ACK; 415 ackpacket.Header.PacketID = packet_hdr->Header.PacketID; 416 ackpacket.Address = packet_hdr->Address; 417 ackpacket.ProductID = ProductID; 418 419 Send ((char *)&ackpacket, sizeof(GlobalHeaderType)); 420 421 rec_entry->IsACK = 1; 422 } 423 424 /*------------------------------------------------------------------------ 425 If this packet has been read by the application, and has been ACK'd, and 426 there is another packet in the queue behind this one, it means the other 427 system got the ACK we sent for this packet; remove this packet from the 428 queue. 429 ------------------------------------------------------------------------*/ 430 if (rec_entry!=NULL && rec_entry->IsRead && rec_entry->IsACK && 431 Queue->Num_Receive() > 1) 432 Queue->UnQueue_Receive(NULL,NULL,0); 433 434 return(1); 435 436 } /* end of Service_Receive_Queue */ 437 438 /*************************************************************************** 439 * Set_Bridge -- Sets up connection to cross a bridge * 440 * * 441 * This routine is designed to prevent the connection from having to * 442 * call Get_Local_Target, except the minimum number of times, since that * 443 * routine is buggy & goes away for long periods sometimes. * 444 * * 445 * INPUT: * 446 * bridge network number of the destination bridge * 447 * * 448 * OUTPUT: * 449 * none * 450 * * 451 * WARNINGS: * 452 * none * 453 * * 454 * HISTORY: * 455 * 07/06/1995 BRR : Created. * 456 *=========================================================================*/ 457 void IPXGlobalConnClass::Set_Bridge(NetNumType bridge) 458 { 459 if (Configured) { 460 memcpy (BridgeNet, bridge, 4); 461 memset (BridgeNode, 0xff, 6); 462 463 if (IPX_Get_Local_Target (BridgeNet, BridgeNode, Socket, BridgeNode)==0) { 464 IsBridge = 1; 465 } else { 466 IsBridge = 0; 467 } 468 } 469 } 470