Logo Search packages:      
Sourcecode: dosbox version File versions  Download package

ipx.cpp

/*
 *  Copyright (C) 2002-2004  The DOSBox Team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "dosbox.h"

#if C_IPX

#include <string.h>
#include <time.h>
#include <stdio.h>
#include "cross.h"
#include "support.h"
#include "cpu.h"
#include "SDL_net.h"
#include "regs.h"
#include "inout.h"
#include "setup.h"
#include "debug.h"
#include "callback.h"
#include "dos_system.h"
#include "mem.h"
#include "ipx.h"
#include "ipxserver.h"
#include "timer.h"
#include "SDL_net.h"
#include "programs.h"

#define SOCKTABLESIZE   150 // DOS IPX driver was limited to 150 open sockets
#define DOS_MEMSEG            0xd000;

Bit32u tcpPort;
bool isIpxServer;
bool isIpxConnected;
IPaddress ipxClientIp;  // IPAddress for client connection to server
IPaddress ipxServConnIp;  // IPAddress for client connection to server
TCPsocket ipxTCPClientSocket;
UDPsocket ipxClientSocket;
int UDPChannel;  // Channel used by UDP connection
Bit8u recvBuffer[IPXBUFFERSIZE]; // Incoming packet buffer
Bitu call_ipx; // Callback of RETF entrypoint
Bitu call_ipxint; // Callback of INT 7A entrypoint
Bitu call_ipxesr1; // Callback of ESR init routine
Bitu call_ipxesr2; // Callback of ESR return routine
Bit16u dospage;
static RealPt ipx_callback;
static RealPt ipx_intcallback;
static RealPt ipx_esrcallback;
static RealPt ipx_esrptraddr;
static RealPt processedECB;

SDLNet_SocketSet clientSocketSet;
static bool inESR;

struct ipxnetaddr {
      Uint8 netnum[4];   // Both are big endian
      Uint8 netnode[6];
} localIpxAddr;

struct fragmentDescriptor {
      Bit16u offset;
      Bit16u segment;
      Bit16u size;
};

packetBuffer incomingPacket;

static Bit16u swapByte(Bit16u sockNum) {
      return (((sockNum>> 8)) | (sockNum << 8));
}

void UnpackIP(PackedIP ipPack, IPaddress * ipAddr) {
      ipAddr->host = ipPack.host;
      ipAddr->port = ipPack.port;
}

void PackIP(IPaddress ipAddr, PackedIP *ipPack) {
      ipPack->host = ipAddr.host;
      ipPack->port = ipAddr.port;
}

class ECBClass {
public:
      RealPt ECBAddr;
    
      ECBClass *prevECB;
      ECBClass *nextECB;

      Bit16u getSocket(void) {
            return swapByte(real_readw(RealSeg(ECBAddr), RealOff(ECBAddr) + 0xa));
      }

      Bit8u getInUseFlag(void) {
            return real_readb(RealSeg(ECBAddr), RealOff(ECBAddr) + 0x8);
      }

      void setInUseFlag(Bit8u flagval) {
            real_writeb(RealSeg(ECBAddr), RealOff(ECBAddr) + 0x8, flagval);
      }

      void setCompletionFlag(Bit8u flagval) {
            real_writeb(RealSeg(ECBAddr), RealOff(ECBAddr) + 0x9, flagval);
      }

      Bit16u getFragCount(void) {
            return real_readw(RealSeg(ECBAddr), RealOff(ECBAddr) + 34);
      }

      void getFragDesc(Bit16u descNum, fragmentDescriptor *fragDesc) {
            Bit16u memoff = RealOff(ECBAddr) + 30 + ((descNum+1) * 6);
            fragDesc->offset = real_readw(RealSeg(ECBAddr), memoff);
            memoff+=2;
            fragDesc->segment = real_readw(RealSeg(ECBAddr), memoff);
            memoff+=2;
            fragDesc->size = real_readw(RealSeg(ECBAddr), memoff);
      }

      RealPt getESRAddr(void) {
            return RealMake(real_readw(RealSeg(ECBAddr), RealOff(ECBAddr)+6), real_readw(RealSeg(ECBAddr), RealOff(ECBAddr)+4));
      }

      void NotifyESR(void) {
            RealPt tmpAddr = getESRAddr();
            if(tmpAddr == 0) return;
            if(!inESR) {
                  //LOG_MSG("Calling ESR");
                  processedECB = ECBAddr;
                  inESR = true;
                  //LOG_MSG("Write %x to %x", real_readd(RealSeg(ECBAddr), RealOff(ECBAddr)+4), RealMake(RealSeg(ipx_esrptraddr), RealOff(ipx_esrptraddr)));
                  real_writed(RealSeg(ipx_esrptraddr), RealOff(ipx_esrptraddr),real_readd(RealSeg(ECBAddr), RealOff(ECBAddr)+4));
                  CPU_CALL(false, RealSeg(ipx_esrcallback), RealOff(ipx_esrcallback),0);
            }
      }

      void setImmAddress(Bit8u *immAddr) {
            Bits i;
            for(i=0;i<6;i++) {
                  real_writeb(RealSeg(ECBAddr), RealOff(ECBAddr)+28, immAddr[i]);
            }
      }

};

ECBClass *ECBList;  // Linked list of ECB's

ECBClass * CreateECB(RealPt useAddr) {
      ECBClass *tmpECB;
      tmpECB = new ECBClass();
      
      tmpECB->ECBAddr = useAddr;
      tmpECB->prevECB = NULL;
      tmpECB->nextECB = NULL;
      
      if (ECBList == NULL) {
            ECBList = tmpECB;
      } else {
            // Transverse the list until we hit the end
            ECBClass *useECB;
            useECB = ECBList;
            while(useECB->nextECB != NULL) {
                  useECB = useECB->nextECB;
            }
            useECB->nextECB = tmpECB;
            tmpECB->prevECB = useECB;
      }

      return tmpECB;
}

void DeleteECB(ECBClass * useECB) {
      if(useECB == NULL) return;

      if(useECB->prevECB == NULL) {
            ECBList = useECB->nextECB;
            if(ECBList != NULL) ECBList->prevECB = NULL;
      } else {
            useECB->prevECB->nextECB = useECB->nextECB;
            if(useECB->nextECB != NULL) useECB->nextECB->prevECB = useECB->prevECB;
      }
      delete useECB;
}

Bit16u socketCount;
Bit16u opensockets[SOCKTABLESIZE]; 

static bool sockInUse(Bit16u sockNum) {
      Bit16u i;
      for(i=0;i<socketCount;i++) {
            if (opensockets[i] == sockNum) return true;
      }
      return false;
}

static void OpenSocket(void) {
      Bit16u sockNum, sockAlloc;

      sockNum = swapByte(reg_dx);

      if(socketCount >= SOCKTABLESIZE) {
            reg_al = 0xfe; // Socket table full
            return;
      }

      if(sockNum == 0x0000) {
            // Dynamic socket allocation
            sockAlloc = 0x4002;
            while(sockInUse(sockAlloc) && (sockAlloc < 0x7fff)) sockAlloc++;
            if(sockAlloc > 0x7fff) {
                  // I have no idea how this could happen if the IPX driver is limited to 150 open sockets at a time
                  LOG_MSG("IPX: Out of dynamic sockets");
            }
            sockNum = sockAlloc;
      } else {
            if(sockInUse(sockNum)) {
                  reg_al = 0xff; // Socket already open
                  return;
            } 
      }

      opensockets[socketCount] = sockNum;
      socketCount++;

      reg_al = 0x00; // Success
      reg_dx = swapByte(sockNum);  // Convert back to big-endian

}

static void CloseSocket(void) {
      Bit16u sockNum, i;

      sockNum = swapByte(reg_dx);
      if(!sockInUse(sockNum)) return;

      for(i=0;i<socketCount-1;i++) {
            if (opensockets[i] == sockNum) {
                  // Realign list of open sockets
                  memcpy(&opensockets[i], &opensockets[i+1], SOCKTABLESIZE - (i + 1));
                  break;
            }
      }
      --socketCount;
}

static bool IPX_Multiplex(void)
{
      if(reg_ax != 0x7a00) return false;
      reg_al = 0xff;
      SegSet16(es,RealSeg(ipx_callback));
      reg_di = RealOff(ipx_callback);
      return true;
}

static void handleIpxRequest(void) {
      ECBClass *tmpECB;

      switch (reg_bx) {
            case 0x0000:  // Open socket
                  OpenSocket();
                  LOG_MSG("IPX: Open socket %4x", swapByte(reg_dx));
                  break;
            case 0x0001:  // Close socket
                  LOG_MSG("IPX: Close socket %4x", swapByte(reg_dx));
                  CloseSocket();
                  break;
            case 0x0003:  // Send packet
                  if(!incomingPacket.connected) {
                        tmpECB = CreateECB(RealMake(SegValue(es), reg_si));
                        tmpECB->setInUseFlag(USEFLAG_AVAILABLE);
                        tmpECB->setCompletionFlag(COMP_UNDELIVERABLE);
                        DeleteECB(tmpECB);
                        reg_al = 0xff; // Failure
                  } else {
                        tmpECB = CreateECB(RealMake(SegValue(es), reg_si));
                        tmpECB->setInUseFlag(USEFLAG_SENDING);
                        reg_al = 0x00; // Success
                  }
                  //LOG_MSG("IPX: Sending packet on %4x", tmpECB->getSocket());
                  break;
            case 0x0004:  // Listen for packet
                  tmpECB = CreateECB(RealMake(SegValue(es), reg_si));
                  if(!sockInUse(tmpECB->getSocket())) {
                        reg_al = 0xff;  // Socket is not open
                        tmpECB->setInUseFlag(USEFLAG_AVAILABLE);
                        tmpECB->setCompletionFlag(COMP_HARDWAREERROR);
                        DeleteECB(tmpECB);
                  } else {
                        reg_al = 0x00;  // Success
                        tmpECB->setInUseFlag(USEFLAG_LISTENING);
                        //LOG_MSG("IPX: Listen for packet on 0x%4x - ESR address %4x:%4x", tmpECB->getSocket(), RealSeg(tmpECB->getESRAddr()), RealOff(tmpECB->getESRAddr()));
                  }
                  
                  
                  break;
            case 0x0008:  // Get interval marker
                  // ????
                  break;
            case 0x0009:  // Get internetwork address
                  {
                        LOG_MSG("IPX: Get internetwork address %2x:%2x:%2x:%2x:%2x:%2x", localIpxAddr.netnode[5], localIpxAddr.netnode[4], localIpxAddr.netnode[3], localIpxAddr.netnode[2], localIpxAddr.netnode[1], localIpxAddr.netnode[0]);
                        Bit8u * addrptr;
                        Bits i;

                        addrptr = (Bit8u *)&localIpxAddr;
                        for(i=0;i<10;i++) {
                    real_writeb(SegValue(es),reg_si+i,addrptr[i]);
                        }
                        break;
                  }
            case 0x000a:  // Relinquish control
                  // Idle thingy
                  break;
            default:
                  LOG_MSG("Unhandled IPX function: %4x", reg_bx);
                  break;
      }
      
}

// Entrypoint handler
Bitu IPX_Handler(void) {
      handleIpxRequest();
      return CBRET_NONE;
}

// INT 7A handler
Bitu IPX_IntHandler(void) {
      handleIpxRequest();
      return CBRET_NONE;
}

static void disconnectServer(bool unexpected) {
      
      // There is no Timer remove code, hence this has to be done manually
      incomingPacket.connected = false;

      if(unexpected) LOG_MSG("IPX: Server disconnected unexpectedly");

}

static void pingAck(IPaddress retAddr) {
      IPXHeader regHeader;
      UDPpacket regPacket;
      Bits result;

      SDLNet_Write16(0xffff, regHeader.checkSum);
      SDLNet_Write16(sizeof(regHeader), regHeader.length);
      
      SDLNet_Write32(0, regHeader.dest.network);
      PackIP(retAddr, &regHeader.dest.addr.byIP);
      SDLNet_Write16(0x2, regHeader.dest.socket);

      SDLNet_Write32(0, regHeader.src.network);
      memcpy(regHeader.src.addr.byNode.node, localIpxAddr.netnode, sizeof(localIpxAddr));
      SDLNet_Write16(0x2, regHeader.src.socket);
      regHeader.transControl = 0;
      regHeader.pType = 0x0;

      regPacket.data = (Uint8 *)&regHeader;
      regPacket.len = sizeof(regHeader);
      regPacket.maxlen = sizeof(regHeader);
      regPacket.channel = UDPChannel;
      
      result = SDLNet_UDP_Send(ipxClientSocket, regPacket.channel, &regPacket);

}

static void pingSend(void) {
      IPXHeader regHeader;
      UDPpacket regPacket;
      Bits result;

      SDLNet_Write16(0xffff, regHeader.checkSum);
      SDLNet_Write16(sizeof(regHeader), regHeader.length);
      
      SDLNet_Write32(0, regHeader.dest.network);
      regHeader.dest.addr.byIP.host = 0xffffffff;
      regHeader.dest.addr.byIP.port = 0xffff;
      SDLNet_Write16(0x2, regHeader.dest.socket);

      SDLNet_Write32(0, regHeader.src.network);
      memcpy(regHeader.src.addr.byNode.node, localIpxAddr.netnode, sizeof(localIpxAddr));
      SDLNet_Write16(0x2, regHeader.src.socket);
      regHeader.transControl = 0;
      regHeader.pType = 0x0;

      regPacket.data = (Uint8 *)&regHeader;
      regPacket.len = sizeof(regHeader);
      regPacket.maxlen = sizeof(regHeader);
      regPacket.channel = UDPChannel;
      
      result = SDLNet_UDP_Send(ipxClientSocket, regPacket.channel, &regPacket);
      if(!result) {
            LOG_MSG("IPX: SDLNet_UDP_Send: %s\n", SDLNet_GetError());
      }

}

static void receivePacket(Bit8u *buffer, Bit16s bufSize) {
      ECBClass *useECB;
      ECBClass *nextECB;
      fragmentDescriptor tmpFrag;
      Bit16u i, fragCount,t;
      Bit16s bufoffset;
      Bit16u *bufword = (Bit16u *)buffer;
      Bit16u useSocket = swapByte(bufword[8]);
      Bit32u hostaddr;
      IPXHeader * tmpHeader;
      tmpHeader = (IPXHeader *)buffer;

      // Check to see if ping packet
      if(useSocket == 0x2) {
            // Is this a broadcast?
            if((tmpHeader->dest.addr.byIP.host == 0xffffffff) && (tmpHeader->dest.addr.byIP.port = 0xffff)) {
                  // Yes.  We should return the ping back to the sender
                  IPaddress tmpAddr;
                  UnpackIP(tmpHeader->src.addr.byIP, &tmpAddr);
                  pingAck(tmpAddr);
                  return;
            }
      }


      useECB = ECBList;
      while(useECB != NULL) {
            nextECB = useECB->nextECB;
            if(useECB->getInUseFlag() == USEFLAG_LISTENING) {
                  if(useECB->getSocket() == useSocket) {
                        useECB->setInUseFlag(USEFLAG_AVAILABLE);
                        fragCount = useECB->getFragCount(); 
                        bufoffset = 0;
                        for(i=0;i<fragCount;i++) {
                              useECB->getFragDesc(i,&tmpFrag);
                              for(t=0;t<tmpFrag.size;t++) {
                                    real_writeb(tmpFrag.segment, tmpFrag.offset+t, buffer[bufoffset]);
                                    bufoffset++;
                                    if(bufoffset>=bufSize) {
                                          useECB->setCompletionFlag(COMP_SUCCESS);
                                          useECB->setImmAddress(&buffer[22]);  // Write in source node
                                          hostaddr = *((Bit32u *)&buffer[24]);

                                          //LOG_MSG("IPX: Received packet of %d bytes from %d.%d.%d.%d (%x CRC)", bufSize, CONVIP(hostaddr), packetCRC(&buffer[30], bufSize-30));
                                          useECB->NotifyESR();
                                          DeleteECB(useECB);
                                          return;
                                    }
                              }
                        }
                        if(bufoffset < bufSize) {
                              useECB->setCompletionFlag(COMP_MALFORMED);
                              useECB->NotifyESR();
                              DeleteECB(useECB);
                              return;
                        }
                  }
            }
            useECB = nextECB;
      }

}

static void sendPacketsTCP(void) {
      ECBClass *useECB;
      ECBClass *nextECB;
      char outbuffer[IPXBUFFERSIZE];
      fragmentDescriptor tmpFrag; 
      Bit16u i, fragCount,t;
      Bit16s packetsize;
      Bit16u *wordptr;
      Bits result;

      useECB = ECBList;
      while(useECB != NULL) {
            nextECB = useECB->nextECB;
            if(useECB->getInUseFlag() == USEFLAG_SENDING) {
                  useECB->setInUseFlag(USEFLAG_AVAILABLE);
                  packetsize = 0;
                  fragCount = useECB->getFragCount(); 
                  for(i=0;i<fragCount;i++) {
                        useECB->getFragDesc(i,&tmpFrag);
                        if(i==0) {
                              // Fragment containing IPX header
                              // Must put source address into header
                              Bit8u * addrptr;
                              Bits m;

                              addrptr = (Bit8u *)&localIpxAddr.netnode;
                              for(m=0;m<6;m++) {
                                    real_writeb(tmpFrag.segment,tmpFrag.offset+m+22,addrptr[m]);
                              }
                        }
                        for(t=0;t<tmpFrag.size;t++) {
                              outbuffer[packetsize] = real_readb(tmpFrag.segment, tmpFrag.offset + t);
                              packetsize++;
                              if(packetsize>=IPXBUFFERSIZE) {
                                    LOG_MSG("IPX: Packet size to be sent greater than %d bytes.", IPXBUFFERSIZE);
                                    useECB->setCompletionFlag(COMP_MALFORMED);
                                    useECB->NotifyESR();
                                    DeleteECB(useECB);
                                    goto nextECB;
                              }
                        }
                  }
                  result = SDLNet_TCP_Send(ipxTCPClientSocket, &packetsize, 2);
                  if(result != 2) {
                        useECB->setCompletionFlag(COMP_UNDELIVERABLE);
                        useECB->NotifyESR();
                        DeleteECB(useECB);
                        disconnectServer(true); 
                        return;
                  }
                  
                  // Add length and source socket to IPX header
                  wordptr = (Bit16u *)&outbuffer[0];
                  // Blank CRC
            wordptr[0] = 0xffff;
                  // Length
                  wordptr[1] = swapByte(packetsize);
                  // Source socket
                  wordptr[14] = swapByte(useECB->getSocket());

                  result = SDLNet_TCP_Send(ipxTCPClientSocket, &outbuffer[0], packetsize);
                  if(result != packetsize) {
                        useECB->setCompletionFlag(COMP_UNDELIVERABLE);
                        useECB->NotifyESR();
                        DeleteECB(useECB);
                        disconnectServer(true);
                        return;
                  }
                  useECB->setInUseFlag(USEFLAG_AVAILABLE);
                  useECB->setCompletionFlag(COMP_SUCCESS);
                  useECB->NotifyESR();
                  DeleteECB(useECB);

            }
nextECB:

            useECB = nextECB;
      }

}


static void IPX_TCPClientLoop(void) {
      Bits result;

      // Check for incoming packets
      SDLNet_CheckSockets(clientSocketSet,0);
      
      if(SDLNet_SocketReady(ipxClientSocket)) {
            if(!incomingPacket.inPacket) {
                  if(!incomingPacket.waitsize) {
                        result = SDLNet_TCP_Recv(ipxTCPClientSocket, &incomingPacket.packetSize, 2);
                        if(result!=2) {
                              if(result>0) {
                                    incomingPacket.waitsize = true;
                                    goto finishReceive;
                              } else {
                                    disconnectServer(true);
                                    goto finishReceive;
                              }
                        }
                        incomingPacket.packetRead = 0;
                  } else {
                        Bit8u * nextchar;
                        nextchar = (Bit8u *)&incomingPacket.packetSize;
                        nextchar++;
                        result = SDLNet_TCP_Recv(ipxTCPClientSocket, nextchar, 1);
                        if(result!=1) {
                              if(result>0) {
                                    LOG_MSG("IPX: Packet overrun");
                              } else {
                                    disconnectServer(true);
                                    goto finishReceive;
                              }
                        }
                        incomingPacket.waitsize = false;
                        incomingPacket.packetRead = 0;
                  }
                  incomingPacket.inPacket = true;
            }
            result = SDLNet_TCP_Recv(ipxTCPClientSocket, &incomingPacket.buffer[incomingPacket.packetRead], incomingPacket.packetSize);
            if (result>0) {
                  incomingPacket.packetRead+=result;
                  incomingPacket.packetSize-=result;
                  if(incomingPacket.packetSize<=0) {
                        // IPX packet is complete.  Now interpret IPX header and try to match to listening ECB
                        receivePacket(&incomingPacket.buffer[0], incomingPacket.packetRead);
                        incomingPacket.inPacket = false;
                  }
            } else {
                  // Clost active socket
                  disconnectServer(true);
            }

      }

finishReceive:;


}

static void IPX_UDPClientLoop(void) {
      int numrecv;
      UDPpacket inPacket;
      inPacket.data = (Uint8 *)recvBuffer;
      inPacket.maxlen = IPXBUFFERSIZE;
      inPacket.channel = UDPChannel;

      // Its amazing how much simpler UDP is than TCP
      numrecv = SDLNet_UDP_Recv(ipxClientSocket, &inPacket);
      if(numrecv) {
            receivePacket(inPacket.data, inPacket.len);
      }
      
}

static void sendPackets() {
      ECBClass *useECB;
      ECBClass *nextECB;
      char outbuffer[IPXBUFFERSIZE];
      fragmentDescriptor tmpFrag; 
      Bit16u i, fragCount,t;
      Bit16s packetsize;
      Bit16u *wordptr;
      Bits result;
      UDPpacket outPacket;

      useECB = ECBList;
      while(useECB != NULL) {
            nextECB = useECB->nextECB;
            if(useECB->getInUseFlag() == USEFLAG_SENDING) {
                  useECB->setInUseFlag(USEFLAG_AVAILABLE);
                  packetsize = 0;
                  fragCount = useECB->getFragCount(); 
                  for(i=0;i<fragCount;i++) {
                        useECB->getFragDesc(i,&tmpFrag);
                        if(i==0) {
                              // Fragment containing IPX header
                              // Must put source address into header
                              Bit8u * addrptr;
                              Bits m;

                              addrptr = (Bit8u *)&localIpxAddr.netnode;
                              for(m=0;m<6;m++) {
                                    real_writeb(tmpFrag.segment,tmpFrag.offset+m+22,addrptr[m]);
                              }
                        }
                        for(t=0;t<tmpFrag.size;t++) {
                              outbuffer[packetsize] = real_readb(tmpFrag.segment, tmpFrag.offset + t);
                              packetsize++;
                              if(packetsize>=IPXBUFFERSIZE) {
                                    LOG_MSG("IPX: Packet size to be sent greater than %d bytes.", IPXBUFFERSIZE);
                                    useECB->setCompletionFlag(COMP_UNDELIVERABLE);
                                    useECB->NotifyESR();
                                    DeleteECB(useECB);
                                    goto nextECB;
                              }
                        }
                  }
                  
                  // Add length and source socket to IPX header
                  wordptr = (Bit16u *)&outbuffer[0];
                  // Blank CRC
            wordptr[0] = 0xffff;
                  // Length
                  wordptr[1] = swapByte(packetsize);
                  // Source socket
                  wordptr[14] = swapByte(useECB->getSocket());

                  outPacket.channel = UDPChannel;
                  outPacket.data = (Uint8 *)&outbuffer[0];
                  outPacket.len = packetsize;
                  outPacket.maxlen = packetsize;
                  // Since we're using a channel, we won't send the IP address again
                  result = SDLNet_UDP_Send(ipxClientSocket, UDPChannel, &outPacket);
                  if(result == 0) {
                        LOG_MSG("IPX: Could not send packet: %s", SDLNet_GetError());
                        useECB->setCompletionFlag(COMP_UNDELIVERABLE);
                        useECB->NotifyESR();
                        DeleteECB(useECB);
                        disconnectServer(true);
                        return;
                  }
                  useECB->setInUseFlag(USEFLAG_AVAILABLE);
                  useECB->setCompletionFlag(COMP_SUCCESS);
                  useECB->NotifyESR();
                  DeleteECB(useECB);

            }
nextECB:

            useECB = nextECB;
      }

}

static void IPX_ClientLoop(void) {
      IPX_UDPClientLoop();

      // Send outgoing packets
      sendPackets();
}


static bool pingCheck(IPXHeader * outHeader) {
      char buffer[1024];
      Bits result;
      UDPpacket regPacket;

      IPXHeader *regHeader;
      regPacket.data = (Uint8 *)buffer;
      regPacket.maxlen = sizeof(buffer);
      regPacket.channel = UDPChannel;
      regHeader = (IPXHeader *)buffer;
      
      result = SDLNet_UDP_Recv(ipxClientSocket, &regPacket);
      if (result != 0) {
            memcpy(outHeader, regHeader, sizeof(IPXHeader));
            return true;
      }
      return false;


}

bool ConnectToServer(char *strAddr) {
      int numsent;
      UDPpacket regPacket;
      IPXHeader regHeader;

      if(!SDLNet_ResolveHost(&ipxServConnIp, strAddr, (Bit16u)tcpPort)) {
            
            // Select an anonymous UDP port
            ipxClientSocket = SDLNet_UDP_Open(0);
            if(ipxClientSocket) {
                  // Bind UDP port to address to channel
                  UDPChannel = SDLNet_UDP_Bind(ipxClientSocket,-1,&ipxServConnIp);
                  //ipxClientSocket = SDLNet_TCP_Open(&ipxServConnIp);
                  SDLNet_Write16(0xffff, regHeader.checkSum);
                  SDLNet_Write16(sizeof(regHeader), regHeader.length);

                  // Echo packet with zeroed dest and src is a server registration packet
                  SDLNet_Write32(0, regHeader.dest.network);
                  regHeader.dest.addr.byIP.host = 0x0;
                  regHeader.dest.addr.byIP.port = 0x0;
                  SDLNet_Write16(0x2, regHeader.dest.socket);

                  SDLNet_Write32(0, regHeader.src.network);
                  regHeader.src.addr.byIP.host = 0x0;
                  regHeader.src.addr.byIP.port = 0x0;
                  SDLNet_Write16(0x2, regHeader.src.socket);
                  regHeader.transControl = 0;

                  regPacket.data = (Uint8 *)&regHeader;
                  regPacket.len = sizeof(regHeader);
                  regPacket.maxlen = sizeof(regHeader);
                  regPacket.channel = UDPChannel;
                  // Send registration string to server.  If server doesn't get this, client will not be registered
                  numsent = SDLNet_UDP_Send(ipxClientSocket, regPacket.channel, &regPacket);
                  
                  if(!numsent) {
                        LOG_MSG("IPX: Unable to connect to server: %s", SDLNet_GetError());
                        SDLNet_UDP_Close(ipxClientSocket);
                        return false;
                  } else {
                        // Wait for return packet from server.  This will contain our IPX address and port num
                        Bits result;
                        Bit32u ticks, elapsed;
                        ticks = GetTicks();

                        while(true) {
                              elapsed = GetTicks() - ticks;
                              if(elapsed > 5000) {
                                    LOG_MSG("Timeout connecting to server at %s", strAddr);
                                    SDLNet_UDP_Close(ipxClientSocket);

                                    return false;
                              }
                              CALLBACK_Idle();
                              result = SDLNet_UDP_Recv(ipxClientSocket, &regPacket);
                              if (result != 0) {
                                    memcpy(localIpxAddr.netnode, regHeader.dest.addr.byNode.node, sizeof(localIpxAddr.netnode));
                                    memcpy(localIpxAddr.netnum, regHeader.dest.network, sizeof(localIpxAddr.netnum));
                                    break;
                              }
                              
                        }

                        LOG_MSG("IPX: Connected to server.  IPX address is %d:%d:%d:%d:%d:%d", CONVIPX(localIpxAddr.netnode));


                        incomingPacket.connected = true;
                        TIMER_AddTickHandler(&IPX_ClientLoop);
                        return true;
                  }
            } else {
                  LOG_MSG("IPX: Unable to open socket");

            }
      } else {
            LOG_MSG("IPX: Unable resolve connection to server");
      }
      return false;
}

void DisconnectFromServer(void) {

      if(incomingPacket.connected) {
            incomingPacket.connected = false;
            TIMER_DelTickHandler(&IPX_ClientLoop);
            SDLNet_UDP_Close(ipxClientSocket);
      }
}

bool IPX_NetworkInit() {

      localIpxAddr.netnum[0] = 0x0; localIpxAddr.netnum[1] = 0x0; localIpxAddr.netnum[2] = 0x0; localIpxAddr.netnum[3] = 0x1;

      /*
      if(SDLNet_ResolveHost(&ipxClientIp, localhostname, tcpPort)) {
            LOG_MSG("IPX: Unable to resolve localname: \"%s\".  IPX disabled.", localhostname);
            return false;
      } else {
            LOG_MSG("IPX: Using localname: %s IP is: %d.%d.%d.%d", localhostname, CONVIP(ipxClientIp.host));
      }
      */

      // Generate the MAC address.  This is made by zeroing out the first two octets and then using the actual IP address for
      // the last 4 octets.  This idea is from the IPX over IP implementation as specified in RFC 1234:
      // http://www.faqs.org/rfcs/rfc1234.html
      localIpxAddr.netnode[0] = 0x00;
      localIpxAddr.netnode[1] = 0x00;
      //localIpxAddr.netnode[5] = (ipxClientIp.host >> 24) & 0xff;
      //localIpxAddr.netnode[4] = (ipxClientIp.host >> 16) & 0xff;
      //localIpxAddr.netnode[3] = (ipxClientIp.host >> 8) & 0xff;
      //localIpxAddr.netnode[2] = (ipxClientIp.host & 0xff);
      //To be filled in on response from server
      localIpxAddr.netnode[2] = 0x00;
      localIpxAddr.netnode[3] = 0x00;
      localIpxAddr.netnode[4] = 0x00;
      localIpxAddr.netnode[5] = 0x00;

      socketCount = 0;
      return true;
}

