; LANLIB.TXT -- Documentation for LANLIB
; Copyright (c) 1994 by Allen Brunson  version 1.00  06/30/94


******************************************************************************
*                                                                            *
***  Trademarks                                                            ***
*                                                                            *
******************************************************************************

Borland is a registered trademark of Borland International, Inc.

Microsoft and MS-DOS are registered trademarks and Windows is a trademark
of Microsoft Corporation.

Netware and Novell are registered trademarks and IPX is a trademark
of Novell, Inc.



******************************************************************************
*                                                                            *
***  Introduction                                                          ***
*                                                                            *
******************************************************************************

LANLIB is a library of routines written in assembler for communication
amongst multiple PCs using the IPX protocol, the low-level language spoken
by Novell's NetWare.  It is designed to be called from C, but if you know
what you're doing, you can get it to work with assembler, C++, Pascal, or
any other language that can be made to support the C calling convention and
works with standard object file formats.  Since LANLIB is written in
assembler, its impact on the size of your program is minimal; it has only
2.8k of code and requires about 12k of memory for communication buffers.

IPX is a fast and very low-level protocol, designed for maximum speed.  It
does not guarantee delivery, but Novell claims IPX packets properly arrive
at their destinations about 95 percent of the time.

IPX can be quite cumbersome to use.  Given its orientation, a great many
details of NetWare networks must be known by the programmer to use it
properly.  This library takes care of almost all of those details, allowing
you to concentrate on your program's main function rather than worrying
about IPX specifics.

This library is geared towards the needs of smaller projects, where perhaps
up to 20 or so PCs will be exchanging data.  Larger projects would probably
be best served if the programmer were to learn how to make IPX calls
directly or by some other product.

This library does not offer any NetWare-related services, such as logging in,
logging out, copying files, getting lists of logged-on users, and so on.
Its only purpose is to allow programs running on multiple PCs to share data.

This document covers issues specific to using LANLIB.  It assumes that you
are familiar with NetWare and IPX concepts.  It would be a good idea to
read NETWORK.TXT before tackling this file.

The file LANTEST.TXT explains the LANLIB demo program, LANTEST.  LANTEST
uses every LANLIB routine, so it is a good reference for implementation
details.


Shareware Version Restrictions
------------------------------

The shareware version of LANLIB does everything that the registered version
does, with one exception: the procedure ipxRouteFind() has been removed,
so the shareware version is limited to only one network segment.

The shareware version is provided for evaluation purposes only.  You are
NOT free to distribute programs you create with it.



******************************************************************************
*                                                                            *
***  Adding LANLIB To Your Program                                         ***
*                                                                            *
******************************************************************************

LANLIB has been tested with both Microsoft and Borland C++ compilers.  One
small snag is that the Microsoft C++ compiler won't be able to use the Tiny
model version of LANLIB; its linker reports that far segment accesses are not
allowed in that model.  Other compilers will probably work if they can deal
with standard object files.

LANLIB's use is limited to real-mode DOS executables.  I am working on a
version for the Watcom 32-bit compiler, but at this writing I don't have a
release date.

Novell's documented method for determining the presence of IPX requires the
use of the DOS multiplex interrupt, which was introduced with DOS version
3.0.  Therefore, LANLIB will not work with any DOS version earlier than 3.0.

Special note for users of Microsoft compilers: Microsoft C defaults to
two-byte (one word) structure member alignment.  This will NOT WORK with
LANLIB, which uses one-byte aligned structures.  (It HAS to; IPX itself
uses one-byte aligned structures.)  You must set the compiler to one-byte
structure alignment for it to work with LANLIB.

LANLIB is contained in one of four .LIB files, depending on your chosen
memory model:

  LANLIBT.LIB   Tiny model
 LANLIBSC.LIB   Small and Compact models
 LANLIBML.LIB   Medium and Large models
  LANLIBH.LIB   Huge model

Include one of these .LIB files in your project.

LANLIB always uses far pointers for passed data parameters, so there's no
distinction between Small and Compact (near code calls) or between Medium and
Large (far code calls).  The Tiny model code never makes direct references to
the data segment, so a program built with LANLIBT.LIB can be turned into a
.COM file.  The Huge memory model has special considerations for the data
segment register, so it requires its own .LIB file.

Include the file LANLIB.H in any module that will be calling the LANLIB
routines.  It is important that you follow this rule: LANLIB routines all
take far pointers to data structures, and pointers in the small data
memory models are by default near, so messy crashes will result if you don't
perform this step.

Defines in LANCFG.H are used to set the number of send and receive packet
buffers, the number of bytes per packet, the socket number, and the number
of networks to allow.  Make a copy of this file for each program that will
use LANLIB and then include LANCFG.H in each source file that will call
LANLIB routines.  See a later section called "Configuring LANLIB With Defines
in LANCFG.H" for more details.

The routines in LANUTIL.C can be used to work with IPXADDRFULL structures.
While I was writing the demo program I had to manage the addresses of many
PCs; each requires an IPXADDRFULL structure.  Since this is a large
hunk of data with several unwieldy fields, I wrote routines to copy one
address to another, convert an address to a string, compare two addresses for
equality, and so on.  LANUTIL.C is the result.  See a later section in this
file for reference.



******************************************************************************
*                                                                            *
***  Calling LANLIB from Assembler                                         ***
*                                                                            *
******************************************************************************

While LANLIB was being beta-tested, I was contacted by a programmer who was
interested in using it with a pure assembler program.  This is entirely
possible; LANLIB is written in assembler itself.  I've included the file
LANLIB.INC, which contains the same defines as LANLIB.H but in assembler
format, and LANCFG.INC, which duplicates LANCFG.H.

All the LANLIB procedures use the C calling convention so you must do the
same when calling them from assembler.  Each procedure's arguments are
pushed on the stack in right-to-left order; after the call, you must get rid
of them by POPing them off (or by adding a value to SP, the stack pointer).
If a procedure returns a value, it will be in AX on return (this is standard
C procedure).  Since C compilers don't require procedures to save many
registers, the LANLIB procedures trash most of them.  All registers except
BP, SP, SI, DI, SS, and DS might be trashed.

You won't be able to use the utility routines in LANUTIL.C in an assembler
program, of course.  But their use is optional; you may discover you don't
need them; and if you do, they are all quite simple and easy to port to
assembler.



******************************************************************************
*                                                                            *
***  An Overview Of LANLIB Structures                                      ***
*                                                                            *
******************************************************************************

You don't need to know a whole lot about IPX structures to use LANLIB; it
pretty much takes care of the details for you.  However, a general overview
of the seven structures defined in LANLIB.H (and LANLIB.INC) seems
appropriate.

struct IPXADDR: This is the network, node, and socket number for a PC.  It
is enough to uniquely identify a process on a PC, but not enough info to
be able to send a packet to any process on any PC anywhere on an
internetwork.  Note that the socket field is high-low (Motorola) format.
This structure is defined only for inclusion in the IPXPKTHDR structure.

struct IPXADDRFULL: This is a superset of the IPXADDR structure, adding an
immediate address field.  Therefore it contains enough address info to send
a packet anywhere.  I kind of "made up" this structure; nowhere in "official"
IPX do you find all four of these fields grouped together; however it seems
quite convenient for storing the addresses of other PCs you wish to
communicate with.  LANTEST uses a group of these structures to keep track
of all other LANTEST users; it is assumed that LANLIB users will employ a
similar scheme.  Keep in mind that the socket field MUST be maintained in
high-low byte order or sends and receives will not complete properly.
Routines for manipulating IPXADDRFULLs are given in LANUTIL.C.

struct IPXDIAG: This is the structure of the data portion of the IPX
diagnostic packet.  It is used by ipxRouteFind() to locate other network
segments.

struct IPXDIAGREPLY: This is the format of the data portion of IPX diagnostic
reply packets received in response to diagnostic requests; again, it is used
by ipxRouteFind().

