// ===================================================
// Листинг 10. Include-файл для работы с IPX
// Файл ipx.hpp
//
// (C) A. Frolov, 1993
// ===================================================

#include <mem.h>
#include <dos.h>

// -----------------------
// Команды интерфейса IPX
// -----------------------

#define IPX_CMD_OPEN_SOCKET			0x00
#define IPX_CMD_CLOSE_SOCKET			0x01
#define IPX_CMD_GET_LOCAL_TARGET		0x02
#define IPX_CMD_SEND_PACKET			0x03
#define IPX_CMD_LISTEN_FOR_PACKET		0x04
#define IPX_CMD_SCHEDULE_IPX_EVENT		0x05
#define IPX_CMD_CANCEL_EVENT			0x06
#define IPX_CMD_GET_INTERVAL_MARKER		0x08
#define IPX_CMD_GET_INTERNETWORK_ADDRESS	0x09
#define IPX_CMD_RELINQUISH_CONTROL		0x0a
#define IPX_CMD_DISCONNECT_FROM_TARGET		0x0b

// -----------------------
// Коды ошибок
// -----------------------

#define NO_ERRORS		0
#define ERR_NO_IPX		1
#define ERR_NO_SPX		2
#define NO_LOGGED_ON		3
#define UNKNOWN_ERROR 	0xff

// -----------------------
// Константы
// -----------------------

#define SHORT_LIVED	0
#define LONG_LIVED	0xff
#define IPX_DATA_PACKET_MAXSIZE	546

// Максимальный размер буфера данных

#define BUFFER_SIZE 512

// Внешние процедуры для инициализации
// и вызова драйвера IPX/SPX

extern "C" void far    ipxspx_entry(void far *ptr);
extern "C" int         ipx_init(void);
extern unsigned        IntSwap(unsigned i);

void IPXRelinquishControl(void);

// Структура для вызова драйвера IPX/SPX

struct IPXSPX_REGS {
		unsigned int	ax;
		unsigned int	bx;
		unsigned int	cx;
		unsigned int	dx;
		unsigned int	si;
		unsigned int	di;
		unsigned int	es;
};

// Класс динамических сокетов

class DYNAMIX_SOCKET {
public:
	unsigned errno;
	unsigned Socket;
	struct IPXSPX_REGS iregs;

// Конструктор динамического сокета.
// Открывает сокет и запоминает его номер.

	DYNAMIX_SOCKET() {
		iregs.bx = IPX_CMD_OPEN_SOCKET;
		iregs.dx = 0;
		iregs.ax = 0;
		ipxspx_entry( (void far *)&iregs );
		Socket = iregs.dx;
		errno = iregs.ax;
	};

// Деструктор. Закрывает ранее открытый сокет.

	~DYNAMIX_SOCKET() {
		iregs.bx = IPX_CMD_CLOSE_SOCKET;
		iregs.dx = Socket;
		ipxspx_entry( (void far *)&iregs );
	};

};

// Класс программ-клиентов IPX

class IPX_CLIENT {
public:

	unsigned errno;

// Сокет, с которым работает программа-клиент

	DYNAMIX_SOCKET *Socket;

// Конструктор. Выполняет инициализацию клиента:
// инициализирует драйвер IPX и открывает
// динамический сокет.

	IPX_CLIENT() {
		if(ipx_init() != 0xff) {
			errno = 0xff; return;
		}
		Socket = new DYNAMIX_SOCKET;
	}

// Деструктор. Автоматически закрывает
// сокет при завершении работы программы.

	~IPX_CLIENT() {
		delete Socket;
	}

// Функция, определяющая конфигурацию сети

	void Go(void);
};

// Класс заголовков IPX-пакетов.

struct IPX_HEADER {

// Структура, описывающая заголовок

	struct _IPX_HEADER {
		unsigned int	Checksum;
		unsigned int	Length;
		unsigned char	TransportControl;
		unsigned char	PacketType;
		unsigned char	DestNetwork[4];
		unsigned char	DestNode[6];
		unsigned int	DestSocket;
		unsigned char	SourceNetwork[4];
		unsigned char	SourceNode[6];
		unsigned int	SourceSocket;
	} _ipx_header;

// Конструктор. Записывает в заголовок тип пакета,
// нулевой номер сети, в которую будет отправлен пакет,
// адрес 0xFFFFFFFFFFFF в качестве адреса назначения,
// номера сокетов адресата и отправителя пакета,