class IPXNET : public Program {
public:
      void HelpCommand(const char *helpStr) {
            // Help on connect command
            if(strcasecmp("connect", helpStr) == 0) {
                  WriteOut("IPXNET CONNECT opens a connection to an IPX tunneling server running on another\n");
                  WriteOut("DosBox session.  The \"address\" parameter specifies the IP address or host name\n");
                  WriteOut("of the server computer.  One can also specify the UDP port to use.  By default\n");
                  WriteOut("IPXNET uses port 213, the assigned IANA port for IPX tunneling, for its\nconnection.\n\n");
                  WriteOut("The syntax for IPXNET CONNECT is:\n\n");
                  WriteOut("IPXNET CONNECT address <port>\n\n");
                  return;
            }
            // Help on the disconnect command
            if(strcasecmp("disconnect", helpStr) == 0) {
                  WriteOut("IPXNET DISCONNECT closes the connection to the IPX tunneling server.\n\n");
                  WriteOut("The syntax for IPXNET DISCONNECT is:\n\n");
                  WriteOut("IPXNET DISCONNECT\n\n");
                  return;
            }
            // Help on the startserver command
            if(strcasecmp("startserver", helpStr) == 0) {
                  WriteOut("IPXNET STARTSERVER starts and IPX tunneling server on this DosBox session.  By\n");
                  WriteOut("default, the server will accept connections on UDP port 213, though this can be\n");
                  WriteOut("changed.  Once the server is started, DosBox will automatically start a client\n");
                  WriteOut("connection to the IPX tunneling server.\n\n");
                  WriteOut("The syntax for IPXNET STARTSERVER is:\n\n");
                  WriteOut("IPXNET STARTSERVER <port>\n\n");
                  return;
            }
            // Help on the stop server command
            if(strcasecmp("stopserver", helpStr) == 0) {
                  WriteOut("IPXNET STOPSERVER stops the IPX tunneling server running on this DosBox\nsession.");
                  WriteOut("  Care should be taken to ensure that all other connections have\nterminated ");
                  WriteOut("as well sinnce stoping the server may cause lockups on other\nmachines still using ");
                  WriteOut("the IPX tunneling server.\n\n");
                  WriteOut("The syntax for IPXNET STOPSERVER is:\n\n");
                  WriteOut("IPXNET STOPSERVER\n\n");
                  return;
            }
            // Help on the ping command
            if(strcasecmp("ping", helpStr) == 0) {
                  WriteOut("IPXNET PING broadcasts a ping request through the IPX tunneled network.  In    \n");
                  WriteOut("response, all other connected computers will respond to the ping and report\n");
                  WriteOut("the time it took to receive and send the ping message.\n\n");
                  WriteOut("The syntax for IPXNET PING is:\n\n");
                  WriteOut("IPXNET PING\n\n");
                  return;
            }
            // Help on the status command
            if(strcasecmp("status", helpStr) == 0) {
                  WriteOut("IPXNET STATUS reports the current state of this DosBox's sessions IPX tunneling\n");
                  WriteOut("network.  For a list of the computers connected to the network use the IPXNET \n");
                  WriteOut("PING command.\n\n");
                  WriteOut("The syntax for IPXNET STATUS is:\n\n");
                  WriteOut("IPXNET STATUS\n\n");
                  return;
            }
      }