struct IPXEVENT: This is another structure I "made up."  LANLIB contains
two ESRs (Event Service Routines), one which IPX calls when a send is
completed, the other when a receive is completed.  Each of these ESRs
maintains a queue of events that it has been notified of (except that the
send ESR simply throws away events if the send completed without errors).
Each event is stored in a queue of IPXEVENT structures.  These queues are
maintained and processed entirely by internal LANLIB functions.

struct IPXPKTHDR: This is the 30-byte packet header that all IPX packets
must have.  You don't need to directly concern yourself with it; LANLIB
will fill out the fields in IPX packet headers where necessary.

struct ECB: This is the standard IPX Event Control Block.  It contains
pointers to two "fragments," the packet header and the packet data.  An
ECB can actually have fields for any number of "fragments," but LANLIB
always uses two, one for the header and one for the data.  Again, LANLIB
will take care of filling out ECB fields, so you don't have to worry about
them.



******************************************************************************
*                                                                            *
***  Configuring LANLIB with Defines In LANCFG.H                           ***
*                                                                            *
******************************************************************************

LANLIB is tailored to the needs of your program by setting the defines in
LANCFG.H (or LANCFG.INC for assembly programmers).  In this way, you
determine how many ECB/packet pairs are available for sending and receiving,
the size of the packets, the socket number, and the maximum number of network
segments allowed.  (These defines are then used as parameters to ipxStart()
and ipxRouteFind(), described below.)  You will also indirectly be setting
how much memory the communication data will take.

Set IPXSOCKET to the socket number you want your program to use.  This
value will be passed as a parameter to ipxStart().  Novell states that
"dynamic" socket numbers, that is, ones not officially reserved, start at
4000h and end at 7FFFh.  The default socket number is 51E7h, but you should
open up the file and change it right now, because everybody else who uses
LANLIB will also be getting that as the default, and two programs running on
the same network with the same socket number will bump heads.  See the file
NETWORK.TXT for more information on socket numbers.

Set IPXNETCNT to the maximum number of network segments you will want LANLIB
to be aware of.  The default is 10.  Most NetWare networks have six or fewer
segments.  Setting it to higher numbers means that networks further away
will be found, but communicating with PCs farther away will take longer and
will require a bigger structure for managing network segment addresses.
The shareware version of LANLIB is effectively limited to 1 regardless of
what you set this value to.  LANLIB imposes no upper limit on this value,
but generally speaking, setting it higher than 300 seems pointless.

Set IPXRECVCNT to the total number of ECB/packet pairs you wish to make
available for receiving incoming packets.  The default is 30.  The minimum
value is 0; the maximum is 250.  If you are planning to use ipxRouteFind()
to locate other network segments, then the minimum is 30.  It is passed as
a parameter to ipxStart() and is used to set the number of receive ECBs,
packet headers, packet data buffers, and the receive event queue size.  You
can make your program completely "deaf" to incoming packets by setting this
value to 0.  If your program will only receive packets infrequently and
you're prepared to retrieve them quickly, you might get by with setting this
value to 1.  If you anticipate receiving lots of data and your program's
main loop might not get around to processing incoming packets for several
seconds, set this value to a high number, perhaps as high as 200.  It's far
better to set this number too high than too low, because if a data packet
arrives and all the ECBs are already full, IPX will simply throw it away
without so much as an error.  Each ECB/packet pair requires one ECB
(48 bytes), one packet header (30 bytes), one entry in the receive event
queue (4 bytes), and one packet buffer, the size of which is set by
IPXDATASIZE (see below).

Set IPXSENDCNT to the total number of ECB/packet pairs you wish to use for
sending packets.  The default is 8.  The minimum value is 0; the maximum is
250.  If you're going to use ipxRouteFind(), the minimum is 2.  It is passed
as a parameter to ipxStart().  It is used to set the number of send ECBs,
send packet headers, send packet buffers, and the size of the send event
queue.  You can make your program listen-only by setting this value to 0.
Generally this number can be lower than IPXRECVCNT because most network
programs send less data than they receive and send requests are usually
carried out very quickly.  Even if all send ECBs are in use when you want to
do another send, you'll simply get an error; you're free to retry the send a
few milliseconds later.  Setting this value to 1 is plenty enough for
programs with light send needs.  Even if your program does nothing but sit in
a loop sending packets, you probably shouldn't need more than 20.  Each send
ECB/packet pair takes the same number of bytes as a receive ECB/packet pair
(see above).

Set IPXDATASIZE to the number of bytes of data you want to be able to send
(and receive) in each packet.  Don't take the 30-byte IPX packet header
into account here; this number is only for data bytes.  The default is 256.
The minimum size is 0; the maximum is 546.  If you're going to use
ipxRouteFind(), the minimum is 256.  It's entirely possible to send packets
with zero data bytes; the receiver will know nothing more than the sender's
address, but in some contexts this might be enough.  If you'll be sending
lots of little packets, set IPXDATASIZE to a small number; big streams of
data would be better-served by large packet sizes (but this will require
more memory).  It's possible to send packets that are smaller than
IPXDATASIZE, but it's never possible to send one that's bigger than this
value.  The amount of memory taken up by this setting depends on the values
of IPXRECVCNT and IPXSENDCNT; each ECB/packet pair used for both sending and
receiving will have one buffer of this size.



******************************************************************************
*                                                                            *
***  Dealing with Network Segments                                         ***
*                                                                            *
******************************************************************************

Network segments involve some of the most difficult concepts of IPX
programming, and therefore also some of the most difficult concepts in LANLIB
programming.

There are two general approaches you can take: Operate only on one segment,
or operate with many or all accessible segments.  Naturally there are
advantages and disadvantages to each approach.

If ROUTE is not defined (in LANTEST.H), then LANTEST will be a single-segment
program.  If ROUTE is defined, then it will function as a multiple-segment
program (after ipxRouteFind() has been called, which is accomplished with
the R command).  Look for the conditional compile directive "#ifdef ROUTE"
which appears in several places in NETWORK.C to see what's different for each
approach.


The Single Segment Approach
---------------------------

Operating on only one segment is easiest, fastest, and simplest to manage,
and the only mode available for LANLIB programs if you're using the
shareware version.  There are no worries about finding other network
segments.  Since no routers will be involved, you can rest assured that
packets will arrive as quickly as possible.  Every IPX-aware game I've ever
seen operates in this manner (excepting games in development using LANLIB, of
course).  When you need to broadcast packets to find potential users in
single segment mode, you merely call ipxAddrLocal() with a pointer to an
IPXADDRFULL structure, then pass that structure to ipxAddrBrd() to set it
up as a broadcast address, then pass that address to ipxSendPkt() for the
broadcast.  You don't need to concern yourself further with the concept
of network segments.


The Multiple Segment Approach
-----------------------------

Operating with multiple segments is more complicated to manage and
potentially slower if more than a few routers are involved, but far more
flexible, and also better in tune with the size and scope of today's
networks.  I've installed and managed NetWare networks for many years now,
and I can say for certain that networks with multiple segments are far more
common than single-segment networks.  It doesn't seem particularly sporting
to restrict users from interacting with each other via your program simply
because their PCs are attached to different network segments.

The method for discovering network segments other than your own in NetWare
environments is accomplished through "NetWare diagnostics."  It's an
interesting bit of trivia that NetWare file servers, routers, and PCs
running NetWare's shell TSRs keep quite a few statistics on their
performance, and are willing to give up this information (via SPX
diagnostics) to anyone on the network who requests it, with absolutely no
security checks whatever.  You don't even have to be logged in to gather
NetWare statistics.

Another use for the statistics functions is to gather network segment
information.  When an IPX diagnostic request is broadcast on a network
segment, PCs will respond with a simple list of "components" installed:
IPX/SPX, shell, shell driver, and so on.  Routers will return this
information as well, but will also give information about all network
segments they are attached to.  Then the diagnostic request can be
rebroadcast on these newly discovered segments, which will probably turn
up more routers with information on more segments, and so on.  This method
is used by ipxRouteFind() to locate network segments.