	IPX_HEADER(unsigned Socket, unsigned SrcSocket) {
		_ipx_header.PacketType = 4;
		memset(_ipx_header.DestNetwork, 0, 4);
		memset(_ipx_header.DestNode, 0xff, 6);
		_ipx_header.DestSocket = Socket;
		_ipx_header.SourceSocket = SrcSocket;
	}

// Конструктор. Записывает в заголовок тип пакета,
// нулевой номер сети, в которую будет отправлен пакет,
// адрес 0xFFFFFFFFFFFF в качестве адреса назначения.

	IPX_HEADER() {
		_ipx_header.PacketType = 4;
		memset(_ipx_header.DestNetwork, 0, 4);
		memset(_ipx_header.DestNode, 0xff, 6);
	}
};

// Класс блоков ECB.

struct ECB {

// Сам блок ECB в стандарте IPX/SPX.

		struct _ECB {
			void far	*Link;
			void far	(*ESRAddress)(void);
			unsigned char	InUse;
			unsigned char	CCode;
			unsigned int	Socket;
			unsigned int	ConnectionId;
			unsigned int	RrestOfWorkspace;
			unsigned char	DriverWorkspace[12];
			unsigned char	ImmAddress[6];
			unsigned int	FragmentCnt;
			struct {
				void far	*Address;
				unsigned int Size;
			} Packet[2];
		} _ecb;

// Указатель на заголовок пакета,
// связанного с данным ECB.

		struct IPX_HEADER *IPXHeader;

// Структура для приема ответа от станции
// после посылки диагностического пакета.

		struct Reply {
			unsigned char MajVer;
			unsigned char MinVer;
			unsigned Socket;
			unsigned char NumberOfComponents;
			unsigned char Buffer[512];
		} Rep;

// Структура для хранения диагностического
// пакета.

		struct DiagnRequest {
			unsigned char Exclusions;
			unsigned char List[80][6];
		} DReq;

		struct IPXSPX_REGS iregs;

// Конструктор. Создается заголовок пакета,
// в блок ECB записывается номер сокета,
// используемого клиентом, инициализируется
// счетчик фрагментов и дескрипторы фрагментов.
// В качестве непосредственного адреса указывается
// адрес 0xFFFFFFFFFFFF.

		ECB(DYNAMIX_SOCKET *Socket) {
			IPXHeader = new IPX_HEADER;
			memset(&_ecb, 0, sizeof(_ecb));
			_ecb.Socket = Socket->Socket;
			_ecb.FragmentCnt       = 2;
			_ecb.Packet[0].Address = &(IPXHeader->_ipx_header);
			_ecb.Packet[0].Size    = 30;
			_ecb.Packet[1].Address = &Rep;
			_ecb.Packet[1].Size    = sizeof(Rep);
			memset(_ecb.ImmAddress, 0xff, 6);
		}

// Конструктор. Создается заголовок пакета,
// в блок ECB записывается номер сокета,
// используемого клиентом, а также номер
// сокета адресата, инициализируется
// счетчик фрагментов и дескрипторы фрагментов.
// В качестве непосредственного адреса указывается
// адрес 0xFFFFFFFFFFFF.

		ECB(DYNAMIX_SOCKET *Socket, unsigned DstSocket) {

			IPXHeader = new IPX_HEADER(IntSwap(DstSocket),
				 Socket->Socket);

// Запрос адресуется всем станциям
// без исключения.

			DReq.Exclusions = 0;

			memset(&_ecb, 0, sizeof(_ecb));
			_ecb.Socket = Socket->Socket;
			_ecb.FragmentCnt       = 2;
			_ecb.Packet[0].Address = &(IPXHeader->_ipx_header);
			_ecb.Packet[0].Size    = 30;
			_ecb.Packet[1].Address = &DReq;
			_ecb.Packet[1].Size    = sizeof(DReq);
			memset(_ecb.ImmAddress, 0xff, 6);
		}

// Прием IPX-пакета.

		void ListenForPacket(void) {
			iregs.es = FP_SEG((void far*)&_ecb);
			iregs.si = FP_OFF((void far*)&_ecb);
			iregs.bx = IPX_CMD_LISTEN_FOR_PACKET;
			ipxspx_entry( (void far *)&iregs );
		}

// Передача IPX-пакета.

		void SendPacket(void) {
			iregs.es = FP_SEG((void far*)&_ecb);
			iregs.si = FP_OFF((void far*)&_ecb);
			iregs.bx = IPX_CMD_SEND_PACKET;
			ipxspx_entry( (void far *)&iregs );
		}

// Распечатать принятый пакет конфигурации

		void PrintDiagnostics(void);
};