      void Run(void)
      {
            WriteOut("IPX Tunneling utility for DosBox\n\n");
            if(!cmd->GetCount()) {
                  WriteOut("The syntax of this command is:\n\n");
                  WriteOut("IPXNET [ CONNECT | DISCONNECT | STARTSERVER | STOPSERVER | PING | HELP |\n         STATUS ]\n\n");
                  return;
            }
            
            if(cmd->FindCommand(1, temp_line)) {
                  if(strcasecmp("help", temp_line.c_str()) == 0) {
                        if(!cmd->FindCommand(2, temp_line)) {
                              WriteOut("The following are valid IPXNET commands:\n\n");
                              WriteOut("IPXNET CONNECT        IPXNET DISCONNECT       IPXNET STARTSERVER\n");
                              WriteOut("IPXNET STOPSERVER     IPXNET PING             IPXNET STATUS\n\n");
                              WriteOut("To get help on a specific command, type:\n\n");
                              WriteOut("IPXNET HELP command\n\n");

                        } else {
                              HelpCommand(temp_line.c_str());
                              return;
                        }
                        return;
                  } 
                  if(strcasecmp("startserver", temp_line.c_str()) == 0) {
                        if(!isIpxServer) {
                              if(incomingPacket.connected) {
                                    WriteOut("IPX Tunneling Client alreadu connected to another server.  Disconnect first.\n");
                                    return;
                              }
                              bool startsuccess;
                              if(!cmd->FindCommand(2, temp_line)) {
                                    tcpPort = 213;
                              } else {
                                    tcpPort = strtol(temp_line.c_str(), NULL, 10);
                              }
                              startsuccess = IPX_StartServer((Bit16u)tcpPort);
                              if(startsuccess) {
                                    WriteOut("IPX Tunneling Server started\n");
                                    isIpxServer = true;
                                    ConnectToServer("localhost");
                              } else {
                                    WriteOut("IPX Tunneling Server failed to start\n");
                              }
                        } else {
                              WriteOut("IPX Tunneling Server already started\n");
                        }
                        return;
                  }
                  if(strcasecmp("stopserver", temp_line.c_str()) == 0) {
                        if(!isIpxServer) {
                              WriteOut("IPX Tunneling Server not running in this DosBox session.\n");
                        } else {
                              isIpxServer = false;
                              DisconnectFromServer();
                              IPX_StopServer();
                              WriteOut("IPX Tunneling Server stopped.");
                              // Don't know how to stop the timer just yet.
                        }
                        return;
                  }
                  if(strcasecmp("connect", temp_line.c_str()) == 0) {
                        char strHost[1024];
                        if(incomingPacket.connected) {
                              WriteOut("IPX Tunneling Client already connected.\n");
                              return;
                        }
                        if(!cmd->FindCommand(2, temp_line)) {
                              WriteOut("IPX Server address not specified.\n");
                              return;
                        }
                        strcpy(strHost, temp_line.c_str());

                        if(!cmd->FindCommand(3, temp_line)) {
                              tcpPort = 213;
                        } else {
                              tcpPort = strtol(temp_line.c_str(), NULL, 10);
                        }

                        if(ConnectToServer(strHost)) {
                  WriteOut("IPX Tunneling Client connected to server at %s.\n", strHost);
                        } else {
                              WriteOut("IPX Tunneling Client failed to connect to server at %s.\n", strHost);
                        }
                        return;
                  }
                  
                  if(strcasecmp("disconnect", temp_line.c_str()) == 0) {
                        if(!incomingPacket.connected) {
                              WriteOut("IPX Tunneling Client not connected.\n");
                              return;
                        }
                        // TODO: Send a packet to the server notifying of disconnect

                        WriteOut("IPX Tunneling Client disconnected from server.\n");
                        DisconnectFromServer();
                        return;
                  }

                  if(strcasecmp("status", temp_line.c_str()) == 0) {
                        WriteOut("IPX Tunneling Status:\n\n");
                        WriteOut("Server status: ");
                        if(isIpxServer) WriteOut("ACTIVE\n"); else WriteOut("INACTIVE\n");
                        WriteOut("Client status: ");
                        if(incomingPacket.connected) {
                              WriteOut("CONNECTED -- Server at %d.%d.%d.%d port %d\n", CONVIP(ipxServConnIp.host), tcpPort);
                        } else {
                              WriteOut("DISCONNECTED\n");
                        }
                        if(isIpxServer) {
                              WriteOut("List of active connections:\n\n");
                              int i;
                              IPaddress *ptrAddr;
                              for(i=0;i<SOCKETTABLESIZE;i++) {
                                    if(IPX_isConnectedToServer(i,&ptrAddr)) {
                                          WriteOut("     %d.%d.%d.%d from port %d\n", CONVIP(ptrAddr->host), SDLNet_Read16(&ptrAddr->port));
                                    }
                              }
                              WriteOut("\n");
                        }
                        return;
                  }

                  if(strcasecmp("ping", temp_line.c_str()) == 0) {
                        Bit32u ticks;
                        IPXHeader pingHead;

                        if(!incomingPacket.connected) {
                              WriteOut("IPX Tunneling Client not connected.\n");
                              return;
                        }

                        WriteOut("Sending broadcast ping:\n\n");
                        pingSend();
                        ticks = GetTicks();
                        while((GetTicks() - ticks) < 1500) {
                              CALLBACK_Idle();
                              if(pingCheck(&pingHead)) {
                                    WriteOut("Response from %d.%d.%d.%d, port %d time=%dms\n", CONVIP(pingHead.src.addr.byIP.host), SDLNet_Read16(&pingHead.src.addr.byIP.port), GetTicks() - ticks);
                              }
                        }
                        return;
                  }



            }

            /*
            WriteOut("IPX Status\n\n");
            if(!incomingPacket.connected) {
                  WriteOut("IPX tunneling client not presently connected");
                  return;
            }
            if(isIpxServer) {
                  WriteOut("This DosBox session is an IPX tunneling server running on port %d", tcpPort);
            } else {
                  WriteOut("This DosBox session is an IPX tunneling client connected to: %s",SDLNet_ResolveIP(&ipxServConnIp));
            }
            */
      }
};