ipxRouteFind() is called with a structure of two or more IPXADDRFULLs.  (If
you were only interested in knowing about one network segment, you wouldn't
need ipxRouteFind().)  It sets up the first of these structures to be
a broadcast address suitable for use on the local segment; it will have its
node and immediate address fields set to FFFFFFFFFFFFh.  In other words, it
will be set up exactly as you would set it up yourself in a single segment
program with a call to ipxAddrLocal() and then ipxAddrBrd().

This first broadcast address is used to send a diagnostic request on the
local segment.  PCs will respond with their diagnostic information, but they
are ignored, and in fact subsequent diagnostic packets are filled with a list
of "exclusion addresses," or nodes that should not respond to the request;
all known PC addresses are added to this list.  ipxRouteFind() only cares
about routers.

If there are routers on the segment, they will respond with more network
segment numbers.  These segments are all added to the table of network
addresses; the node address fields will all be set to FFFFFFFFFFFh,
suitable for broadcasting, and the immediate address fields will be filled
in with a call to ipxAddrImmed(), which is the officially sanctioned
method of determining the "correct" immediate address to use.  Remember
that packets sent to other network segments must first be sent to a router,
which in turn sends the packet on its way towards its destination.  The
immediate address indicates the node number on the local segment of the
router that will do the first part of the routing.

Each new broadcast address set up in this fashion is used to send a
diagnostic request on that segment.  Newly discovered segments will continue
to be added.  ipxRouteFind() works its way through the list with the aid of
two pointers: one that indicates the network segment currently being
investigated and another that indicates the first free slot in the table for
storing a newly discovered address.  When the two pointers point to the same
location, router-finding is finished; all known network segments have been
investigated.

So, when it's time to broadcast queries looking for other users of your
program, you do not send out a request only on your network segment, as
you would in a single-segment program.  You send out a request on all
segments in all the IPXADDRFULL structures you passed to ipxRouteFind(),
using those addresses as the address parameter to ipxSendPkt().  You can use
the sending addresses of received replies just as you would in a single-
segment program, even though some will probably contain network numbers other
than your own.

If you want to allow your program to work with a few segments for
flexibility, but not a huge number so as to keep the speed up, you can limit
the number of IPXADDRFULL structures you pass to ipxRouteFind().  You could
even save this number as a run-time variable and let the user change it
according to his or her needs (obviously the user would have to be rather
technically astute to understand this option, however).  You could perhaps
set a reasonable default, or maybe have two radio buttons in your "network
options" dialogue, one labeled "favor speed" which would allow up to six
network segments, the other labeled "find all possible users" which would
allow up to 60.


"Virtual" Network Segments
--------------------------

It is likely that at least one of the segments that ipxRouteFind() locates
for you will be a "virtual" segment.  Starting with NetWare v3.0, servers
must be assigned something called an "IPX Internal Network Number;" this
is normally set in the server's AUTOEXEC.NCF command file with the command
"IPX Internal Net xxxxxxxx".  Internal network numbers are eight hexadecimal
digits long, just like "real" network numbers.  The server always makes
itself node address 000000000001 on this network.  This is its "official"
network address and there will be no other nodes on the virtual network.
Prior to version 3.x, a NetWare server's address was the network and node
address of the first NIC installed in it (the one referred to as "LAN A").

Network information returned from routers has one byte that indicates the
type of each known network, and so virtual networks can be distinguished
from "real" networks.  It would at first seem logical for ipxRouteFind() to
throw away information on these virtual networks, but there are two catches
that make this a bad idea.

First is the fact that there is another type of "virtual" network.  Servers
running version 2.x of non-dedicated NetWare all have a virtual network
segment that is used for the DOS side of the server itself.  (Non-dedicated
servers function as both a file server and as a normal DOS PC through the
magic of multitasking.)  Therefore this type of virtual network will have a
PC on it that you might want to communicate with.  From the standpoint of
NetWare diagnostics, this type of virtual network is indistinguishable from a
NetWare v3.x server's virtual network.

Second is the fact that one possible use of LANLIB is to poke around inside
NetWare itself.  Filtering out the server's network segment would make it
impossible to send requests directly to a NetWare file server.


NetWare Diagnostics Cautions
----------------------------

In testing LANLIB, I have noticed that if lots of PCs are running diagnostic
searches at the same time, one or more may end up with incomplete network
information.  I ran LANTEST on 16 PCs attached to a two-segment Ethernet
network with one NetWare v3.12 server acting as the router.  I sent a command
to all PCs to start diagnostics at the exact same moment.  Seven ended up
with incomplete network information; they knew about their own network
segment (of course), but did not find the second one or the server's
"virtual" segment.  Since each PC sends three diagnostic requests on each
segment, and the server exists on all three segments, it had to (try to) send
nine diagnostic replies to each PC.  My guess is that the IPX drivers at each
workstation were so busy sending and receiving diagnostic requests and
replies that some were lost, or the server/router could not keep up with the
demand.  (Almost all the PCs were 486s; there were only two 386s.)  Obviously
this is a worst case scenario, but it's not unlikely that several people
could start up your program at the same time.  A possible option would be to
allow the user to re-scan if all expected users do not appear, or perhaps to
have a secondary diagnostic phase where all copies of your program ask all
other known copies for their network info; if even one PC on a segment got
all the information, then it could pass it on to all others whose knowledge
might be incomplete.

"Diagnostic inundation" is not the only reason that using NetWare diagnostics
to locate network segments is not totally foolproof.  It will work reliably
almost all of the time in small- to medium-sized networks, where the only
type of routers in use are probably NetWare file servers.  Larger
environments can be trickier.  Some standalone routers may route NetWare
packets from segment to segment and yet not respond to standard NetWare
diagnostic requests.  Given my resources, there's no way I can support
non-standard routers in LANLIB.

There is another method that can be used for locating network segments: the
list of logged-in users (the "connection list") on a NetWare file server can
be examined.  This method has a big disadvantage, however: it requires that
everybody who will use the program must be logged in to a file server.  Ad
hoc networks can spring up solely for the purpose of running a favorite
network-aware game; many of these networks may be too small to even have a
file server, so this would not be a good approach in those situations.
Still, if a LANLIB user tells me that ipxRouteFind() isn't working in a
particular environment she wants to support, I would probably write a
secondary ipxRouteFind()-like procedure that operated via a server's
connection list that could be used in troublesome environments.



******************************************************************************
*                                                                            *
***  ECB Usage Statistics                                                  ***
*                                                                            *
******************************************************************************

While I was writing LANTEST, I found myself wondering how many ECBs the
program was actually using.  It had the default total of 8 send ECBs, but
was there ever a time when that many were in use?  The LANLIB procedure for
sending packets will report an error if there are no send ECBs free, but
that doesn't tell you much.  Receive ECBs are even harder to gauge; if you
run out, IPX simply starts throwing away packets.

That's when I built ECB usage statistics into LANLIB.  It keeps track of
how many receive and send ECBs are in use at any given moment in variables,
and makes two other word-sized variables available to your program,
"ipxRecvMax" and "ipxSendMax," that contain the maximum number of receive
and send ECBs that have been in use so far.  For the purposes of the
statistics, a send ECB is considered "in use" between the time when you post
it to IPX and the time that IPX notifies LANLIB that it has been sent; a
receive ECB is considered "in use" beginning at the moment when IPX uses it
to receive a packet and is no longer in use once you collect that packet and
the ECB is put back into service.  All the LANLIB procedures update these
variables dynamically as packets are sent and received, when the Event
Service Routines are notified that sends and receives have completed, and so
on, so they will always be correct.

You can reset the values of "ipxRecvMax" and "ipxSendMax" to zeroes at any
time you like to start over again.

I ran LANTEST with "flurry mode" turned on (which means it continually
sends out packets as fast as it can) and checked the statistics.  What I
discovered is that it is very rare for LANTEST to need more than one send
ECB.  If the main loop is jiggered to favor sends over receives, then it
might need as many as four, but not usually.

Receive ECBs are a different matter.  I discovered that normally the program
gets along just fine with four or fewer receive ECBs in "flurry mode," but
that every now and then some event will steal time normally used for
processing incoming packets, like dealing with a lot of keyboard input, and
the max receive ECB value will spike up to a value as high as 50 or 60.  In
fact, no matter how high I set the receive value, I could eventually get the
program into a jam bad enough that it would use every last one of its receive
ECBs.  That's bad news, because it almost certainly means that there were at
least one or two more packets that were lost.  This is a good argument for
writing your program to send acknowledges for all packet transactions.



