IPX.CPP (53275B)
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\ipx.cpv 2.17 16 Oct 1995 16:49:34 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 : IPX.CPP * 24 * * 25 * Programmer : Barry Nance * 26 * from Client/Server LAN Programming * 27 * Westwood-ized by Bill Randolph * 28 * * 29 * Start Date : December 14, 1994 * 30 * * 31 * Last Update : December 15, 1994 [BR] * 32 * * 33 *-------------------------------------------------------------------------* 34 * Pitfalls: * 35 * - Never try to use a closed socket; always check the return code from * 36 * IPX_Open_Socket(). * 37 * - Always give IPX an outstanding ECB for listening, before you send. * 38 * - It turns out that IPX is pretty bad about saving registers, so if * 39 * you have any register variables in your program, they may get * 40 * trashed. To circumvent this, all functions in this module save & * 41 * restore the registers before invoking any IPX or NETX function. * 42 * * 43 *-------------------------------------------------------------------------* 44 * Functions: * 45 * IPX_SPX_Installed -- checks for installation of IPX/SPX * 46 * IPX_Open_Socket -- opens an IPX socket for sending or receiving * 47 * IPX_Close_Socket -- closes an open socket * 48 * IPX_Get_Connection_Number -- gets local Connection Number * 49 * IPX_Get_1st_Connection_Num -- gets 1st Connect Number for given user * 50 * IPX_Get_Internet_Address -- gets Network Number & Node Address * 51 * IPX_Get_User_ID -- gets user ID from Connection Number * 52 * IPX_Listen_For_Packet -- commands IPX to listen for a packet * 53 * IPX_Send_Packet -- commands IPX to send a packet * 54 * IPX_Get_Local_Target -- fills in ImmediateAddress field of ECB * 55 * IPX_Cancel_Event -- cancels an operation in progress * 56 * Let_IPX_Breath -- gives IPX some CPU time * 57 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 58 59 #include "function.h" 60 #include "ipx95.h" 61 62 63 /*************************************************************************** 64 * IPX_SPX_Installed -- checks for installation of IPX/SPX * 65 * * 66 * INPUT: * 67 * none. * 68 * * 69 * OUTPUT: * 70 * 0 = not installed; 1 = IPX only, 2 = IPX and SPX are installed * 71 * * 72 * WARNINGS: * 73 * none. * 74 * * 75 * HISTORY: * 76 * 12/14/1994 BR : Created. * 77 *=========================================================================*/ 78 int IPX_SPX_Installed(void) 79 { 80 81 #ifndef NOT_FOR_WIN95 82 83 return (IPX_Initialise()); 84 85 #else //NOT_FOR_WIN95 86 87 union REGS regs; 88 struct SREGS sregs; 89 RMIType rmi; 90 91 /*------------------------------------------------------------------------ 92 Init all registers to 0's 93 ------------------------------------------------------------------------*/ 94 memset (®s, 0 ,sizeof(regs)); 95 segread (&sregs); 96 memset (&rmi, 0 ,sizeof(rmi)); 97 98 /*------------------------------------------------------------------------ 99 Fill in registers for the DPMI call, function 0x300 100 ------------------------------------------------------------------------*/ 101 regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call 102 regs.w.bx = 0x002f; // interrupt # to invoke 103 sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is 104 regs.x.edi = FP_OFF(&rmi); 105 106 /*------------------------------------------------------------------------ 107 Fill in registers for the real-mode interrupt handler. 108 To test for the presence of IPX, set AH to 0x7a, AL to 0, and invoke 109 interrupt 0x2f (the "multiplex" interrupt). If IPX is installed, 110 AL will be 0xff, and ES:DI will contain the IPX/SPX function address. 111 ------------------------------------------------------------------------*/ 112 rmi.eax = 0x00007a00; 113 114 /*------------------------------------------------------------------------ 115 call DPMI 116 ------------------------------------------------------------------------*/ 117 int386x(DPMI_INT, ®s, ®s, &sregs); 118 119 /*------------------------------------------------------------------------ 120 If IPX isn't there, return 0 121 ------------------------------------------------------------------------*/ 122 if ( (rmi.eax & 0x00ff) != 0xff) { 123 return(0); 124 } 125 126 /*------------------------------------------------------------------------ 127 Test for SPX by invoking the IPX_SPX function with BX = 0x10, and AL = 0. 128 If SPX is present, AL will be 0xff. 129 ------------------------------------------------------------------------*/ 130 /*........................................................................ 131 Fill in registers for the DPMI call 132 ........................................................................*/ 133 memset (®s, 0 ,sizeof(regs)); 134 segread (&sregs); 135 memset (&rmi, 0 ,sizeof(rmi)); 136 regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call 137 regs.w.bx = IPX_INT; // interrupt # to invoke 138 sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is 139 regs.x.edi = FP_OFF(&rmi); 140 /*........................................................................ 141 Fill in registers for the interrupt call 142 ........................................................................*/ 143 rmi.ebx = 0x00000010; 144 /*........................................................................ 145 call DPMI 146 ........................................................................*/ 147 int386x(DPMI_INT, ®s, ®s, &sregs); 148 149 /*------------------------------------------------------------------------ 150 SPX is installed; return '2' 151 ------------------------------------------------------------------------*/ 152 if ( (rmi.eax & 0x00ff) == 0xff) { 153 return(2); 154 } 155 156 /*------------------------------------------------------------------------ 157 SPX is not installed; return '1' 158 ------------------------------------------------------------------------*/ 159 return(1); 160 #endif //NOT_FOR_WIN95 161 } /* end of IPX_SPX_Installed */ 162 163 164 /*************************************************************************** 165 * IPX_Open_Socket -- opens an IPX socket for sending or receiving * 166 * * 167 * INPUT: * 168 * socket the socket number to open * 169 * * 170 * OUTPUT: * 171 * 0 = OK * 172 * -1 = IPX not installed * 173 * 0xfe = socket table is full * 174 * 0xff = socket is already open * 175 * * 176 * WARNINGS: * 177 * The application must define its socket number carefully. Use * 178 * values from 0x4000 to 0x8000 for custom socket numbers. The app * 179 * must know its own socket number as well as the socket number of * 180 * a destination workstation. * 181 * * 182 * HISTORY: * 183 * 12/15/1994 BR : Created. * 184 *=========================================================================*/ 185 #ifdef NOT_FOR_WIN95 186 int IPX_Open_Socket(unsigned short socket) 187 { 188 union REGS regs; 189 struct SREGS sregs; 190 RMIType rmi; 191 int rc; 192 193 /*------------------------------------------------------------------------ 194 Open the socket: 195 DX = socket number 196 AL = 0 for short-lived socket, 0xff for long-lived socket 197 ------------------------------------------------------------------------*/ 198 /*........................................................................ 199 Fill in registers for the DPMI call 200 ........................................................................*/ 201 memset (®s, 0 ,sizeof(regs)); 202 segread (&sregs); 203 memset (&rmi, 0 ,sizeof(rmi)); 204 regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call 205 regs.w.bx = IPX_INT; // interrupt # to invoke 206 sregs.es = FP_SEG (&rmi); // tell DPMI where the RMI is 207 regs.x.edi = FP_OFF (&rmi); 208 /*........................................................................ 209 Fill in registers for the interrupt call 210 ........................................................................*/ 211 rmi.ebx = IPX_OPEN_SOCKET; // function code 212 rmi.edx = socket; // desired socket # 213 rmi.eax = 0x00ff; // make this a long-lived socket 214 /*........................................................................ 215 call DPMI 216 ........................................................................*/ 217 int386x (DPMI_INT, ®s, ®s, &sregs); 218 219 rc = (rmi.eax & 0xff); 220 221 return(rc); 222 223 } /* end of IPX_Open_Socket */ 224 225 #endif 226 /*************************************************************************** 227 * IPX_Close_Socket -- closes an open socket * 228 * * 229 * INPUT: * 230 * socket socket number to close * 231 * * 232 * OUTPUT: * 233 * 0 = ok, -1 = error * 234 * * 235 * WARNINGS: * 236 * none. * 237 * * 238 * HISTORY: * 239 * 12/15/1994 BR : Created. * 240 *=========================================================================*/ 241 #ifdef NOT_FOR_WIN95 242 int IPX_Close_Socket(unsigned short socket) 243 { 244 union REGS regs; 245 struct SREGS sregs; 246 RMIType rmi; 247 248 /*------------------------------------------------------------------------ 249 Close the socket: 250 DX = socket number 251 ------------------------------------------------------------------------*/ 252 /*........................................................................ 253 Fill in registers for the DPMI call 254 ........................................................................*/ 255 memset (®s, 0 ,sizeof(regs)); 256 segread (&sregs); 257 memset (&rmi, 0 ,sizeof(rmi)); 258 regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call 259 regs.w.bx = IPX_INT; // interrupt # to invoke 260 sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is 261 regs.x.edi = FP_OFF(&rmi); 262 /*........................................................................ 263 Fill in registers for the interrupt call 264 ........................................................................*/ 265 rmi.ebx = IPX_CLOSE_SOCKET; 266 rmi.edx = socket; 267 /*........................................................................ 268 call DPMI 269 ........................................................................*/ 270 int386x(DPMI_INT, ®s, ®s, &sregs); 271 272 return(0); 273 274 } /* end of IPX_Close_Socket */ 275 #endif //NOT_FOR_WIN95 276 277 /*************************************************************************** 278 * IPX_Get_Connection_Number -- gets local Connection Number * 279 * * 280 * This Novell call will the return the user's local "Connection Number". * 281 * This value will be 0 if the user isn't logged into Novell, so this * 282 * routine can be used to detect if other calls (such as Get_Local_Target) * 283 * will be OK. * 284 * * 285 * INPUT: * 286 * none. * 287 * * 288 * OUTPUT: * 289 * Connection Number, 0 = none * 290 * * 291 * WARNINGS: * 292 * none. * 293 * * 294 * HISTORY: * 295 * 12/15/1994 BR : Created. * 296 *=========================================================================*/ 297 #ifdef NOT_FOR_WIN95 298 int IPX_Get_Connection_Number(void) 299 { 300 union REGS regs; 301 struct SREGS sregs; 302 RMIType rmi; 303 int num; 304 305 /*------------------------------------------------------------------------ 306 Call Interrupt 0x21, with AH = 0xdc. This tells Novell to put the local 307 connection number into AL. 308 ------------------------------------------------------------------------*/ 309 /*........................................................................ 310 Fill in registers for the DPMI call 311 ........................................................................*/ 312 memset (®s, 0 ,sizeof(regs)); 313 segread (&sregs); 314 memset (&rmi, 0 ,sizeof(rmi)); 315 regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call 316 regs.w.bx = 0x21; // interrupt # to invoke 317 sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is 318 regs.x.edi = FP_OFF(&rmi); 319 /*........................................................................ 320 Fill in registers for the interrupt call 321 ........................................................................*/ 322 rmi.eax = 0x0000dc00; 323 /*........................................................................ 324 call DPMI 325 ........................................................................*/ 326 int386x(DPMI_INT, ®s, ®s, &sregs); 327 328 num = rmi.eax & 0x00ff; 329 330 return(num); 331 332 } /* end of IPX_Get_Connection_Number */ 333 334 #endif //NOT_FOR_WIN95 335 336 /*************************************************************************** 337 * IPX_Get_1st_Connection_Num -- gets 1st Connect Number for given user * 338 * * 339 * This gets the Connection Number for the given User ID. Since a user * 340 * may be logged in more than once, this just returns the first connection * 341 * found and ignores the others. * 342 * * 343 * INPUT: * 344 * username name of the user to get the Connection Number for * 345 * * 346 * OUTPUT: * 347 * first-found Connection Number for that user, 0 if user not logged in * 348 * * 349 * WARNINGS: * 350 * * 351 * HISTORY: * 352 * 12/15/1994 BR : Created. * 353 *=========================================================================*/ 354 #ifdef NOT_FOR_WIN95 355 int IPX_Get_1st_Connection_Num (char *username) 356 { 357 struct request_buffer { 358 unsigned short len; // username length + 5 359 unsigned char buffer_type; // ConnectionNum = 0x15 360 unsigned short object_type; // set ot 0x0100 361 unsigned char name_len; // length of username 362 char name [48]; // copy of username 363 unsigned short reserved; 364 }; 365 struct reply_buffer { 366 unsigned short len; 367 unsigned char number_connections; // will be 0 - 100 368 unsigned char connection_num [100]; // array of connection numbers 369 unsigned short reserved[2]; 370 }; 371 union REGS regs; 372 struct SREGS sregs; 373 RMIType rmi; 374 struct request_buffer *reqbuf; 375 struct reply_buffer *replybuf; 376 unsigned short segment; // for DOS allocation 377 unsigned short selector; // for DOS allocation 378 int num_conns; // # connections returned 379 int conn_num; // connection number 380 int rc; 381 382 /*------------------------------------------------------------------------ 383 Allocate DOS memory to store the buffers passed to the interrupt 384 ------------------------------------------------------------------------*/ 385 memset (®s, 0 ,sizeof(regs)); 386 segread (&sregs); 387 regs.x.eax = DPMI_ALLOC_DOS_MEM; // DPMI function to call 388 regs.x.ebx = (sizeof(struct request_buffer) + // # paragraphs to allocate 389 sizeof(struct reply_buffer) + 15) >> 4; 390 int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory 391 /*........................................................................ 392 If the carry flag is set, DPMI is indicating an error. 393 ........................................................................*/ 394 if (regs.x.cflag) { 395 return(0); 396 } 397 398 /*........................................................................ 399 Get pointers to allocated memory. 400 'reqbuf' is just the returned real-mode segment, multiplied by 16. 401 'replybuf' is an offset from 'reqbuf'. 402 ........................................................................*/ 403 segment = regs.w.ax; 404 selector = regs.w.dx; 405 reqbuf = (struct request_buffer *)(segment << 4); 406 replybuf = (struct reply_buffer *) 407 (((char *)reqbuf) + sizeof (struct request_buffer)); 408 409 /*------------------------------------------------------------------------ 410 Init the contents of the request & reply buffers 411 ------------------------------------------------------------------------*/ 412 reqbuf->len = (unsigned short)(strlen(username) + 5); 413 reqbuf->buffer_type = 0x15; 414 reqbuf->object_type = 0x0100; 415 reqbuf->name_len = (unsigned char) strlen(username); 416 strcpy(reqbuf->name, username); 417 reqbuf->reserved = reqbuf->reserved; // prevent compiler warning 418 replybuf->len = 101; 419 replybuf->reserved[0] = replybuf->reserved[0]; // prevent compiler warning 420 replybuf->reserved[0] = replybuf->reserved[1]; // prevent compiler warning 421 422 /*------------------------------------------------------------------------ 423 Invoke Int 21 with AH=0xe3, DS:SI=&request_buffer, ES:DI=&reply_buffer 424 ------------------------------------------------------------------------*/ 425 /*........................................................................ 426 Fill in registers for the DPMI call 427 ........................................................................*/ 428 memset (®s, 0 ,sizeof(regs)); 429 segread (&sregs); 430 memset (&rmi, 0 ,sizeof(rmi)); 431 regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call 432 regs.w.bx = 0x21; // interrupt # to invoke 433 sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is 434 regs.x.edi = FP_OFF(&rmi); 435 /*........................................................................ 436 Fill in registers for the interrupt call 437 ........................................................................*/ 438 rmi.eax = 0x0000e300; 439 rmi.ds = segment; 440 rmi.esi = 0; 441 rmi.es = segment; 442 rmi.edi = sizeof(struct request_buffer); 443 /*........................................................................ 444 call DPMI 445 ........................................................................*/ 446 int386x(DPMI_INT, ®s, ®s, &sregs); 447 448 /*------------------------------------------------------------------------ 449 Stash the 1st connection number 450 ------------------------------------------------------------------------*/ 451 rc = (rmi.eax & 0x00ff); // if AL !=0, error 452 num_conns = replybuf->number_connections; // # times user is logged in 453 conn_num = (int )replybuf->connection_num[0]; // 1st connection # 454 455 /*------------------------------------------------------------------------ 456 Free DOS memory 457 ------------------------------------------------------------------------*/ 458 memset (®s, 0 ,sizeof(regs)); 459 segread (&sregs); 460 regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call 461 regs.x.edx = selector; // ptr to free 462 int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory 463 464 /*------------------------------------------------------------------------ 465 Return error if function failed, or user not logged in 466 ------------------------------------------------------------------------*/ 467 if (rc != 0 || num_conns==0) { 468 return(0); 469 } else { 470 return(conn_num); 471 } 472 } 473 #endif //NOT_FOR_WIN95 474 475 /*************************************************************************** 476 * IPX_Get_Internet_Address -- gets Network Number & Node Address * 477 * * 478 * Once you've obtained a Connection Number from IPX_Get_Connection_Number * 479 * or IPX_Get_1st_Connection_Num, use this function to translate it into * 480 * a Network Number and Node Address; then, place those numbers in the * 481 * IPX header for outgoing packets. * 482 * * 483 * INPUT: * 484 * connection_number Connection Number to translate * 485 * network_number ptr: will hold Network Number * 486 * physical_node ptr: will hold Node Address * 487 * * 488 * OUTPUT: * 489 * 0 = OK, -1 = error * 490 * * 491 * WARNINGS: * 492 * If connection_number is 0 and NETX isn't running, this routine * 493 * will just put garbage into the network_number and physical_node. * 494 * * 495 * HISTORY: * 496 * 12/15/1994 BR : Created. * 497 *=========================================================================*/ 498 #ifdef NOT_FOR_WIN95 499 int IPX_Get_Internet_Address(int connection_number, 500 unsigned char *network_number, unsigned char *physical_node) 501 { 502 struct request_buffer { 503 unsigned short len; 504 unsigned char buffer_type; // Internet = 0x13 505 unsigned char connection_number; // Conn. Number to translate 506 }; 507 struct reply_buffer { 508 unsigned short len; 509 unsigned char network_number [4]; // filled in by IPX 510 unsigned char physical_node [6]; // filled in by IPX 511 unsigned short server_socket; // filled in by IPX, but don't use! 512 }; 513 union REGS regs; 514 struct SREGS sregs; 515 RMIType rmi; 516 struct request_buffer *reqbuf; 517 struct reply_buffer *replybuf; 518 unsigned short segment; // for DOS allocation 519 unsigned short selector; // for DOS allocation 520 521 /*------------------------------------------------------------------------ 522 Error if invalid connection is given 523 ------------------------------------------------------------------------*/ 524 if (connection_number==0) 525 return(-1); 526 527 /*------------------------------------------------------------------------ 528 Allocate DOS memory to store the buffers passed to the interrupt 529 ------------------------------------------------------------------------*/ 530 memset (®s, 0 ,sizeof(regs)); 531 segread (&sregs); 532 regs.x.eax = DPMI_ALLOC_DOS_MEM; // DPMI function to call 533 regs.x.ebx = (sizeof(struct request_buffer) + // # paragraphs to allocate 534 sizeof(struct reply_buffer) + 15) >> 4; 535 int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory 536 /*........................................................................ 537 If the carry flag is set, DPMI is indicating an error. 538 ........................................................................*/ 539 if (regs.x.cflag) { 540 return(-1); 541 } 542 543 /*........................................................................ 544 Get pointers to allocated memory. 545 'reqbuf' is just the returned real-mode segment, multiplied by 16. 546 'replybuf' is an offset from 'reqbuf'. 547 ........................................................................*/ 548 segment = regs.w.ax; 549 selector = regs.w.dx; 550 reqbuf = (struct request_buffer *)(segment << 4); 551 replybuf = (struct reply_buffer *) 552 (((char *)reqbuf) + sizeof (struct request_buffer)); 553 554 /*------------------------------------------------------------------------ 555 Init the contents of the request & reply buffers 556 ------------------------------------------------------------------------*/ 557 reqbuf->len = 2; 558 reqbuf->buffer_type = 0x13; 559 reqbuf->connection_number = (unsigned char)connection_number; 560 replybuf->len = 12; 561 replybuf->network_number[0] = replybuf->network_number[0]; // suppress warning 562 replybuf->physical_node[0] = replybuf->physical_node[0]; // suppress warning 563 replybuf->server_socket = replybuf->server_socket; // suppress warning 564 565 /*------------------------------------------------------------------------ 566 Invoke Int 21 with AH=0xe3, DS:SI=&request_buffer, ES:DI=&reply_buffer 567 ------------------------------------------------------------------------*/ 568 /*........................................................................ 569 Fill in registers for the DPMI call 570 ........................................................................*/ 571 memset (®s, 0 ,sizeof(regs)); 572 segread (&sregs); 573 memset (&rmi, 0 ,sizeof(rmi)); 574 regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call 575 regs.w.bx = 0x21; // interrupt # to invoke 576 sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is 577 regs.x.edi = FP_OFF(&rmi); 578 /*........................................................................ 579 Fill in registers for the interrupt call 580 ........................................................................*/ 581 rmi.eax = 0x0000e300; 582 rmi.ds = segment; 583 rmi.esi = 0; 584 rmi.es = segment; 585 rmi.edi = sizeof(struct request_buffer); 586 /*........................................................................ 587 call DPMI 588 ........................................................................*/ 589 int386x(DPMI_INT, ®s, ®s, &sregs); 590 591 memcpy(network_number, replybuf->network_number, 4); 592 memcpy(physical_node, replybuf->physical_node, 6); 593 594 /*------------------------------------------------------------------------ 595 Free DOS memory 596 ------------------------------------------------------------------------*/ 597 memset (®s, 0 ,sizeof(regs)); 598 segread (&sregs); 599 regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call 600 regs.x.edx = selector; // ptr to free 601 int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory 602 603 return(0); 604 605 } /* end of IPX_Get_Internet_Address */ 606 607 #endif // NOT_FOR_WIN95 608 609 /*************************************************************************** 610 * IPX_Get_User_ID -- gets user ID from Connection Number * 611 * * 612 * INPUT: * 613 * connection_number Connection Number to get User ID for * 614 * user_id ptr to buffer to put User ID into; * 615 * size must be >= 48 chars * 616 * * 617 * OUTPUT: * 618 * 0 = OK, -1 = error * 619 * * 620 * WARNINGS: * 621 * none. * 622 * * 623 * HISTORY: * 624 * 12/15/1994 BR : Created. * 625 *=========================================================================*/ 626 #ifdef NOT_FOR_WIN95 627 int IPX_Get_User_ID(int connection_number, char *user_id) 628 { 629 struct request_buffer { 630 unsigned short len; 631 unsigned char buffer_type; // 0x16 = UserID buffer type 632 unsigned char connection_number; // Connection Number to get ID for 633 }; 634 struct reply_buffer { 635 unsigned short len; 636 unsigned char object_id[4]; 637 unsigned char object_type[2]; 638 char object_name[48]; 639 char login_time[7]; 640 unsigned short reserved; 641 }; 642 union REGS regs; 643 struct SREGS sregs; 644 RMIType rmi; 645 struct request_buffer *reqbuf; 646 struct reply_buffer *replybuf; 647 unsigned short segment; // for DOS allocation 648 unsigned short selector; // for DOS allocation 649 650 /*------------------------------------------------------------------------ 651 Error if invalid connection is given 652 ------------------------------------------------------------------------*/ 653 if (connection_number==0) 654 return(-1); 655 656 /*------------------------------------------------------------------------ 657 Allocate DOS memory to store the buffers passed to the interrupt 658 ------------------------------------------------------------------------*/ 659 memset (®s, 0 ,sizeof(regs)); 660 segread (&sregs); 661 regs.x.eax = DPMI_ALLOC_DOS_MEM; // DPMI function to call 662 regs.x.ebx = (sizeof(struct request_buffer) + // # paragraphs to allocate 663 sizeof(struct reply_buffer) + 15) >> 4; 664 int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory 665 /*........................................................................ 666 If the carry flag is set, DPMI is indicating an error. 667 ........................................................................*/ 668 if (regs.x.cflag) { 669 return(-1); 670 } 671 672 /*........................................................................ 673 Get pointers to allocated memory. 674 'reqbuf' is just the returned real-mode segment, multiplied by 16. 675 'replybuf' is an offset from 'reqbuf'. 676 ........................................................................*/ 677 segment = regs.w.ax; 678 selector = regs.w.dx; 679 reqbuf = (struct request_buffer *)(segment << 4); 680 replybuf = (struct reply_buffer *) 681 (((char *)reqbuf) + sizeof (struct request_buffer)); 682 683 /*------------------------------------------------------------------------ 684 Init the contents of the request & reply buffers 685 ------------------------------------------------------------------------*/ 686 reqbuf->len = 2; 687 reqbuf->buffer_type = 0x16; 688 reqbuf->connection_number = (unsigned char)connection_number; 689 replybuf->len = sizeof(struct reply_buffer) - 2; 690 replybuf->object_id[0] = replybuf->object_id[0]; // suppress warnings 691 replybuf->object_type[0] = replybuf->object_type[0]; // suppress warnings 692 replybuf->login_time[0] = replybuf->login_time[0]; // suppress warnings 693 replybuf->reserved = replybuf->reserved; // suppress warnings 694 695 /*------------------------------------------------------------------------ 696 Invoke Int 21 with AH=0xe3, DS:SI=&request_buffer, ES:DI=&reply_buffer 697 ------------------------------------------------------------------------*/ 698 /*........................................................................ 699 Fill in registers for the DPMI call 700 ........................................................................*/ 701 memset (®s, 0 ,sizeof(regs)); 702 segread (&sregs); 703 memset (&rmi, 0 ,sizeof(rmi)); 704 regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call 705 regs.w.bx = 0x21; // interrupt # to invoke 706 sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is 707 regs.x.edi = FP_OFF(&rmi); 708 /*........................................................................ 709 Fill in registers for the interrupt call 710 ........................................................................*/ 711 rmi.eax = 0x0000e300; 712 rmi.ds = segment; 713 rmi.esi = 0; 714 rmi.es = segment; 715 rmi.edi = sizeof(struct request_buffer); 716 /*........................................................................ 717 call DPMI 718 ........................................................................*/ 719 int386x(DPMI_INT, ®s, ®s, &sregs); 720 721 /*------------------------------------------------------------------------ 722 Fill in the caller's buffer with the user name 723 ------------------------------------------------------------------------*/ 724 strncpy(user_id, replybuf->object_name, 48); 725 726 /*------------------------------------------------------------------------ 727 Free DOS memory 728 ------------------------------------------------------------------------*/ 729 memset (®s, 0 ,sizeof(regs)); 730 segread (&sregs); 731 regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call 732 regs.x.edx = selector; // ptr to free 733 int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory 734 735 return(0); 736 737 } /* end of IPX_Get_User_ID */ 738 #endif //NOT_FOR_WIN95 739 740 741 /*************************************************************************** 742 * IPX_Listen_For_Packet -- commands IPX to listen for a packet * 743 * * 744 * Before calling this function, you must fill in an ECB: * 745 * SocketNumber: must contain the socket you've opened, * 746 * and are "listening" on * 747 * Event_Service_Routine: optionally points to a callback routine * 748 * PacketCount: set to 2, to tell IPX there are 2 areas to * 749 * store the incoming data in * 750 * Packet[0].Address: set to the address of an IPXHeaderType * 751 * Packet[0].Length: sizeof(IPXHeaderType) * 752 * Packet[1].Address: address of data buffer, for the packet * 753 * Packet[1].Length: size of the data buffer * 754 * * 755 * When the packet is received, ECBType.CompletionCode will be 0 if * 756 * successful. Otherwise, some error occurred. * 757 * * 758 * You should initialize the ECB to 0's before filling it in. * 759 * * 760 * INPUT: * 761 * ecb_ptr pointer to a filled-in ECB; MUST be real-mode memory * 762 * * 763 * OUTPUT: * 764 * 0 = OK, IPX error otherwise * 765 * * 766 * WARNINGS: * 767 * The ECB must be located in real-mode memory, as well as the values * 768 * pointed to in the ECB (the IPX Header, the buffer to send, etc.) * 769 * * 770 * HISTORY: * 771 * 12/15/1994 BR : Created. * 772 *=========================================================================*/ 773 #ifdef NOT_FOR_WIN95 774 int IPX_Listen_For_Packet(struct ECB *ecb_ptr) 775 { 776 union REGS regs; 777 struct SREGS sregs; 778 RMIType rmi; 779 780 /*------------------------------------------------------------------------ 781 Call IPX with ES:SI=ecb_ptr 782 ------------------------------------------------------------------------*/ 783 /*........................................................................ 784 Fill in registers for the DPMI call 785 ........................................................................*/ 786 memset (®s, 0 ,sizeof(regs)); 787 segread (&sregs); 788 memset (&rmi, 0 ,sizeof(rmi)); 789 regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call 790 regs.w.bx = IPX_INT; // interrupt # to invoke 791 sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is 792 regs.x.edi = FP_OFF(&rmi); 793 /*........................................................................ 794 Fill in registers for the interrupt call 795 ........................................................................*/ 796 rmi.ebx = IPX_LISTEN_FOR_PACKET; 797 rmi.es = (short)((long)ecb_ptr >> 4); 798 rmi.esi = (long)((long)ecb_ptr & 0x000f); 799 /*........................................................................ 800 call DPMI 801 ........................................................................*/ 802 int386x(DPMI_INT, ®s, ®s, &sregs); 803 804 return(rmi.eax & 0x00ff); 805 806 } /* end of IPX_Listen_For_Packet */ 807 #endif //NOT_FOR_WIN95 808 809 /*************************************************************************** 810 * IPX_Send_Packet -- commands IPX to send a packet * 811 * * 812 * Before calling this function, you must fill in an ECB: * 813 * SocketNumber: must contain the socket you've opened, * 814 * and are sending on * 815 * Event_Service_Routine: optionally points to a callback routine * 816 * PacketCount: set to 2, to tell IPX there are 2 areas the * 817 * outgoing data is stored in * 818 * Packet[0].Address: set to the address of an IPXHeaderType * 819 * Packet[0].Length: sizeof(IPXHeaderType) * 820 * Packet[1].Address: address of buffer containing data to send * 821 * Packet[1].Length: size of the data buffer * 822 * ImmediateAddress: must be filled in with the node address of * 823 * the bridge that will route the message; * 824 * fill this in by calling IPX_Get_Local_Target * 825 * * 826 * Also, you must fill in the IPXHeaderType with certain values: * 827 * PacketType: set to 4 for IPX * 828 * DestNetworkNumber: Network Number of the destination system * 829 * DestNetworkNode: Node Address of the destination system * 830 * DestNetworkSocket: the destination system's socket to send to; * 831 * this doesn't have to be the same as the * 832 * socket opened on the local machine. * 833 * * 834 * You should initialize the ECB & IPXHeader to 0's before filling them in.* 835 * * 836 * INPUT: * 837 * ecb_ptr pointer to a filled-in ECB * 838 * * 839 * OUTPUT: * 840 * none. This function doesn't return anything; the caller must check * 841 * its ECB.CompletionCode for: 0 = OK, IPX Error otherwise. * 842 * * 843 * WARNINGS: * 844 * The ECB must be located in real-mode memory, as well as the values * 845 * pointed to in the ECB (the IPX Header, the buffer to send, etc.) * 846 * * 847 * HISTORY: * 848 * 12/15/1994 BR : Created. * 849 *=========================================================================*/ 850 #ifdef NOT_FOR_WIN95 851 void IPX_Send_Packet(struct ECB *ecb_ptr) 852 { 853 union REGS regs; 854 struct SREGS sregs; 855 RMIType rmi; 856 857 /*------------------------------------------------------------------------ 858 Call IPX with ES:SI=ecb_ptr 859 ------------------------------------------------------------------------*/ 860 /*........................................................................ 861 Fill in registers for the DPMI call 862 ........................................................................*/ 863 memset (®s, 0 ,sizeof(regs)); 864 segread (&sregs); 865 memset (&rmi, 0 ,sizeof(rmi)); 866 regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call 867 regs.w.bx = IPX_INT; // interrupt # to invoke 868 sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is 869 regs.x.edi = FP_OFF(&rmi); 870 /*........................................................................ 871 Fill in registers for the interrupt call 872 ........................................................................*/ 873 rmi.ebx = IPX_SEND_PACKET; 874 rmi.es = (short)((long)ecb_ptr >> 4); 875 rmi.esi = (long)((long)ecb_ptr & 0x000f); 876 /*........................................................................ 877 call DPMI 878 ........................................................................*/ 879 int386x(DPMI_INT, ®s, ®s, &sregs); 880 881 } /* end of IPX_Send_Packet */ 882 #endif //NOT_FOR_WIN95 883 884 /*************************************************************************** 885 * IPX_Get_Local_Target -- fills in ImmediateAddress field of ECB * 886 * * 887 * Use this function to fill in the ECB's ImmediateAddress field when * 888 * sending a packet. The Immediate Address is the node address of the * 889 * bridge that must route the message. If there is no bridge, it's * 890 * filled in with the destination Node Address. In either case, it * 891 * will contain the proper value for sending. * 892 * * 893 * INPUT: * 894 * dest_network destination Network Number * 895 * dest_node destination Node Address * 896 * dest_socket destination Socket Number * 897 * bridge_address field to fill in with Immediate Address * 898 * * 899 * OUTPUT: * 900 * 0 = OK, error otherwise * 901 * * 902 * WARNINGS: * 903 * If the Connection Number is 0 (user not logged in), dest_network * 904 * and dest_node will be garbage, and this routine will probably crash. * 905 * * 906 * HISTORY: * 907 * 12/15/1994 BR : Created. * 908 *=========================================================================*/ 909 #ifdef NOT_FOR_WIN95 910 int IPX_Get_Local_Target(unsigned char *dest_network, unsigned char *dest_node, 911 unsigned short dest_socket, unsigned char *bridge_address) 912 { 913 struct request_buffer { 914 unsigned char network_number [4]; 915 unsigned char physical_node [6]; 916 unsigned short socket; 917 }; 918 struct reply_buffer { 919 unsigned char local_target [6]; 920 }; 921 unsigned int rc; 922 union REGS regs; 923 struct SREGS sregs; 924 RMIType rmi; 925 struct request_buffer *reqbuf; 926 struct reply_buffer *replybuf; 927 unsigned short segment; // for DOS allocation 928 unsigned short selector; // for DOS allocation 929 930 /*------------------------------------------------------------------------ 931 Allocate DOS memory to store the buffers passed to the interrupt 932 ------------------------------------------------------------------------*/ 933 memset (®s, 0 ,sizeof(regs)); 934 segread (&sregs); 935 regs.x.eax = DPMI_ALLOC_DOS_MEM; // DPMI function to call 936 regs.x.ebx = (sizeof(struct request_buffer) + // # paragraphs to allocate 937 sizeof(struct reply_buffer) + 15) >> 4; 938 int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory 939 /*........................................................................ 940 If the carry flag is set, DPMI is indicating an error. 941 ........................................................................*/ 942 if (regs.x.cflag) { 943 return(-1); 944 } 945 946 /*........................................................................ 947 Get pointers to allocated memory. 948 'reqbuf' is just the returned real-mode segment, multiplied by 16. 949 'replybuf' is an offset from 'reqbuf'. 950 ........................................................................*/ 951 segment = regs.w.ax; 952 selector = regs.w.dx; 953 reqbuf = (struct request_buffer *)(segment << 4); 954 replybuf = (struct reply_buffer *) 955 (((char *)reqbuf) + sizeof (struct request_buffer)); 956 957 /*------------------------------------------------------------------------ 958 Init the contents of the request & reply buffers 959 ------------------------------------------------------------------------*/ 960 memcpy(reqbuf->network_number, dest_network, 4); 961 memcpy(reqbuf->physical_node, dest_node, 6); 962 reqbuf->socket = dest_socket; 963 964 /*------------------------------------------------------------------------ 965 Invoke IPX with DS:SI=&request_buffer, ES:DI=&reply_buffer 966 ------------------------------------------------------------------------*/ 967 /*........................................................................ 968 Fill in registers for the DPMI call 969 ........................................................................*/ 970 memset (®s, 0 ,sizeof(regs)); 971 segread (&sregs); 972 memset (&rmi, 0 ,sizeof(rmi)); 973 regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call 974 regs.w.bx = IPX_INT; // interrupt # to invoke 975 sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is 976 regs.x.edi = FP_OFF(&rmi); 977 /*........................................................................ 978 Fill in registers for the interrupt call 979 ........................................................................*/ 980 rmi.ebx = IPX_GET_LOCAL_TARGET; 981 rmi.ds = segment; 982 rmi.esi = 0; 983 rmi.es = segment; 984 rmi.edi = sizeof(struct request_buffer); 985 /*........................................................................ 986 call DPMI 987 ........................................................................*/ 988 int386x(DPMI_INT, ®s, ®s, &sregs); 989 990 rc = (rmi.eax & 0x00ff); 991 memcpy(bridge_address, replybuf->local_target, 6); 992 993 /*------------------------------------------------------------------------ 994 Free DOS memory 995 ------------------------------------------------------------------------*/ 996 memset (®s, 0 ,sizeof(regs)); 997 segread (&sregs); 998 regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call 999 regs.x.edx = selector; // ptr to free 1000 int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory 1001 1002 return(rc); 1003 1004 } /* end of IPX_Get_Local_Target */ 1005 1006 #endif //NOT_FOR_WIN95 1007 1008 /*************************************************************************** 1009 * IPX_Cancel_Event -- cancels an operation in progress * 1010 * * 1011 * INPUT: * 1012 * ecb_ptr pointer to ECB event to cancel * 1013 * * 1014 * OUTPUT: * 1015 * ??? * 1016 * * 1017 * WARNINGS: * 1018 * The ECB must be located in real-mode memory, as well as the values * 1019 * pointed to in the ECB (the IPX Header, the buffer to send, etc.) * 1020 * * 1021 * HISTORY: * 1022 * 12/15/1994 BR : Created. * 1023 *=========================================================================*/ 1024 #ifdef NOT_FOR_WIN95 1025 int IPX_Cancel_Event(struct ECB *ecb_ptr) 1026 { 1027 union REGS regs; 1028 struct SREGS sregs; 1029 RMIType rmi; 1030 unsigned int rc; 1031 1032 /*........................................................................ 1033 Fill in registers for the DPMI call 1034 ........................................................................*/ 1035 memset (®s, 0 ,sizeof(regs)); 1036 segread (&sregs); 1037 memset (&rmi, 0 ,sizeof(rmi)); 1038 regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call 1039 regs.w.bx = IPX_INT; // interrupt # to invoke 1040 sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is 1041 regs.x.edi = FP_OFF(&rmi); 1042 /*........................................................................ 1043 Fill in registers for the interrupt call 1044 ........................................................................*/ 1045 rmi.ebx = IPX_CANCEL_EVENT; 1046 rmi.es = (short)((long)ecb_ptr >> 4); 1047 rmi.esi = (long)((long)ecb_ptr & 0x000f); 1048 /*........................................................................ 1049 call DPMI 1050 ........................................................................*/ 1051 int386x(DPMI_INT, ®s, ®s, &sregs); 1052 1053 rc = (rmi.eax & 0x00ff); 1054 1055 return(rc); 1056 1057 } /* end of IPX_Cancel_Event */ 1058 #endif //NOT_FOR_WIN95 1059 1060 /*************************************************************************** 1061 * Let_IPX_Breath -- gives IPX some CPU time * 1062 * * 1063 * Use this function if you're polling the ECB's InUse flag, waiting * 1064 * for it to go to 0: * 1065 * * 1066 * while ECBType.InUse * 1067 * Let_IPX_Breath(); * 1068 * * 1069 * INPUT: * 1070 * none. * 1071 * * 1072 * OUTPUT: * 1073 * none. * 1074 * * 1075 * WARNINGS: * 1076 * none. * 1077 * * 1078 * HISTORY: * 1079 * 12/15/1994 BR : Created. * 1080 *=========================================================================*/ 1081 #ifdef NOT_FOR_WIN95 1082 void Let_IPX_Breath(void) 1083 { 1084 union REGS regs; 1085 struct SREGS sregs; 1086 RMIType rmi; 1087 1088 /*........................................................................ 1089 Fill in registers for the DPMI call 1090 ........................................................................*/ 1091 memset (®s, 0 ,sizeof(regs)); 1092 segread (&sregs); 1093 memset (&rmi, 0 ,sizeof(rmi)); 1094 regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call 1095 regs.w.bx = IPX_INT; // interrupt # to invoke 1096 sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is 1097 regs.x.edi = FP_OFF(&rmi); 1098 /*........................................................................ 1099 Fill in registers for the interrupt call 1100 ........................................................................*/ 1101 rmi.ebx = IPX_RELINQUISH_CONTROL; 1102 /*........................................................................ 1103 call DPMI 1104 ........................................................................*/ 1105 int386x(DPMI_INT, ®s, ®s, &sregs); 1106 1107 } /* end of Let_IPX_Breath */ 1108 #endif //NOT_FOR_WIN95