static void IPXNET_ProgramStart(Program * * make) {
      *make=new IPXNET;
}

Bitu IPX_ESRHandler1(void) {
      CPU_Push32(reg_flags);
      CPU_Push32(reg_eax);CPU_Push32(reg_ecx);CPU_Push32(reg_edx);CPU_Push32(reg_ebx);
      CPU_Push32(reg_ebp);CPU_Push32(reg_esi);CPU_Push32(reg_edi);
      CPU_Push16(SegValue(ds)); CPU_Push16(SegValue(es)); 

      SegSet16(es, RealSeg(processedECB));
      reg_si = RealOff(processedECB);
      reg_al = 0xff;
      //LOG_MSG("ESR Callback 1");

      return CBRET_NONE;
}

Bitu IPX_ESRHandler2(void) {
      SegSet16(es, CPU_Pop16()); SegSet16(ds, CPU_Pop16());
      reg_edi=CPU_Pop32();reg_esi=CPU_Pop32();reg_ebp=CPU_Pop32();
      reg_ebx=CPU_Pop32();reg_edx=CPU_Pop32();reg_ecx=CPU_Pop32();reg_eax=CPU_Pop32();
      reg_flags=CPU_Pop32();

      //LOG_MSG("Leaving ESR");
      inESR = false;

      return CBRET_NONE;
}