******************************************************************************
*                                                                            *
***  LANLIB Error Values                                                   ***
*                                                                            *
******************************************************************************

LANLIB routines generally return zero to indicate no error or some positive
error value otherwise (some routines take a pointer to a word-sized variable
and return errors there).  Possible error values are defined in LANLIB.H and
LANLIB.INC along with a brief description for each.  All defines begin with
the letters "ipxe", or "IPX Error."  ipxeNOERR is defined as 0; other values
begin at 2 and head upward from there.  Further treatment of the error values
is given in the descriptions of the LANLIB routines.

It is strongly recommended that you use the error defines in your program
rather than the numerical values of the defines, because I reserve the right
to change the numerical values in future versions of LANLIB.

It was my original intention to use IPX's own error return values, but this
proved to be unworkable.  Almost every IPX call can return the error code
FFh, for instance, but it means something different in every case.  I wanted
one set of error codes that always meant the same thing, so procedures in
LANLIB all "translate" IPX's errors into one of the LANLIB error values.

Note that, in some very rare cases, you may get an error return value other
than the ones listed in LANLIB.H.  Novell has released many, many different
versions of the IPX TSRs over the years, and I wouldn't be surprised if some
of them return error values not given in Novell's documentation.

Naturally LANLIB can only translate error values that it knows about.  It is
programmed so that if IPX returns an unrecognized error value, it will pass
the unknown value straight through to your program untranslated.  Chances
are good that such an error would be in the E0h to FDh range; IPX calls
tend to return high-numbered errors.  If you get such an error value, I'd
be interested in hearing about it so I can incorporate it into future
versions of LANLIB.



******************************************************************************
*                                                                            *
***  Using The Routines in LANLIB                                          ***
*                                                                            *
******************************************************************************

The following is a reference guide for all the routines in LANLIB that your
program can use.  This is NOT the place to start if you don't understand the
concepts involved.  Begin by reading NETWORK.TXT, then the earlier parts of
this file.  Use this section as a day-to-day reference for the routines.



ipxAddrImmed()
--------------

Prototype: word ipxAddrImmed(struct IPXADDRFULL far *ipxAddr,
                             word far *ticks);

This procedure calls the IPX routine known as IPX Get Local Target to get the
preferred immediate address for a network address.  At minimum, the network
and node address fields in "ipxAddr" should be filled in before calling this
procedure.  If successful, it will fill in the immediate address field.  If
the address is on the same network segment, then the immediate address will
be the same as the node address; if it's on a different segment, then the
immediate address will be that of the router that is best suited for the
task.

Parameters:

  * struct IPXADDRFULL far *ipxAddr: A pointer to an address containing
    a network number and node number which are known to be valid.  It's
    okay if the node number is FFFFFFFFFFFFh (a broadcast address).

  * word far *ticks: A pointer to a word to receive the estimated time
    of arrival to this destination address, in IBM PC clock ticks
    (approximately 1/18th of a second).  This time is only an estimate;
    actual transport time will vary depending on network traffic.

Return values:

  * ipxeNOERR (FALSE): The immediate address was filled in without errors.

  * ipxeIPXNOTSTARTED: ipxStart() hasn't yet been called.

  * ipxeNOPATHFOUND: No path to the destination address could be found;
    the immediate address field hasn't been set.

You should use this procedure only once for each address, rather than
before each send, because it requires IPX to do some spelunking out on the
network, which can create network traffic as well as take time.

Generally speaking, use of this procedure is optional, because there is a
more direct method for determining the immediate address for a network
address: When a packet is received, simply save the full address of the
sender, including the immediate address, and use that.  However, there are
some occasions where this procedure comes in useful.

Since there can be more than one route to a given destination, there can
be more than one immediate address that will work for a given address.  When
this procedure is used, IPX tries to determine the "best" route, taking
network traffic and hop distance into account.

This procedure is also useful if you know the network and node address of a
PC you want to talk to but don't also know the immediate address.

Finally, its estimate of time necessary to reach an address can be helpful
if transport speed is important to your program.

ipxRouteFind() uses this procedure to determine the best immediate address
to use for each network address that it finds.



ipxAddrLocal()
--------------

Prototype: word ipxAddrLocal(struct IPXADDRFULL far *ipxAddr);

This procedure gets the IPX address of the PC that your program is running
on.  It fills in every field of the IPXADDRFULL structure.  The immediate
address will be the same as the node address.

Parameters:

  * struct IPXADDRFULL far *ipxAddr: A pointer to a structure to receive
    the address.  It doesn't have to be initialized in any way, as every
    field will be filled in.

Return values:

  * ipxeNOERR (FALSE): The address was collected without errors.

  * ipxeIPXNOTSTARTED: ipxStart() has not yet been called; the local
    address can't be retrieved until it is.



ipxLibVer()
-----------

Prototype: word ipxLibVer(void);

This procedure gets the version number of LANLIB.  It is the only LANLIB
function that can be called before ipxStart() or after ipxStop().  The major
version number is returned in the high byte and the minor version in the low
byte.  For example, a return value of 0100h would mean major version 1, minor
version 0, or 1.0.



ipxRecvChk()
------------

Prototype: word ipxRecvChk(void);

This procedure checks to see if any packets have been received and are
waiting to be picked up.  If there are, you can retrieve them with
ipxRecvPkt().

Return values:

  * FALSE: No packets are waiting.

  * TRUE: At least one packet is waiting.

  * ipxeIPXNOTSTARTED: ipxStart() hasn't been called; it isn't possible
    to check for packets yet.

If IPXRECVCNT is 0, then this procedure will always indicate that no packets
are waiting.



ipxRecvPkt()
------------

Prototype: word ipxRecvPkt(void far *data,
                           word dataSize,
                           struct IPXADDRFULL far *ipxAddr,
                           word far *pktSize,
                           word far *error);

This procedure checks to see if a packet has arrived, and if it has, it
passes the data in the packet and the IPX address of the PC it was
received from to your program.

Parameters:

  * void far *data: A pointer to your buffer for receiving the data.

  * word dataSize: The size, in bytes, of your buffer.

  * struct IPXADDRFULL far *ipxAddr: A structure for receiving the
    address of the sending PC.

  * word far *pktSize: The address of a word that will receive the total
    size of the data in the packet.

  * word far *error: The address of a word that will receive the error
    code associated with this packet.

Return values:

  * FALSE: No packets are waiting and nothing was copied to your data
    buffer or ipxAddr structure.  "pktSize" is zero.  "error" is also
    zero, unless ipxStart() hasn't yet been called.

  * TRUE: A packet has been received.  The sending address has been
    copied to ipxAddr and "error" has been set accordingly.  If "error"
    is set to ipxeNOERR (zero) or ipxeSOCKETNOTOPEN, a packet has been
    copied to your buffer and "pktSize" has been set to the number of
    data bytes received.  If "error" has some other nonzero value, no
    packet data has been copied and "pktSize" is set to zero.

Return values for "error:"

  * ipxeNOERR (FALSE): A packet was received and copied to your buffer
    without any errors.

  * ipxeIPXNOTSTARTED: ipxStart() has not been called so no packets can
    be received.

  * ipxeRECVPKTTOOBIG: A packet was received but it was too big to fit
    in the buffer associated with the ECB (in other words, it was larger
    than IPXDATASIZE), so IPX threw it away.  No data was copied to your
    buffer, but your IPXADDRFULL structure has been filled in with the
    sender's address.

  * ipxeRECVCANCEL: IPX reports that the receive event was canceled.
    No packet data was copied.  The sender's address has been copied, but
    is probably meaningless in this case.  This should never happen under
    normal circumstances.

  * ipxeSOCKETNOTOPEN: The packet was received okay and has been copied
    to your buffer, as well as the sender's address.  While LANLIB was
    attempting to put the ECB back into service to receive more packets,
    IPX reported that the socket referenced by the ECB is not open.  This
    should never happen under normal circumstances.