bool IPX_ESRSetupHook(Bitu callback1, CallBack_Handler handler1, Bitu callback2, CallBack_Handler handler2, RealPt *ptrAddr) {
      PhysPt phyDospage;
      phyDospage = PhysMake(dospage,0);

      // Inital callback routine (should save registers, etc.)
      phys_writeb(phyDospage+0,(Bit8u)0xFA);    //CLI
      phys_writeb(phyDospage+1,(Bit8u)0xFE);    //GRP 4
      phys_writeb(phyDospage+2,(Bit8u)0x38);    //Extra Callback instruction
      phys_writew(phyDospage+3,callback1);            //The immediate word
      phys_writeb(phyDospage+5,(Bit8u)0x9a);    //CALL Ap
      // 0x6, 0x7, 0x8, 0x9 = address of called routine
      *ptrAddr = RealMake(dospage, 6);
      phys_writed(phyDospage+6,(Bit32u)0x00000000); // Called address
      phys_writeb(phyDospage+0xa,(Bit8u)0xFE);  //GRP 4
      phys_writeb(phyDospage+0xb,(Bit8u)0x38);  //Extra Callback instruction
      phys_writew(phyDospage+0xc,callback2);    //The immediate word
      phys_writeb(phyDospage+0xe,(Bit8u)0xFB);  //STI
      phys_writeb(phyDospage+0xf,(Bit8u)0xCB);  //A RETF Instruction

      CallBack_Handlers[callback1]=handler1;
      CallBack_Handlers[callback2]=handler2;
      return true;
}