If the size of your buffer isn't large enough to hold all the received data
(this can only happen if your buffer is smaller than IPXDATASIZE), then as
much data as would fit was copied (and no error will be returned).  You can
tell this has happened if the value of "pktSize" is greater than the value
you passed as "dataSize."

If IPXRECVCNT is 0, then this procedure will always indicate that no packets
have been received.



ipxRouteFind()
--------------

Prototype: word ipxRouteFind(struct IPXADDRFULL far *network,
                             word netCnt,
                             word (*ipxWaitProc)(word netsFound),
                             word far *netsFound);

This is a complicated procedure which finds other network segments in the
internetwork.  It fills in a structure of IPXADDRFULLs with broadcast
addresses for all the segments it finds, including the network segment the
program is running on.  See the earlier section "Dealing with Network
Segments" for details on using this information.  This procedure is not
available in the shareware version of LANLIB.

Parameters:

  * struct IPXADDRFULL far *network: A pointer to at least two
    IPXADDRFULL structures.  It is suggested that you use the define
    IPXNETCNT from LANCFG.H when determining how big to make this
    structure (or how much memory to allocate for it), rather than
    using a fixed number.

  * word netCnt: The total number of IPXADDRFULLs pointed to by
    "network."  It is suggested that you use the define IPXNETCNT
    rather than a fixed number.

  * word (*ipxWaitProc)(word netsFound): A pointer to a procedure
    that will be called repeatedly while the router-finding is taking
    place.  This will be a near pointer in small code models and a far
    pointer in large code models.  Since ipxRouteFind() might conceivably
    take quite a long time to do its work, this gives you a chance to
    display something on the screen to let the user know the program
    hasn't crashed and to allow an opportunity to abort the procedure, if
    need be.  See the description for ipxWaitProc() for details.  If this
    pointer is NULL, then no external procedure will be called.

  * word far *netsFound: A pointer to a word that will receive the
    number of network segments that were located.

Return values:

  * ipxeNOERR (FALSE): The procedure completed without errors.
    "network" has been filled in with all known networks, and the
    total count has been placed in "netsFound."

  * TRUE (1): A send error occurred either at some point before
    ipxRouteFind() was called or while sending out diagnostic requests.
    The send error(s) are still waiting to be picked up via calls to
    ipxSendErr().

  * ipxeNETOVERFLOW: The procedure aborted because it found at least one
    more network segment than would fit in the "network" structure that
    was passed to it.  This is not really an "error" per se; you might
    purposefully wish to keep the number of networks your program will
    work with to a minimum to ensure fast response.

  * ipxeBADCOMMPARMS: "netCnt" is less than two, or one or more of the
    values IPXRECVCNT, IPXSENDCNT, or IPXDATASIZE passed to ipxStart()
    are too small to allow the procedure to run.  See below for details.

  * ipxeNOFREESENDECB: While trying to send out a diagnostic request,
    ipxSendPkt() reported that there were no free send ECBs.  If you get
    this error during testing, you should increase the value of
    IPXSENDCNT.

  * ipxeROUTEFINDABORT: The procedure was aborted because "ipxWaitProc()"
    returned TRUE.  See below for details.

  * ipxeIPXNOTSTARTED: ipxStart() has not yet been called.

  * ipxeNOPATHFOUND: When ipxRouteFind() called ipxAddrImmed() to get
    the immediate address for a discovered network, ipxAddrImmed()
    returned this value.  This shouldn't happen under normal
    circumstances.

  * ipxeRECVPKTTOOBIG: While receiving diagnostic replies, ipxRecvPkt()
    reported this error.  See ipxRecvPkt() for more details.

  * ipxeRECVCANCEL: See explanation for ipxeRECVPKTTOOBIG above.

  * ipxeSOCKETNOTOPEN: See explanation for ipxeRECVPKTTOOBIG above.

If the procedure returns with some value other than FALSE, the network
information in the "network" structure and the "netCnt" variable will be
accurate but not necessarily complete; the procedure might not have found
all networks before it had to abort.  If the value returned is
ipxeBADCOMMPARMS or ipxeIPXNOTSTARTED, then "network" will contain no
network information at all.

ipxRouteFind() performs the following steps:

  * It calls ipxAddrLocal() to fill in the first IPXADDRFULL structure
    pointed to by "network" with the network address of the network that
    it is running on.  Then it sets the node and immediate address fields
    of this first structure to all F's, making the address suitable for
    broadcasting.

  * It calls ipxSendChk() to make sure there are no send errors in the
    send queue.  If there are, it aborts and returns to your program,
    leaving the send errors in the queue.

  * It calls ipxSendPkt() to send out a diagnostic request on the local
    network segment.

  * It calls ipxRecvPkt() repeatedly to get diagnostic replies.  Packets
    from anything that routes packets to other segments (standalone
    routers or file servers) are examined for other network segments.
    Replies from other PCs are ignored.  Non-diagnostic packets are also
    ignored (this is determined by examining the socket that the packet
    came from).  The addresses of other networks discovered are saved,
    and ipxAddrImmed() is called for each to fill in the best immediate
    address.  To avoid clogging the network with diagnostic replies,
    each node will wait for a period of time up to half a second before
    sending a diagnostic response, so this procedure will wait at least
    that long to give all nodes a chance to respond.  It only checks the
    elapsed time when no replies are coming in, so the actual time used
    might be greater than half a second.

  * If a pointer to a procedure of type ipxWaitProc() was given, it is
    called at this point.

  * The cycle is repeated twice more, starting with the call to
    ipxSendChk(), to ensure that all routers are heard from and to allow
    enough time to receive responses from routers that might take longer
    than half a second to arrive.  Therefore, about 1.5 seconds are spent
    analyzing the network segment from start to finish, and ipxWaitProc()
    will be called three times.

  * The entire 1.5 second three-cycle ordeal is repeated for each new
    segment that is discovered.  It is possible that late replies from
    earlier diagnostic requests on other segments might arrive during
    this time; if so, they will be properly processed as well.  This
    process continues until all segments have been discovered and
    analyzed or until a new segment is discovered and the structure
    for saving the segment information is full, whichever comes first.

  * After all discovered segments have been analyzed, the procedure waits
    one more full second to see if any more late diagnostic responses
    will arrive.  If one or more such responses arrive, and they contain
    the addresses of one or more new network segments, then the 1.5
    second analysis procedure is again run for those segments and the
    full second final wait will be repeated.

Since ipxRouteFind() is basically a small LANLIB-using application unto
itself, it has certain requirements that must be met for it to be used:
IPXRECVCNT must be at least 30, IPXSENDCNT must be at least 2, and
IPXDATASIZE must be at least 256.  If these conditions aren't met, then
ipxRouteFind() will return ipxeBADCOMMPARMS, no network segment data
will be copied to "network," and "netsFound" will be zero.

If a procedure was given as "ipxWaitProc()", then ipxRouteFind() will call it
at the end of each send/receive cycle, approximately every half second.  The
procedure is called with one word-sized parameter, netsFound, which indicates
the number of networks found so far.  This number will not be incremented on
every call to "ipxWaitProc()".  If the ipxWaitProc() procedure returns FALSE,
then router-finding continues; if it returns TRUE, then ipxRouteFind() will
abort and return ipxeROUTEFINDABORT.  See ipxWaitProc() for more details.

You shouldn't have any "conversations" with other PCs going on when you call
this procedure, because it will "eat" any and all packets that are received
while it is running and throw away any that aren't diagnostic responses.

It's okay to call this procedure more than once during the run of a program;
for instance, it might need to be called a second time with a higher number
for netCnt if a particular expected address was not found or ipxeNETOVERFLOW
was returned.

It is possible but unlikely that your program will receive a late diagnostic
response at some point after ipxRouteFind() completes.  You can check for
this condition by examining the socket number in the sending address;
diagnostic replies come from socket 0456h.  If this happens consistently,
let me know; it probably means that I need to tweak the code inside
ipxRouteFind() to wait a little longer for replies to arrive.

If a router does not respond to standard NetWare diagnostic requests, then
this procedure won't recognize it and won't be able to access the networks
it knows about.  I am told that routers like this exist in some NetWare
environments.  Routers of this type are not supported by LANLIB.

If a router claims to be attached to more than 32 networks, its information
is ignored.  Values higher than 32 probably indicate an error of some sort.

Since it must take real time into account, ipxRouteFind() examines the word
at 0040:006Ch in the BIOS data segment, which is the least significant word
of the number of timer ticks that have elapsed since midnight.

This procedure uses almost 1k of stack space for temporary variables, so you
should ensure that this much is available.



ipxSendChk()
------------

Prototype: word ipxSendChk(void);

This procedure checks to see if any errors were encountered while trying
to send packets, and therefore whether those packets are waiting around
for you to pick them up.  If there are unsent packets, you can pick them up
with ipxSendErr().  This should be done periodically, because ECB/packet
pairs that were used for unsuccessful sends are unusable until retrieved
with ipxSendErr().

Return values:

  * FALSE: No undelivered packets are waiting.

  * TRUE: At least one undelivered packet is waiting.

  * ipxeIPXNOTSTARTED: ipxStart() hasn't been called; it isn't possible
    to check for send packet errors yet.

If IPXSENDCNT is 0, then this procedure will always indicate that no send
errors are waiting.



ipxSendErr()
------------

Prototype: word ipxSendErr(void far *data,
                           word dataSize,
                           struct IPXADDRFULL far *ipxAddr,
                           word far *pktSize,
                           word far *error);

This procedure checks for IPX send errors.  It is unlikely that you'll get
any, but if you do, this routine in effect gives your packet back to you so
you can retry the send or take other steps as necessary.  It should be called
every now and then even if you don't want to process send errors, because a
send ECB/packet pair that had an error is taken out of service indefinitely
until you call ipxSendErr() to free it up.  If all send ECBs are taken out
of service due to errors, then you won't be able to send any more data.

Parameters:

  * void far *data: A pointer to your buffer that will receive the
    packet that couldn't be sent (if an error is reported).

  * word dataSize: The size of your buffer, in bytes.

  * struct IPXADDRFULL far *ipxAddr: A structure to receive the address
    of the PC that the packet should have been sent to.

  * word far *pktSize: A pointer to a word to receive the size of the
    data that is being returned.

  * word far *error: A pointer to a word to receive the error value
    associated with the packet.

Return values:

  * FALSE: No undelivered packets are waiting.  "error" will be set to
    ipxeNOERR (zero) unless ipxStart() hasn't been called.

  * TRUE: An undelivered packet has been copied to your buffer, the
    total size has been set in "pktSize," and the intended recipient's
    address has been copied to "ipxAddr."  The ECB with the error is
    returned to service, ready to be used for the next send request.