void IPX_Init(Section* sec) {
      Section_prop * section=static_cast<Section_prop *>(sec);

      if(!section->Get_bool("ipx")) return;

      if(!SDLNetInited) {
            if(SDLNet_Init()==-1) {
                  LOG_MSG("SDLNet_Init failed: %s\n", SDLNet_GetError());
                  return;
            }
            SDLNetInited = true;
      }

      ECBList = NULL;

      isIpxServer = false;
      isIpxConnected = false;

      IPX_NetworkInit();

      inESR = false;

      DOS_AddMultiplexHandler(IPX_Multiplex);
      call_ipx=CALLBACK_Allocate();
      CALLBACK_Setup(call_ipx,&IPX_Handler,CB_RETF);
      ipx_callback=CALLBACK_RealPointer(call_ipx);

      call_ipxint=CALLBACK_Allocate();    
      CALLBACK_Setup(call_ipxint,&IPX_IntHandler,CB_IRET);
      ipx_intcallback=CALLBACK_RealPointer(call_ipxint);

      call_ipxesr1=CALLBACK_Allocate();   
      call_ipxesr2=CALLBACK_Allocate();   
      //CALLBACK_SetupFarCall(call_ipxesr1, &IPX_ESRHandler1, call_ipxesr2, &IPX_ESRHandler2, &ipx_esrptraddr);
      dospage = DOS_GetMemory(1);
      IPX_ESRSetupHook(call_ipxesr1, &IPX_ESRHandler1, call_ipxesr2, &IPX_ESRHandler2, &ipx_esrptraddr);
      // Allocate 16 bytes of memory from the DOS system area at 0xd000
      ipx_esrcallback=RealMake(dospage,0);
      //CALLBACK_RealPointer(call_ipxesr1);

      RealSetVec(0x7a,ipx_intcallback);
      PROGRAMS_MakeFile("IPXNET.COM",IPXNET_ProgramStart);

      //if(isIpxServer) {
            // Auto-connect to server
      //    ConnectToServer("localhost");
      //}

}

#endif

Generated by  Doxygen 1.6.0   Back to index