Return values for "error:"

  * ipxeNOERR (FALSE): There are no undelivered packets to be processed.

  * ipxeIPXNOTSTARTED: ipxStart() has not been called.

  * ipxeSENDBADROUTE: The packet was undeliverable.  This can happen
    if you send a packet to yourself (broadcasts count as "sending a
    packet to yourself") and there are no free receive ECBs.  If the
    packet was to another PC, then IPX could not find a route to the
    destination node; this probably indicates a problem with the
    immediate address.

  * ipxeSENDCANCEL: IPX reported that the send event was canceled.  This
    shouldn't happen under normal circumstances.

  * ipxeSENDPKTBAD: The packet was "malformed," to use Novell's term.
    It was either too short, too long, or the fragment count was wrong.
    LANLIB takes care of all this, so this error should never be
    returned under normal circumstances.

  * ipxeSENDNETFAIL: IPX reports that the local network hardware or the
    network itself has failed.

If IPXSENDCNT is 0, then this procedure will always indicate that no send
errors are waiting.



ipxSendPkt()
------------

Prototype: word ipxSendPkt(void far *data,
                           word dataSize,
                           struct IPXADDRFULL far *ipxAddr);

This procedure posts a send request with IPX.  No error associated with
the send will be returned, because ipxSendPkt() will return before IPX
has actually sent the packet.

Parameters:

  * void far *data: A pointer to your buffer filled with packet data to
    be sent.

  * word dataSize: The number of bytes in your buffer to be sent.  This
    does not have to be the same as the size of the buffer, if you want
    to send fewer bytes.

  * struct IPXADDRFULL far *ipxAddr: The full IPX address of the PC to
    send the packet to.  If you set the node address to FFFFFFFFFFFFh,
    the packet will be sent to all nodes on the selected network segment.

Return values:

  * ipxeNOERR (FALSE): The request was posted without errors.  This DOES
    NOT mean that the packet has been sent.

  * ipxeIPXNOTSTARTED: ipxStart() has not been called.

  * ipxeNOFREESENDECB: All send ECBs are currently in use, waiting to be
    sent by IPX (or else IPXSENDCNT is set to 0, which shouldn't be true
    if your program ever sends any packets).  You can retry the send at
    some point later in time.  If you get this error during testing, you
    can increase the number of send ECBs by setting the define IPXSENDCNT
    in LANCFG.H to a higher value.

If the value of "dataSize" given is bigger than the value of IPXDATASIZE,
then you've given more data than the procedure can send; the number of
bytes sent will be truncated to IPXDATASIZE and no error will be returned.

The procedure ipxSendErr() should be called occasionally to check for send
errors.  If all packets are sent successfully, then ipxSendErr() will return
nothing, but keep in mind that this does NOT mean that the packet was
successfully received; it could have been lost in transit.  IPX does not
guarantee that all packets sent will be received.

IPX allows you to "send packets to yourself," so to speak.  You could call
ipxSendPkt() to send some data, setting "ipxAddr" to your own IPX address,
and then receive that same packet with ipxRecvPkt().  IPX is smart enough
to figure out that the packet doesn't have to go out onto the network and
merely copies it in memory.  This can be useful for debugging.

If a program "broadcasts" a packet to all addresses on its own network
segment by sending to node address FFFFFFFFFFFFh, then the program will
receive a copy of its own sent packet.



ipxStart()
----------

Prototype: word ipxStart(word recvCnt,
                         word sendCnt,
                         word dataSize,
                         word socket,
                         void far *memPtr,
                         word memSize);

Call this as the very first IPX-related routine in your program.  No other
IPX functions except ipxLibVer() may be used until ipxStart() has been
called.  It performs the following functions:

  * It checks all parameters for validity except "socket."  It also
    checks to make sure that the amount of memory given is large enough
    to hold everything.

  * It checks to make sure that the IPX driver is loaded.  If not, it
    returns with an error code.  If IPX is available, it saves the entry
    point used to make far calls to the IPX driver, which is needed by
    all other LANLIB routines except ipxLibVer().

  * It opens the IPX socket number given as the parameter "socket"
    for sending and receiving packets.

  * It registers all receive ECBs and their associated packet buffers
    with IPX, making them available to receive incoming data packets.

Parameters:

  * word recvCnt: The number of receive ECB/buffer pairs to use.  Legal
    values are 0 to 250.  The minimum is 30 if ipxRouteFind() will be
    used.  It is suggested that you use the define IPXRECVCNT from
    LANCFG.H rather than passing a number directly.  Each receive
    ECB/buffer pair will require IPXCOMMSIZE bytes (a define in
    LANLIB.H).

  * word sendCnt: The number of send ECB/buffer pairs to use.  Legal
    values are 0 to 250.  The minimum is 2 if ipxRouteFind() will be
    used.  It is suggested that you use the define IPXSENDCNT from
    LANCFG.H rather than passing a number directly.  Each send
    ECB/buffer pair will require IPXCOMMSIZE bytes.

  * dataSize: The maximum number of data bytes per packet.  Legal values
    are 0 to 546.  The minimum is 256 if ipxRouteFind() will be used.
    It is suggested that you use the define IPXDATASIZE in LANCFG.H
    rather than passing a number directly.

  * void far *memPtr: This is a far pointer to an allocated block of
    memory that LANLIB will use for ECBs, buffers, queues, and so on.
    This can be memory allocated from the near heap with malloc() or from
    the far heap with farmalloc(), memory obtained with a direct call to
    DOS, a pointer to a block of memory in an initialized or
    uninitialized data segment, or just about anything else, just so long
    as it's big enough to hold everything (see below).  It doesn't have
    to be set to all zeroes; LANLIB does that itself.  The memory block
    must be less than 64k.

  * word memSize: The size of the memory block pointed to by memPtr, in
    bytes.  This value can be calculated by using the define IPXMEMSIZE
    located in LANLIB.H.

Return values:

  * ipxeNOERR (FALSE): All operations were completed without errors.

  * ipxeNOIPX: The IPX driver TSR (or TSRs) are not loaded.  IPX
    communication is not possible.

  * ipxeIPXSTARTED: ipxStart() has already been called.  It should not
    be called twice.

  * ipxeBADCOMMPARMS: One of the communication parameters given was set
    to an illegal value.  All are checked for validity except "socket."
    This value will be returned if "memPtr" is NULL.

  * ipxeMEMTOOSMALL: The value of memSize indicates that the memory
    block given is not big enough to hold all the communication data
    (see below).

  * ipxeMEMABOVE64K: The amount of memory required for all requested
    ECBs and buffers would exceed 64k.  This is not allowed (see
    below).

  * ipxeSOCKETTABLEFULL: The selected socket cannot be opened because
    the IPX driver's socket table is full.  IPX defaults to allowing up
    to 20 open sockets.  To increase this amount, add the line
    "IPX SOCKETS = xx" to the NET.CFG file, where "xx" is the number of
    sockets required.  Sockets use some of the PC's memory, so don't
    increase the number any higher than necessary.

  * ipxeSOCKETOPEN: The requested socket was already open; ipxStart()
    did not complete.  Some other program is probably using the socket.
    This can also happen if the user "crashes out" of your program, so
    ipxStop() was never called and the socket was left open, and then
    the user tries to start it again.  In this case, a reboot will fix
    the trouble.

  * ipxeSOCKETNOTOPEN: While setting up the listen ECBs to receive
    incoming packets, IPX reported that the selected socket was not open,
    despite the fact that the socket was opened earlier with no error.
    It is extremely unlikely that this error will be reported under
    normal circumstances.

The formula for determining how much memory to pass to ipxStart() is as
follows (and is also defined as IPXMEMSIZE in LANLIB.H):

  (IPXRECVCNT + IPXSENDCNT) * IPXCOMMSIZE

This is the total number of send and receive ECB/buffer pairs times the
amount of memory for each, IPXCOMMSIZE.  This is the total overhead bytes
for each plus IPXDATASIZE.  IPXCOMMSIZE is defined in LANLIB.H.  The user
must ensure that the total amount of memory needed is less than 64k.  If
it's greater, reduce the size of one or all of IPXRECVCNT, IPXSENDCNT, or
IPXDATASIZE, defined in LANCFG.H, until the required size is less than
64k.

There is no point in passing more memory than the amount given by this
formula.  LANLIB will not use any excess memory.



ipxStop()
---------

Prototype: word ipxStop(void);

Call this as the last IPX function in your program.  If you end your program
without calling it, your computer may well crash later on when IPX tries to
store an incoming packet in a memory buffer that no longer belongs to you.

ipxStop() performs the following function:

  * It closes the IPX socket passed to ipxStart() as the parameter
    "socket."  This has the effect of canceling any pending send
    requests and all receive ECBs.  The IPX close socket function never
    returns an error, so no form of IPX error will be passed to your
    program.

Return values:

  * ipxeNOERR (FALSE): The procedure completed without errors.

  * ipxeIPXNOTSTARTED: ipxStart() was never called, so there is nothing
    to stop.

If memory was allocated for ipxStart() (as opposed to passing a pointer to
an area of a data segment), then the memory should be freed after the call
to ipxStop().



ipxWaitProc()
-------------

Prototype: word ipxWaitProc(word netsFound);

This is not a procedure in LANLIB, but rather one that can exist in your
program.

Since ipxRouteFind() takes about 1.5 seconds to process each network segment,
and an internetwork might well contain 20 or more segments, it would be easy
for a user of your program to assume that it had crashed and reboot the PC.
To get around this problem, ipxRouteFind() takes as one of its parameters a
pointer to a procedure of this type that it will call approximately every
half second.

Your procedure will receive one word-sized variable, netsFound, which will
be the number of network segments discovered so far.  This number will
never be less than one, since the network segment that your program is
running on is counted, and it is "discovered" before your procedure is
called the first time.  This value might be two or higher on the very first
call to your procedure since one diagnostic cycle will have been completed
at that point.

Your procedure must return one word-sized variable that indicates whether
or not ipxRouteFind() should continue.  If your procedure returns FALSE (0),
then ipxRouteFind() will continue.  If it returns any other value then
ipxRouteFind() will abort.

This procedure is not subject to the restrictions imposed on interrupt
service routines.  At the time it is called the PC is in a stable state.
The procedure can call other procedures in the program, collect keyboard
or mouse input, call DOS and perform file I/O if need be, display information
on the screen (perhaps the running total of networks it was called with), and
take as long as it likes to complete; any amount of time up to a second or so
is reasonable.  It should not call any LANLIB procedures, however (or call
other procedures that call LANLIB); ipxRouteFind() is using LANLIB at the
moment; LANLIB calls should wait until after ipxRouteFind() has finished.

This procedure can be written in any language, but it must follow the C
calling convention.  Its lone parameter will be on the stack at the time it
is called, it must put its return value in the AX register, and
ipxRouteFind() will clean up the stack afterwards.  It doesn't have to save
any registers except SP, BP, SI, DI, DS, and SS.  This is all according to
the standard C calling convention.  ipxRouteFind() will access it with a near
call in small code models and with a far call in large code models, so it
must use the default return type (near or far) for the current memory model.

The LANLIB demo program LANTEST contains a procedure of this type, called
routeWait(), in the file NETWORK.C.



******************************************************************************
*                                                                            *
***  LANUTIL Routines                                                      ***
*                                                                            *
******************************************************************************

Programs using LANLIB will almost certainly have to keep track of at least a
few other PCs out on the network.  This requires keeping track of those PC's
IPX addresses, which are best kept in IPXADDRFULL structures.  You might
have to copy one address to another, compare two addresses to see if they're
the same or to sort a list, or print an address to the screen.  Since an
IPXADDRFULL structure is large and complicated, these sorts of tasks are made
more difficult.  This is exactly the problem I found myself up against when
I wrote LANTEST.  The LANUTIL routines are the solution I came up with.

The LANUTIL routines are written in C and the source code is included in the
file LANUTIL.C.  You can use or modify this code as you see fit.



ipxAddrBrd()
------------

Prototype: struct IPXADDRFULL *ipxAddrBrd(struct IPXADDRFULL *ipxAddr);

This procedure sets the node address and immediate address fields in an
IPXADDR structure to FFFFFFFFFFFFh, which makes the address suitable for
broadcasting packets to all PCs on the local network segment.  It does
nothing at all to the network and socket fields, so those values should
already be set before performing this call.

A convenient way to set up an IPXADDRFULL structure suitable for
broadcasting packets on your local segment is to first get your local
address from the LANLIB procedure ipxAddrLocal(), then pass that address
to ipxAddrBrd() to set the node and immediate address fields.

Note that if you broadcast a packet to all PCs on your network segment,
you yourself will also get a copy of that packet.

This procedure returns a pointer to the IPXADDRFULL structure.



ipxAddrCmp()
------------

Prototype: int ipxAddrCmp(struct IPXADDRFULL *ipxAddr1,
                          struct IPXADDRFULL *ipxAddr2)

This procedure compares the network, node, and socket numbers of two
IPXADDRFULL structures.  It can be used to sort addresses into numerical
order or simply to determine if two addresses are identical.  It returns
-1 if the first address is less than the second, 0 if the addresses are
the same, or 1 if the first address is greater than the second.



ipxAddrCpy()
------------

Prototype: struct IPXADDRFULL *ipxAddrCpy(struct IPXADDRFULL *ipxAddrDst,
                                          struct IPXADDRFULL *ipxAddrSrc)

This procedure copies the entire contents of one IPXADDRFULL structure
to another.  It returns a pointer to the destination address.



ipxAddrSocket()
---------------

Prototype: struct IPXADDRFULL *ipxAddrSocket(struct IPXADDRFULL *ipxAddr,
                                             word socket)

This procedure accepts a pointer to an IPXADDRFULL structure and a socket
number in normal Intel low-high format.  It flips the socket number to
high-low format, as required by IPX, and puts it in the structure.  It
returns a pointer to the structure.



ipxAddrStr()
------------

Prototype: char *ipxAddrStr(char *str, struct IPXADDRFULL *ipxAddr)

This procedure creates a string representation of the network and node
address in an IPXADDRFULL structure in the string pointed to by "str"
in the format xxxxxxxx:yyyyyyyyyyyy, where the x's are the network number
and the y's are the node number.  The string given as "str" must contain at
least 22 bytes.  The network and node are all that's needed to uniquely
identify one PC from any other, so many of NetWare's own utilites use
this format for displaying addresses.  It returns a pointer to the string.



ipxAddrStrLong()
----------------

Prototype: char *ipxAddrStrLong(char *str, struct IPXADDRFULL *ipxAddr)

This procedure creates a string representation of all four components of
the IPXADDRFULL structure, all separated by colons.  The string given as
"str" must contain at least 40 bytes.  This is probably not too useful
for production programs, but it sometimes comes in handy for debugging.  It
returns a pointer to the string.



ipxByteSwap()
-------------

Prototype: word ipxByteSwap(word socket)

This procedure takes a word parameter (typically a socket number), swaps
its high byte with its low byte, and returns the modified value.  This
will convert a socket number in low-high Intel format to high-low Motorola
format, or vice versa.  This can be useful because IPX requires that the
socket field of IPXADDRFULLs be maintained in high-low (Motorola) format.



******************************************************************************
*                                                                            *
***  The Future of LANLIB                                                  ***
*                                                                            *
******************************************************************************

I am planning to do a 32-bit version of LANLIB compatible with the Watcom
32-bit compiler.

I expect to eventually do a NetBIOS library as well, devise a common API
layer that both the NetBIOS version and LANLIB will share, and figure out
a way that a program can load one or the other but not both at runtime.
Eventually I might also round out the situation with a serial communication
library that uses the same API and can be loaded in place of one of the
network drivers.

I may also do a Windows version of LANLIB, and also the NetBIOS library.

If you have an interest in any of these potential projects, please let me
know.  User interest will play a big part in whether or not I get around
to them.

If you would like to do something with LANLIB that I haven't taken into
account, such as use it with a different compiler, or with overlays, or
whatever, let me know.  If it's feasible, I can probably be talked into it.


Allen Brunson
6500 East 21st #2
Wichita, Kansas  67206
U.S.A.

CompuServe: 74464,3472
Internet: 74464.3472@compuserve.com



******************************************************************************
*                                                                            *
***  Special Thanks                                                        ***
*                                                                            *
******************************************************************************

Like any good piece of software, LANLIB was not created in a vacuum.  I had
a lot of help and feedback from many people.  Two in particular stand out and
deserve special mention:

Bart Stewart.  He performed countless tests on my behalf using his company's
huge NetWare internetwork and various buggy versions of LANTEST.EXE.  His
knowledge of and experience with IPX diagnostics directly shaped the
architecture of LANLIB.

Doug Goldner.  Unless somebody else gets into an EXTREME hurry, it looks like
he will produce the first ever LANLIB-using program, a text-based adventure
game called Multi-Venture that uses a client-server architecture.  He figured
out tricks to using LANLIB that didn't even occur to me, and in the process
helped validate the whole LANLIB concept.  You can contact Doug about his
game at his CIS address, 76702,1257.



******************************************************************************
*                                                                            *
***  Release History                                                       ***
*                                                                            *
******************************************************************************


Version 1.00, June 30, 1994

First public general release.

Changes:

  * A little legalese, courtesy of my mom the lawyer, is about all that's
    new.


Version 0.40, June 23, 1994

Fourth public beta release.

Changes:

  * Novell's legal department wouldn't let me use their trademark in my
    product's name, so I can't call it IPXLIB; now it's LANLIB, and all
    the files and so on were renamed.

  * LANTEST (formerly IPXTEST) can now write its network info to a file.
    This is useful (to me, at least) for debugging ipxRouteFind() if it
    fails to find all network segments on a given internetwork.

  * I added the procedure ipxAddrImmed(), which calls IPX to get the
    preferred immediate address for a given network address, and one
    new error value for it, ipxeNOPATHFOUND.  ipxRouteFind() and LANTEST
    were both upgraded to use it.

  * I added a new error value, ipxeMEMABOVE64K, which ipxStart() will
    now return if the amount of memory required is above 64k (an error).


Version 0.30, June 10, 1994

Third public beta release.

Changes:

  * IPX.H was renamed IPXLIB.H, IPX.ASM was renamed IPXLIB.ASM, and the
    .LIB files were also appropriately renamed.

  * The define IPXMEMSIZE was added to IPXLIB.H to calculate the memory
    size needed to pass to ipxStart().

  * IPXLIB.INC and IPXCFG.INC, the assembler versions of IPXLIB.H and
    IPXCFG.H, were added for programmers wishing to use IPXLIB with
    assembler programs.

  * I finally finished ipxRouteFind(), the procedure that finds network
    segments, and added one more IPXLIB error value for it,
    ipxeROUTEFINDABORT.  I got rid of the never-used define IPXHOPS and
    replaced it with the new IPXNETCNT, given as a parameter to
    ipxRouteFind().  IPXTEST was upgraded to use the new procedure.
    "Dealing with Network Segments" was added to the documentation.

  * ipxeBADCOMMPARMS now also applies to ipxRouteFind(), which will
    return this value if the communication parameters aren't to its
    liking.

  * I added two new procedures in IPXUTIL.C for dealing with
    IPXADDRFULLs, ipxAddrSocket() and ipxByteSwap().


Version 0.20, June 1, 1994

Second public beta release.

Changes:

  * The code in IPX.ASM was streamlined considerably, eliminating many
    small bugs.

  * ipxStart() was changed from taking no parameters to taking all
    communication parameters, including a far pointer to memory to
    use for ECBs, buffers, and so on.  IPXTEST was modified so that
    if DEBUG is defined it passes a pointer to an IPXDATA structure
    to use for communication memory; it will allocate memory from
    the heap instead if DEBUG is not defined.

  * IPXDATA.C was eliminated; all communication data is now passed as
    variables to ipxStart(), including a pointer to memory that is used
    for the data that formerly was in this file.

  * Code was added to keep track of the maximum number of ECBs that have
    been used.  IPXTEST was upgraded to display or reset this information
    and to send it to other IPXTEST users.

  * Two new IPXLIB error codes were added, ipxeBADCOMMPARMS and
    ipxeMEMTOOSMALL, which ipxStart() can return if the communication
    parameters given are wrong or if the size of the memory block given
    isn't big enough to hold everything, respectively.  This shifted the
    values of all the other error codes.

  * The ECB pointer field in the IPXEVENT structure was changed from a
    far (4-byte) to a near (2-byte) pointer.

  * IPXLIB was tested with Microsoft C++ version 8.0 and proven to work
    in all memory models except Tiny.  IPXTEST was modified to work with
    Microsoft C as well, although a few minor things didn't get ported,
    like the variable-length wait before sending a ping response and
    screen scrolling, because there weren't functions in the Microsoft
    run-time libraries equivalent to the ones I used in Borland C.

  * "Flurry mode," that is to say, sending as many packets as time will
    allow, was added to IPXTEST, so as to stress-test IPXLIB.

  * IPXTEST can now be "remote controlled:" It attempts to interpret the
    text of broadcast messages and directed messages as commands.

  * Several extra IPX programming considerations were added to the IPXTEST
    documentation.


Version 0.10, May 27, 1994

First public beta release.
