mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2064 lines
76 KiB
2064 lines
76 KiB
/* ddp.c, /appletalk/source, Garth Conboy, 10/05/88 */
|
|
/* Copyright (c) 1988 by Pacer Software Inc., La Jolla, CA */
|
|
|
|
/* GC - Initial coding.
|
|
GC - (12/03/89): AppleTalk phase II comes to town. This routine
|
|
no longer plays router bondage.
|
|
GC - (07/19/90): Gather send support; inline assembly for OS2 checksums.
|
|
GC - (08/03/90): Shortened rev history.
|
|
GC - (08/18/90): New error logging mechanism.
|
|
GC - (10/08/90): Half port support.
|
|
GC - (10/16/90): For LimitedStackSpace environments, leave packets/timers
|
|
defered when we're actually invoking socket handlers.
|
|
This is evil, but it will prevent defered packets from
|
|
being handled when the stack is too deep.
|
|
GC - (01/27/91): When we're deferring DDP datagrams (from DeliverDdp) we
|
|
also need to save the requested zoneMulticast address.
|
|
GC - (01/19/92): Newest OS/2 integration: source routing info in best
|
|
router cache.
|
|
GC - (03/30/92): TransmitDdp() and DeliverDdp() now take a BufferDescriptor
|
|
rather than a "char *". Completely new code for packet
|
|
header formulation. I like these routines alot better
|
|
now, so these have got to be good changes!
|
|
GC - (04/04/92): In order for Arap to handle incoming packets a routine
|
|
like DeliverDdp needs to be used, but the packets aren't
|
|
coming from sockets that are open in this stack. So,
|
|
the new rotuine DeliverDdpOnPort has been added. This
|
|
rotuine is now called by both DeliverDdp and Arap to
|
|
handle delivery to nodes within a port, and failing that
|
|
either hand the packet over to the router (if available)
|
|
or actually send the packet with TransmitDdp(). God, I
|
|
hate to tear up code thats been working for so long!
|
|
GC - (05/14/92): Corrected a potentially serious bug that could cause
|
|
delivery of multiple copies of a packet on ports that
|
|
have multiple nodes active... this bug didn't show up in
|
|
the "real world" for a number of reasons, but it sure
|
|
could. Bug found by Brian Goetz (by inspection, to
|
|
make it worse!).
|
|
GC - (06/25/92): Some modifications to match more advanced buffer
|
|
descriptor mechanism. Use of AdjustBufferDescriptor().
|
|
GC - (06/30/92): Transmit complete notification support for the various
|
|
Ddp send routines.
|
|
GC - (07/07/92): Added "freePacket" argument to DdpPacketIn().
|
|
GC - (11/15/92): Integrated Nikki's (Microsoft) changes for Ddp event
|
|
handler support. See "socket.h" for more information.
|
|
GC - (11/28/92): Locks and reference counts come to town.
|
|
|
|
*** Make the PVCS source control system happy:
|
|
$Header$
|
|
$Log$
|
|
***
|
|
|
|
Note: InvokeSocketHandler is an unbalanced routine: it expects to be
|
|
entered with packets/timers deferred; it will undefer before
|
|
actually invoking the socket handler!
|
|
|
|
DDP handling.
|
|
|
|
*/
|
|
|
|
#define IncludeDdpErrors 1
|
|
|
|
#include "atalk.h"
|
|
|
|
#if not defined(DeferIncomingPackets)
|
|
/* Deferred DDP packet queue (DdpPacketIn): */
|
|
|
|
#define MaximumDeferredDdpPackets 10
|
|
typedef struct dlp { struct dlp far *next;
|
|
int port;
|
|
short length;
|
|
Boolean freePacket;
|
|
Boolean extendedDdpHeader;
|
|
int alapSourceNode;
|
|
int alapDestinationNode;
|
|
char far *packet;
|
|
char packetData[1];
|
|
} far *DeferredDdpPacket;
|
|
static volatile DeferredDdpPacket headOfDeferredDdpPacketList = empty;
|
|
static volatile DeferredDdpPacket tailOfDeferredDdpPacketList = empty;
|
|
volatile short currentDeferredDdpPacketCount = 0;
|
|
|
|
/* Deferred DDP datagram queue (DeliverDdp): */
|
|
|
|
#define MaximumDeferredDdpDatagrams 20
|
|
typedef struct ddp { struct ddp far *next;
|
|
int sourcePort;
|
|
AppleTalkAddress source;
|
|
AppleTalkAddress destination;
|
|
short protocol;
|
|
char far *zoneMulticastAddress;
|
|
BufferDescriptor datagram;
|
|
short datagramLength;
|
|
TransmitCompleteHandler *completionRoutine;
|
|
long unsigned userData;
|
|
} far *DeferredDdpDatagram;
|
|
static volatile DeferredDdpDatagram headOfDeferredDdpDatagramList = empty;
|
|
static volatile DeferredDdpDatagram tailOfDeferredDdpDatagramList = empty;
|
|
volatile short currentDeferredDdpDatagramCount = 0;
|
|
|
|
static volatile short deferIncomingPacketsCount;
|
|
static volatile short handleIncomingPacketsNesting;
|
|
#endif
|
|
|
|
/* If we can't find a destination hardware address, we queue the packet to
|
|
retry it a little later. The following is the retry node and timer value: */
|
|
|
|
typedef struct { BufferDescriptor packet;
|
|
int packetLength;
|
|
int port;
|
|
ExtendedAppleTalkNodeNumber actualDestination;
|
|
AppleTalkAddress source;
|
|
TransmitCompleteHandler *completionRoutine;
|
|
long unsigned userData;
|
|
} far *RetryNode;
|
|
|
|
#define RetryTimerSeconds 2
|
|
|
|
ExternForVisibleFunction TimerHandler RetryTimerExpired;
|
|
|
|
ExternForVisibleFunction BestRouterEntry far
|
|
*FindInBestRouterCache(int port,
|
|
AppleTalkAddress destination);
|
|
|
|
ExternForVisibleFunction Boolean
|
|
SendPacket(BufferDescriptor packet,
|
|
int packetLength,
|
|
int port,
|
|
char far *knownAddress,
|
|
char far *knownRoutingInfo,
|
|
int knownRoutingInfoLength,
|
|
ExtendedAppleTalkNodeNumber actualDestination,
|
|
Boolean broadcast,
|
|
AppleTalkAddress source,
|
|
Boolean retry,
|
|
TransmitCompleteHandler *completionRoutine,
|
|
long unsigned userData);
|
|
|
|
/* The last two arguments are only valid if "not extendedDdpHeader" (i.e.
|
|
the packet is from LocalTalk and we need some information from the LAP
|
|
header).
|
|
|
|
The "freePacket" argument must be set by our caller and indicates whether
|
|
we need to free the passed "packet" when we're finished with it. If this
|
|
argument is set to true we can avoid a buffer-copy when packets are being
|
|
deferred (we simply save this pointer, and free the packet later when it
|
|
has really be handled). This argument may only be set to True if we can
|
|
defer processing (and freeing) the packet until AFTER DeliverDdp has
|
|
returned. */
|
|
|
|
void _near _fastcall DdpPacketIn(int port,
|
|
char far *packet,
|
|
int length,
|
|
Boolean freePacket,
|
|
Boolean extendedDdpHeader,
|
|
int alapSourceNode,
|
|
int alapDestinationNode)
|
|
{
|
|
int datagramLength, protocolType;
|
|
AppleTalkAddress source, destination;
|
|
char far *datagram;
|
|
OpenSocket openSocket;
|
|
unsigned short checksum;
|
|
Boolean broadcastNode = False, shouldBeRouted = False, delivered = False;
|
|
#if not defined(DeferIncomingPackets)
|
|
DeferredDdpPacket deferredDdpPacket;
|
|
#endif
|
|
ActiveNode activeNode, nextActiveNode;
|
|
int numberOfHops = 0;
|
|
BufferDescriptor chain;
|
|
|
|
/* Check for a few obvious problems. */
|
|
|
|
if (not appleTalkRunning or not PortDescriptor(port)->portActive)
|
|
{
|
|
if (freePacket)
|
|
Free(packet);
|
|
return;
|
|
}
|
|
if (length > MaximumLongDdpPacketSize)
|
|
{
|
|
ErrorLog("DdpPacketIn", ISevVerbose, __LINE__, port,
|
|
IErrDdpPacketTooLong, IMsgDdpPacketTooLong,
|
|
Insert0());
|
|
if (freePacket)
|
|
Free(packet);
|
|
return;
|
|
}
|
|
|
|
#if not defined(DeferIncomingPackets)
|
|
/* If we're ignoring packets now, place the incoming packet on the deferred
|
|
packet list. */
|
|
|
|
EnterCriticalSection();
|
|
if (deferIncomingPacketsCount > 0)
|
|
{
|
|
if (currentDeferredDdpPacketCount is MaximumDeferredDdpPackets)
|
|
{
|
|
LeaveCriticalSection();
|
|
ErrorLog("DdpPacketIn", ISevWarning, __LINE__, port,
|
|
IErrDdpLosingData, IMsgDdpLosingData,
|
|
Insert0());
|
|
if (freePacket)
|
|
Free(packet);
|
|
return;
|
|
}
|
|
LeaveCriticalSection();
|
|
if (freePacket)
|
|
deferredDdpPacket =
|
|
(DeferredDdpPacket)Malloc(sizeof(*deferredDdpPacket));
|
|
else
|
|
deferredDdpPacket =
|
|
(DeferredDdpPacket)Malloc(sizeof(*deferredDdpPacket) +
|
|
MaximumHeaderLength +
|
|
length);
|
|
if (deferredDdpPacket is empty)
|
|
{
|
|
ErrorLog("DdpPacketIn", ISevError, __LINE__, port,
|
|
IErrDdpOutOfMemory, IMsgDdpOutOfMemory,
|
|
Insert0());
|
|
if (freePacket)
|
|
Free(packet);
|
|
return;
|
|
}
|
|
|
|
/* Fill in the data strcuture, and place the packet at the end of the
|
|
queue. */
|
|
|
|
deferredDdpPacket->next = empty;
|
|
deferredDdpPacket->port = port;
|
|
deferredDdpPacket->length = (short)length;
|
|
deferredDdpPacket->freePacket = freePacket;
|
|
deferredDdpPacket->extendedDdpHeader = extendedDdpHeader;
|
|
deferredDdpPacket->alapSourceNode = alapSourceNode;
|
|
deferredDdpPacket->alapDestinationNode = alapDestinationNode;
|
|
if (freePacket)
|
|
deferredDdpPacket->packet = packet;
|
|
else
|
|
{
|
|
MoveMem(deferredDdpPacket->packetData + MaximumHeaderLength, packet,
|
|
length);
|
|
deferredDdpPacket->packet = deferredDdpPacket->packetData +
|
|
MaximumHeaderLength;
|
|
}
|
|
|
|
EnterCriticalSection();
|
|
if (tailOfDeferredDdpPacketList is empty)
|
|
tailOfDeferredDdpPacketList = headOfDeferredDdpPacketList =
|
|
deferredDdpPacket;
|
|
else
|
|
{
|
|
tailOfDeferredDdpPacketList->next = deferredDdpPacket;
|
|
tailOfDeferredDdpPacketList = deferredDdpPacket;
|
|
}
|
|
|
|
/* All set... return. */
|
|
|
|
currentDeferredDdpPacketCount += 1;
|
|
LeaveCriticalSection();
|
|
|
|
return;
|
|
}
|
|
else
|
|
LeaveCriticalSection();
|
|
#endif
|
|
|
|
/* If the port has no nodes currently operating, then we can ignore this
|
|
guy now! */
|
|
|
|
TakeLock(DdpLock);
|
|
if (PortDescriptor(port)->activeNodes is empty)
|
|
{
|
|
ReleaseLock(DdpLock);
|
|
if (freePacket)
|
|
Free(packet);
|
|
return;
|
|
}
|
|
|
|
/* In the non-extended header case, we need to hold onto an active node
|
|
to build full addressing information. */
|
|
|
|
if (not extendedDdpHeader)
|
|
activeNode = Link(PortDescriptor(port)->activeNodes);
|
|
ReleaseLock(DdpLock);
|
|
|
|
/* Short and long header formats have the length in the same place,
|
|
so we're okay with the next expression... Thought you caught me,
|
|
but nooooo. */
|
|
|
|
datagramLength = ((packet[LongDdpLengthOffset] & 03) << 8) +
|
|
(unsigned char)packet[LongDdpLengthOffset + 1];
|
|
numberOfHops = ((packet[LongDdpLengthOffset] >> 2) & 0x0F);
|
|
|
|
/* Packet too long? */
|
|
|
|
if (datagramLength > length)
|
|
{
|
|
ErrorLog("DdpPacketIn", ISevWarning, __LINE__, port,
|
|
IErrDdpLengthCorrupted, IMsgDdpLengthCorrupted,
|
|
Insert0());
|
|
if (not extendedDdpHeader)
|
|
UnlinkActiveNode(activeNode);
|
|
if (freePacket)
|
|
Free(packet);
|
|
return;
|
|
}
|
|
|
|
/* Demystify the DDP header. */
|
|
|
|
if (not extendedDdpHeader)
|
|
{
|
|
/* Short DDP header! */
|
|
|
|
if (PortDescriptor(port)->extendedNetwork)
|
|
{
|
|
UnlinkActiveNode(activeNode);
|
|
ErrorLog("DdpPacketIn", ISevWarning, __LINE__, port,
|
|
IErrDdpShortDdp, IMsgDdpShortDdp,
|
|
Insert0());
|
|
if (freePacket)
|
|
Free(packet);
|
|
return;
|
|
}
|
|
if (datagramLength < ShortDdpHeaderLength or
|
|
datagramLength > MaximumDdpDatagramSize + ShortDdpHeaderLength)
|
|
{
|
|
UnlinkActiveNode(activeNode);
|
|
ErrorLog("DdpPacketIn", ISevWarning, __LINE__, port,
|
|
IErrDdpShortDdpTooLong, IMsgDdpShortDdpTooLong,
|
|
Insert0());
|
|
if (freePacket)
|
|
Free(packet);
|
|
return;
|
|
}
|
|
|
|
/* Build complete source and destination addresses from a combination
|
|
of the port and packet descriptors. */
|
|
|
|
source.networkNumber =
|
|
PortDescriptor(port)->activeNodes->extendedNode.networkNumber;
|
|
source.nodeNumber = (unsigned char)alapSourceNode;
|
|
source.socketNumber =
|
|
(unsigned char)packet[ShortDdpSourceSocketOffset];
|
|
destination.networkNumber = source.networkNumber;
|
|
if (alapDestinationNode is AppleTalkBroadcastNodeNumber)
|
|
destination.nodeNumber =
|
|
PortDescriptor(port)->activeNodes->extendedNode.nodeNumber;
|
|
else
|
|
destination.nodeNumber = (unsigned char)alapDestinationNode;
|
|
destination.socketNumber =
|
|
(unsigned char)packet[ShortDdpDestSocketOffset];
|
|
|
|
/* Do we like it? */
|
|
|
|
if (source.nodeNumber < MinimumUsableAppleTalkNode or
|
|
source.nodeNumber > MaximumUsableAppleTalkNode)
|
|
{
|
|
UnlinkActiveNode(activeNode);
|
|
ErrorLog("DdpPacketIn", ISevWarning, __LINE__, port,
|
|
IErrDdpBadSourceShort, IMsgDdpBadSourceShort,
|
|
Insert0());
|
|
if (freePacket)
|
|
Free(packet);
|
|
return;
|
|
}
|
|
if (destination.nodeNumber < MinimumUsableAppleTalkNode or
|
|
destination.nodeNumber isnt
|
|
PortDescriptor(port)->activeNodes->extendedNode.nodeNumber)
|
|
{
|
|
UnlinkActiveNode(activeNode);
|
|
ErrorLog("DdpPacketIn", ISevWarning, __LINE__, port,
|
|
IErrDdpBadDestShort, IMsgDdpBadDestShort,
|
|
Insert0());
|
|
if (freePacket)
|
|
Free(packet);
|
|
return;
|
|
}
|
|
|
|
/* Get protocol type and datagram start address. */
|
|
|
|
protocolType = (unsigned char)packet[ShortDdpProtocolTypeOffset];
|
|
datagram = &packet[ShortDdpDatagramOffset];
|
|
|
|
/* Route the packet to the open socket on the incoming port. */
|
|
|
|
UnlinkActiveNode(activeNode);
|
|
DeferTimerChecking();
|
|
DeferIncomingPackets();
|
|
if ((openSocket = MapAddressToOpenSocket(port, destination)) isnt empty)
|
|
{
|
|
InvokeSocketHandler(openSocket, port, source,
|
|
protocolType, datagram,
|
|
datagramLength - ShortDdpHeaderLength,
|
|
destination);
|
|
UnlinkOpenSocket(openSocket);
|
|
}
|
|
else
|
|
{
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Extended DDP header! */
|
|
|
|
if (datagramLength < LongDdpHeaderLength or
|
|
datagramLength > MaximumDdpDatagramSize + LongDdpHeaderLength)
|
|
{
|
|
ErrorLog("DdpPacketIn", ISevWarning, __LINE__, port,
|
|
IErrDdpLongDdpTooLong, IMsgDdpLongDdpTooLong,
|
|
Insert0());
|
|
if (freePacket)
|
|
Free(packet);
|
|
return;
|
|
}
|
|
|
|
/* Do checksum, verification, if needed. */
|
|
|
|
checksum = (unsigned short)
|
|
(((unsigned char)packet[LongDdpChecksumOffset] << 8) +
|
|
(unsigned char)packet[LongDdpChecksumOffset + 1]);
|
|
if (checksum isnt 0)
|
|
{
|
|
static struct buffDesc descriptor;
|
|
|
|
/* Build a dummy buffer descriptor, checksum the "chain", drop the
|
|
packet on the floor if we don't have a checksum match. */
|
|
|
|
descriptor.outBoardDataValid = True;
|
|
descriptor.outBoardBuffer = packet;
|
|
descriptor.outBoardData = packet;
|
|
descriptor.outBoardAllocatedSize = datagramLength;
|
|
descriptor.outBoardSize = datagramLength;
|
|
if (checksum isnt DdpChecksumBufferChain(&descriptor, datagramLength,
|
|
LeadingUnChecksumedBytes))
|
|
{
|
|
if (freePacket)
|
|
Free(packet);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Build full source and destination AppleTalk address structures
|
|
from our DDP header. */
|
|
|
|
source.networkNumber = (unsigned short)
|
|
(((unsigned char)packet[LongDdpSourceNetworkOffset] << 8) +
|
|
(unsigned char)packet[LongDdpSourceNetworkOffset + 1]);
|
|
source.nodeNumber =
|
|
(unsigned char)packet[LongDdpSourceNodeOffset];
|
|
source.socketNumber =
|
|
(unsigned char)packet[LongDdpSourceSocketOffset];
|
|
destination.networkNumber = (unsigned short)
|
|
(((unsigned char)packet[LongDdpDestNetworkOffset] << 8) +
|
|
(unsigned char)packet[LongDdpDestNetworkOffset + 1]);
|
|
destination.nodeNumber =
|
|
(unsigned char)packet[LongDdpDestNodeOffset];
|
|
destination.socketNumber =
|
|
(unsigned char)packet[LongDdpDestSocketOffset];
|
|
|
|
broadcastNode = (destination.nodeNumber is AppleTalkBroadcastNodeNumber);
|
|
|
|
/* Do we like what we see? Note "nnnn00" is now allowed and used by
|
|
NBP. */
|
|
|
|
if (PortDescriptor(port)->portType isnt NonAppleTalkHalfPort)
|
|
if (source.networkNumber > LastValidNetworkNumber or
|
|
source.networkNumber < FirstValidNetworkNumber or
|
|
source.nodeNumber < MinimumUsableAppleTalkNode or
|
|
source.nodeNumber > MaximumUsableAppleTalkNode)
|
|
{
|
|
ErrorLog("DdpPacketIn", ISevWarning, __LINE__, port,
|
|
IErrDdpBadSourceLong, IMsgDdpBadSourceLong,
|
|
Insert0());
|
|
if (freePacket)
|
|
Free(packet);
|
|
return;
|
|
}
|
|
if (destination.networkNumber > LastValidNetworkNumber or
|
|
(destination.nodeNumber > MaximumUsableAppleTalkNode and
|
|
not broadcastNode))
|
|
{
|
|
ErrorLog("DdpPacketIn", ISevWarning, __LINE__, port,
|
|
IErrDdpBadDestLong, IMsgDdpBadDestLong,
|
|
Insert0());
|
|
if (freePacket)
|
|
Free(packet);
|
|
return;
|
|
}
|
|
|
|
/* Get protocol type and datagram start address. */
|
|
|
|
protocolType = (unsigned char)packet[LongDdpProtocolTypeOffset];
|
|
datagram = &packet[LongDdpDatagramOffset];
|
|
|
|
/* Loop through all nodes that are on the reception port and see if
|
|
anybody wants this packet. The algorithm is from the "AppleTalk
|
|
Phase 2 Protocol Specification" with enhacements to support ports
|
|
that have multiple nodes. */
|
|
|
|
TakeLock(DdpLock);
|
|
activeNode = Link(PortDescriptor(port)->activeNodes);
|
|
ReleaseLock(DdpLock);
|
|
while (activeNode isnt Empty)
|
|
{
|
|
while (True)
|
|
{
|
|
/* "0000xx" (where "xx" isnt "FF") should not be accepted on an
|
|
extended port... For some unknown reason, the spec would like
|
|
us to pass this case onto the router (which will, no doubt,
|
|
drop it on the floor because it won't find network zero in its
|
|
routing table)... you know, bug-for-bug compatible! */
|
|
|
|
if (destination.networkNumber is UnknownNetworkNumber and
|
|
PortDescriptor(port)->extendedNetwork and
|
|
not broadcastNode)
|
|
{
|
|
shouldBeRouted = True;
|
|
break;
|
|
}
|
|
|
|
/* Is the packet for us? Destination net is zero, or destination net
|
|
is our node's net, or we're non extended and our node's net is
|
|
zero. Otherwise, we may want to try to route the packet. */
|
|
|
|
if ((destination.networkNumber is UnknownNetworkNumber or
|
|
destination.networkNumber is
|
|
activeNode->extendedNode.networkNumber or
|
|
(not PortDescriptor(port)->extendedNetwork and
|
|
activeNode->extendedNode.networkNumber is UnknownNetworkNumber))
|
|
and (broadcastNode or
|
|
destination.nodeNumber is activeNode->extendedNode.nodeNumber))
|
|
{
|
|
/* If we're aimed at a proxy node (one that is the node used by an
|
|
active remote access port), send the packet out the proxy port.
|
|
Copy the datagram... transmits may not complete synchronously. */
|
|
|
|
if (activeNode->proxyNode)
|
|
{
|
|
if ((chain = NewBufferDescriptor(datagramLength -
|
|
LongDdpHeaderLength)) is Empty)
|
|
{
|
|
ErrorLog("DdpPacketIn", ISevError, __LINE__, port,
|
|
IErrDdpOutOfMemory, IMsgDdpOutOfMemory,
|
|
Insert0());
|
|
break;
|
|
}
|
|
MoveMem(chain->data, datagram, datagramLength -
|
|
LongDdpHeaderLength);
|
|
if (not TransmitDdp(activeNode->proxyPort, source, destination,
|
|
protocolType, chain, datagramLength -
|
|
LongDdpHeaderLength, numberOfHops,
|
|
Empty, Empty, Empty, 0))
|
|
ErrorLog("DdpPacketIn", ISevWarning, __LINE__,
|
|
activeNode->proxyPort,
|
|
IErrDdpForwardError, IMsgDdpForwardError,
|
|
Insert0());
|
|
delivered = True;
|
|
break;
|
|
}
|
|
|
|
/* Is the socket open on the current node? Broadcast trys all
|
|
nodes. */
|
|
|
|
if (broadcastNode)
|
|
destination.nodeNumber = activeNode->extendedNode.nodeNumber;
|
|
|
|
DeferTimerChecking();
|
|
DeferIncomingPackets();
|
|
if ((openSocket = MapAddressToOpenSocket(port,
|
|
destination)) isnt empty)
|
|
{
|
|
if (broadcastNode)
|
|
destination.nodeNumber = AppleTalkBroadcastNodeNumber;
|
|
InvokeSocketHandler(openSocket, port, source,
|
|
protocolType, datagram,
|
|
datagramLength - LongDdpHeaderLength,
|
|
destination);
|
|
UnlinkOpenSocket(openSocket);
|
|
delivered = True;
|
|
}
|
|
else
|
|
{
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
}
|
|
}
|
|
else
|
|
shouldBeRouted = True;
|
|
break;
|
|
}
|
|
|
|
/* Move to the next ActiveNode. */
|
|
|
|
TakeLock(DdpLock);
|
|
nextActiveNode = Link(activeNode->next);
|
|
ReleaseLock(DdpLock);
|
|
UnlinkActiveNode(activeNode);
|
|
activeNode = nextActiveNode;
|
|
}
|
|
|
|
/* If we're a router and we think that might help, give him a crack
|
|
at it. */
|
|
|
|
#if Iam an AppleTalkRouter
|
|
if (PortDescriptor(port)->routerRunning and
|
|
not delivered and shouldBeRouted)
|
|
{
|
|
#if Iam an OS2
|
|
DeferTimerChecking();
|
|
DeferIncomingPackets();
|
|
#endif
|
|
Router(port, source, destination, protocolType, datagram,
|
|
datagramLength - LongDdpHeaderLength, numberOfHops,
|
|
RouterPrependHeadersInPlace);
|
|
#if Iam an OS2
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
} /* Long DDP header */
|
|
|
|
if (freePacket)
|
|
Free(packet);
|
|
return;
|
|
|
|
} /* DdpPacketIn */
|
|
|
|
/* Note: the last two arguments are used by the router to control the actual
|
|
address that the packet is sent to. As the packet is encoded, the DDP
|
|
destination will always be "destination", however if "knownMulticastAddress"
|
|
is non-empty the packet will be sent there, if "transmitDestination" is
|
|
non-empty the actual destination of this extended node will be used as the
|
|
target address (for moving a packet on to the next internet router in its
|
|
journey). */
|
|
|
|
Boolean _near _fastcall TransmitDdp(int port,
|
|
AppleTalkAddress source,
|
|
AppleTalkAddress destination,
|
|
int protocol,
|
|
BufferDescriptor datagram,
|
|
int datagramLength,
|
|
int hopCount,
|
|
char far *knownMulticastAddress,
|
|
ExtendedAppleTalkNodeNumber
|
|
*transmitDestination,
|
|
TransmitCompleteHandler *completionRoutine,
|
|
long unsigned userData)
|
|
{
|
|
PortHandlers portHandlers;
|
|
int actualLength;
|
|
short unsigned checksum;
|
|
ExtendedAppleTalkNodeNumber actualDestination;
|
|
Boolean broadcast = False, result;
|
|
char far *knownAddress = empty;
|
|
char far *knownRoutingInfo = empty;
|
|
int knownRoutingInfoLength = 0;
|
|
BestRouterEntry far *routerNode = empty;
|
|
BufferDescriptor packet = datagram;
|
|
|
|
/* The basic transmit algorithum is:
|
|
|
|
if (non-extended-network)
|
|
{
|
|
if ((destination-network is 0 or
|
|
destination-network is ThisCableRange.firstNetwork) and
|
|
(source-network is 0 or
|
|
source-network is ThisCableRange.firstNetwork))
|
|
{
|
|
<send short form DDP packet to local network>
|
|
return-okay
|
|
}
|
|
}
|
|
if (destination-network is CableWideBroadcastNetworkNumber or
|
|
destination-network in ThisCableRange or
|
|
destination-network in SartupRange or
|
|
{
|
|
<send long form DDP packet to local network>
|
|
return-okay
|
|
}
|
|
if (destination-network-and-node in best-router-cache)
|
|
{
|
|
<send long form DDP packet to best router>
|
|
return-okay
|
|
}
|
|
if (seen-a-router-recently)
|
|
{
|
|
<send long form DDP packet to a-router>
|
|
return-okay
|
|
}
|
|
return-error
|
|
*/
|
|
|
|
datagram->transmitCompleteHandler = completionRoutine;
|
|
datagram->userData = userData;
|
|
if (not appleTalkRunning or not PortDescriptor(port)->portActive)
|
|
{
|
|
datagram->errorCode = ATtransmitError;
|
|
TransmitComplete(datagram);
|
|
return(False);
|
|
}
|
|
portHandlers = &portSpecificInfo[PortDescriptor(port)->portType];
|
|
|
|
/* First, make sure we like our arguments. */
|
|
|
|
if (destination.networkNumber > LastValidNetworkNumber or
|
|
(destination.nodeNumber > MaximumUsableAppleTalkNode and
|
|
destination.nodeNumber isnt AppleTalkBroadcastNodeNumber) or
|
|
destination.socketNumber < FirstStaticSocket or
|
|
destination.socketNumber > LastValidSocket)
|
|
{
|
|
ErrorLog("TransmitDdp", ISevWarning, __LINE__, port,
|
|
IErrDdpBadDest, IMsgDdpBadDest,
|
|
Insert0());
|
|
datagram->errorCode = ATtransmitError;
|
|
TransmitComplete(datagram);
|
|
return(False);
|
|
}
|
|
if (PortDescriptor(port)->portType isnt NonAppleTalkHalfPort)
|
|
if (source.networkNumber > LastValidNetworkNumber or
|
|
source.nodeNumber < MinimumUsableAppleTalkNode or
|
|
source.nodeNumber > MaximumUsableAppleTalkNode or
|
|
source.socketNumber < FirstStaticSocket or
|
|
source.socketNumber > LastValidSocket)
|
|
{
|
|
ErrorLog("TransmitDdp", ISevWarning, __LINE__, port,
|
|
IErrDdpBadSource, IMsgDdpBadSource,
|
|
Insert0());
|
|
datagram->errorCode = ATtransmitError;
|
|
TransmitComplete(datagram);
|
|
return(False);
|
|
}
|
|
|
|
/* We may get packets targetted to a proxy port when remote access is not
|
|
active on the port (such as incoming NBP lookups); discard these now! */
|
|
|
|
if (PortDescriptor(port)->portType is AppleTalkRemoteAccess and
|
|
PortDescriptor(port)->remoteAccessInfo->state isnt ArapActive)
|
|
{
|
|
TransmitComplete(datagram);
|
|
return(True);
|
|
}
|
|
|
|
/* Allocate a header as large as we could possibly need. */
|
|
|
|
if ((packet = AllocateHeader(datagram, MaximumHeaderLength +
|
|
LongDdpHeaderLength)) is Empty)
|
|
{
|
|
ErrorLog("TransmitDdp", ISevError, __LINE__, port,
|
|
IErrDdpOutOfMemory, IMsgDdpOutOfMemory,
|
|
Insert0());
|
|
datagram->errorCode = ATtransmitError;
|
|
TransmitComplete(datagram);
|
|
return(False);
|
|
}
|
|
|
|
/* For non-extended networks, we may want to send a short DDP header. */
|
|
|
|
if (not PortDescriptor(port)->extendedNetwork and
|
|
(destination.networkNumber is UnknownNetworkNumber or
|
|
destination.networkNumber is
|
|
PortDescriptor(port)->thisCableRange.firstNetworkNumber) and
|
|
(source.networkNumber is UnknownNetworkNumber or
|
|
source.networkNumber is
|
|
PortDescriptor(port)->thisCableRange.firstNetworkNumber))
|
|
{
|
|
/* Move our buffer descriptor data pointer to where we want to start
|
|
building the short Ddp header. Mark "not in use" all but what we'll
|
|
fill now... the ShortDdpHeader. */
|
|
|
|
AdjustBufferDescriptor(packet, -(MaximumHeaderLength +
|
|
LongDdpHeaderLength -
|
|
ShortDdpHeaderLength));
|
|
actualLength = datagramLength + ShortDdpHeaderLength;
|
|
|
|
packet->data[ShortDdpLengthOffset] = (char)((actualLength >> 8) & 0x03);
|
|
packet->data[ShortDdpLengthOffset + 1] = (char)actualLength;
|
|
packet->data[ShortDdpDestSocketOffset] = destination.socketNumber;
|
|
packet->data[ShortDdpSourceSocketOffset] = source.socketNumber;
|
|
packet->data[ShortDdpProtocolTypeOffset] = (char)protocol;
|
|
|
|
/* Prepend LAP header. */
|
|
|
|
(*portHandlers->buildHeader)(packet->data, False, port,
|
|
&destination.nodeNumber,
|
|
empty, 0, AppleTalk);
|
|
AdjustBufferDescriptor(packet, LapHeaderLength);
|
|
actualLength += LapHeaderLength;
|
|
|
|
/* Send the packet. The PacketOut routine will handle freeing the
|
|
buffer chain. */
|
|
|
|
result = (*portHandlers->packetOut)(port, packet, actualLength,
|
|
completionRoutine, userData);
|
|
if (not result)
|
|
{
|
|
ErrorLog("TransmitDdp", ISevError, __LINE__, port,
|
|
IErrDdpBadShortSend, IMsgDdpBadShortSend,
|
|
Insert0());
|
|
return(False);
|
|
}
|
|
return(True);
|
|
}
|
|
|
|
/* Okay, were going to have to send a long Ddp datagram. Move our buffer
|
|
descriptor data pointer to where we want to start building the long Ddp
|
|
header... mark not "in use" all that we allocated except what we'll use
|
|
now... the LondDdpHeaderLength. */
|
|
|
|
AdjustBufferDescriptor(packet, -MaximumHeaderLength);
|
|
actualLength = datagramLength + LongDdpHeaderLength;
|
|
|
|
packet->data[LongDdpLengthOffset] = (char)(((hopCount & 0x0F) << 2) +
|
|
((actualLength >> 8) & 0x03));
|
|
packet->data[LongDdpLengthOffset + 1] = (char)actualLength;
|
|
packet->data[LongDdpChecksumOffset] = 0; /* Set later, if needed. */
|
|
packet->data[LongDdpChecksumOffset + 1] = 0;
|
|
packet->data[LongDdpDestNetworkOffset] = (char)(destination.networkNumber >> 8);
|
|
packet->data[LongDdpDestNetworkOffset + 1] = (char)destination.networkNumber;
|
|
packet->data[LongDdpSourceNetworkOffset] = (char)(source.networkNumber >> 8);
|
|
packet->data[LongDdpSourceNetworkOffset + 1] = (char)source.networkNumber;
|
|
packet->data[LongDdpDestNodeOffset] = destination.nodeNumber;
|
|
packet->data[LongDdpSourceNodeOffset] = source.nodeNumber;
|
|
packet->data[LongDdpDestSocketOffset] = destination.socketNumber;
|
|
packet->data[LongDdpSourceSocketOffset] = source.socketNumber;
|
|
packet->data[LongDdpProtocolTypeOffset] = (char)protocol;
|
|
|
|
/* Okay, set checksum if needed. */
|
|
|
|
if (PortDescriptor(port)->sendDdpChecksums)
|
|
{
|
|
checksum = DdpChecksumBufferChain(packet, actualLength,
|
|
LeadingUnChecksumedBytes);
|
|
|
|
packet->data[LongDdpChecksumOffset] = (char)(checksum >> 8);
|
|
packet->data[LongDdpChecksumOffset + 1] = (char)checksum;
|
|
}
|
|
|
|
/* Compute the extended AppleTalk node number that we'll really need to
|
|
send the packet to. */
|
|
|
|
TakeLock(DdpLock);
|
|
if (knownMulticastAddress isnt empty)
|
|
knownAddress = knownMulticastAddress;
|
|
else if (transmitDestination isnt empty)
|
|
actualDestination = *transmitDestination;
|
|
else if (destination.networkNumber is CableWideBroadcastNetworkNumber or
|
|
IsWithinNetworkRange(destination.networkNumber,
|
|
&PortDescriptor(port)->thisCableRange) or
|
|
IsWithinNetworkRange(destination.networkNumber,
|
|
&startupNetworkRange))
|
|
{
|
|
actualDestination.networkNumber = destination.networkNumber;
|
|
actualDestination.nodeNumber = destination.nodeNumber;
|
|
if (destination.nodeNumber is AppleTalkBroadcastNodeNumber)
|
|
broadcast = True;
|
|
}
|
|
else if (PortDescriptor(port)->portType is AppleTalkRemoteAccess or
|
|
PortDescriptor(port)->portType is NonAppleTalkHalfPort)
|
|
{
|
|
actualDestination.networkNumber = destination.networkNumber;
|
|
actualDestination.nodeNumber = destination.nodeNumber;
|
|
}
|
|
else if ((routerNode = FindInBestRouterCache(port,
|
|
destination)) isnt empty)
|
|
{
|
|
/* Okay, we know where to go. */
|
|
|
|
knownAddress = routerNode->routerAddress;
|
|
knownRoutingInfo = routerNode ->routingInfo;
|
|
knownRoutingInfoLength = routerNode->routingInfoLength;
|
|
}
|
|
else if (PortDescriptor(port)->seenRouterRecently)
|
|
actualDestination = PortDescriptor(port)->aRouter;
|
|
else
|
|
{
|
|
ReleaseLock(DdpLock);
|
|
packet->transmitCompleteHandler = completionRoutine;
|
|
packet->userData = userData;
|
|
packet->errorCode = ATnoRouterKnown;
|
|
TransmitComplete(packet);
|
|
return(False); /* No router known. */
|
|
}
|
|
ReleaseLock(DdpLock);
|
|
|
|
/* Okay, we know where we want to go... do it. The lower levels will
|
|
handle freeing the buffer chain. */
|
|
|
|
result = SendPacket(packet, actualLength, port, knownAddress,
|
|
knownRoutingInfo, knownRoutingInfoLength,
|
|
actualDestination, broadcast, source, False,
|
|
completionRoutine, userData);
|
|
|
|
if (not result)
|
|
{
|
|
ErrorLog("TransmitDdp", ISevError, __LINE__, port,
|
|
IErrDdpBadSend, IMsgDdpBadSend,
|
|
Insert0());
|
|
return(False);
|
|
}
|
|
|
|
/* The deed is done! */
|
|
|
|
return(True);
|
|
|
|
} /* TransmitDdp */
|
|
|
|
AppleTalkErrorCode far DdpRead(long socket,
|
|
void far *opaqueDatagram,
|
|
long bufferLength,
|
|
IncomingDdpHandler *handler,
|
|
long unsigned userData)
|
|
{
|
|
OpenSocket openSocket;
|
|
OutstandingDdpRead outstandingDdpRead;
|
|
|
|
/* We're going to munge with the socket structures... */
|
|
|
|
DeferTimerChecking();
|
|
DeferIncomingPackets();
|
|
|
|
openSocket = MapSocketToOpenSocket(socket);
|
|
if (openSocket is empty or openSocket->closing)
|
|
{
|
|
UnlinkOpenSocket(openSocket);
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATsocketNotOpen);
|
|
}
|
|
|
|
/* Build the structure to handle another outstating ddp read... */
|
|
|
|
if ((outstandingDdpRead =
|
|
(OutstandingDdpRead)malloc(sizeof(*outstandingDdpRead))) is empty)
|
|
{
|
|
UnlinkOpenSocket(openSocket);
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(AToutOfMemory);
|
|
}
|
|
|
|
outstandingDdpRead->opaqueDatagram = opaqueDatagram;
|
|
outstandingDdpRead->bufferLength = bufferLength;
|
|
outstandingDdpRead->handler = handler;
|
|
outstandingDdpRead->userData = userData;
|
|
TakeLock(DdpLock);
|
|
outstandingDdpRead->next = openSocket->outstandingDdpReads;
|
|
openSocket->outstandingDdpReads = outstandingDdpRead;
|
|
ReleaseLock(DdpLock);
|
|
UnlinkOpenSocket(openSocket);
|
|
|
|
/* Okay, another read has been queued... */
|
|
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* DdpRead */
|
|
|
|
AppleTalkErrorCode far DdpWrite(long sourceSocket,
|
|
AppleTalkAddress destination,
|
|
int protocol,
|
|
void far *opaqueDatagram,
|
|
long datagramLength,
|
|
TransmitCompleteHandler
|
|
*completionRoutine,
|
|
long unsigned userData)
|
|
{
|
|
/* Just build the buffer descriptor for the datagram and call DeliverDdp;
|
|
the sendChain will be freed at the depend level when the write
|
|
completes. */
|
|
|
|
BufferDescriptor sendChain;
|
|
|
|
if ((sendChain = DescribeBuffer(datagramLength, (char far *)opaqueDatagram,
|
|
True)) is Empty)
|
|
{
|
|
ErrorLog("DdpWrite", ISevError, __LINE__, UnknownPort,
|
|
IErrDdpOutOfMemory, IMsgDdpOutOfMemory,
|
|
Insert0());
|
|
return(AToutOfMemory);
|
|
}
|
|
|
|
return(DeliverDdp(sourceSocket, destination, protocol, sendChain,
|
|
datagramLength, Empty, completionRoutine, userData));
|
|
|
|
} /* DdpWrite */
|
|
|
|
AppleTalkErrorCode far DeliverDdp(long sourceSocket,
|
|
AppleTalkAddress destination,
|
|
int protocol,
|
|
BufferDescriptor datagram,
|
|
int datagramLength,
|
|
char far *zoneMulticastAddress,
|
|
TransmitCompleteHandler
|
|
*completionRoutine,
|
|
long unsigned userData)
|
|
{
|
|
OpenSocket openSocket;
|
|
int port;
|
|
AppleTalkAddress sourceAddress;
|
|
|
|
/* Find the port that our socket lives on, and the real source address,
|
|
then let DeliverDdpOnPort handle the rest. */
|
|
|
|
DeferTimerChecking();
|
|
DeferIncomingPackets();
|
|
|
|
if ((openSocket = MapSocketToOpenSocket(sourceSocket)) is empty or
|
|
openSocket->closing)
|
|
{
|
|
UnlinkOpenSocket(openSocket);
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
FreeBufferChain(datagram);
|
|
return(ATsocketNotOpen);
|
|
}
|
|
if (MapSocketToAddress(sourceSocket, &sourceAddress) isnt ATnoError)
|
|
{
|
|
ErrorLog("DeliverDdp", ISevError, __LINE__, openSocket->port,
|
|
IErrDdpSourceAddrBad, IMsgDdpSourceAddrBad,
|
|
Insert0());
|
|
UnlinkOpenSocket(openSocket);
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
FreeBufferChain(datagram);
|
|
return(ATinternalError);
|
|
}
|
|
port = openSocket->port;
|
|
|
|
/* Don't unlink the OpenSocket, we want to do that when we're really done
|
|
with the operation... "transfer" the link into the BufferDescriptor. */
|
|
|
|
datagram->openSocket = openSocket;
|
|
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
|
|
return(DeliverDdpOnPort(port, sourceAddress, destination,
|
|
protocol, datagram, datagramLength,
|
|
zoneMulticastAddress, completionRoutine,
|
|
userData));
|
|
|
|
} /* DeliverDdp */
|
|
|
|
#if not defined(DeferIncomingPackets)
|
|
void far DeferIncomingPackets(void)
|
|
{
|
|
|
|
EnterCriticalSection();
|
|
deferIncomingPacketsCount += 1;
|
|
LeaveCriticalSection();
|
|
|
|
return;
|
|
|
|
} /* DeferIncomingPackets */
|
|
|
|
void far HandleIncomingPackets(void)
|
|
{
|
|
DeferredDdpPacket deferredDdpPacket;
|
|
DeferredDdpDatagram deferredDdpDatagram;
|
|
|
|
if (deferIncomingPacketsCount is 0)
|
|
{
|
|
ErrorLog("HandleIncomingPackets", ISevError, __LINE__, UnknownPort,
|
|
IErrDdpZeroDeferCount, IMsgDdpZeroDeferCount,
|
|
Insert0());
|
|
return;
|
|
}
|
|
|
|
/* Decrement defer count. */
|
|
|
|
EnterCriticalSection();
|
|
deferIncomingPacketsCount -= 1;
|
|
|
|
/* This routine can be called indirectly recursively via the call to
|
|
DdpPacketIn... we don't want to let our stack frame get too big, so
|
|
if we're already trying to handle deferred packets higher on the
|
|
stack, just ignore it here. */
|
|
|
|
if (handleIncomingPacketsNesting isnt 0)
|
|
{
|
|
LeaveCriticalSection();
|
|
return;
|
|
}
|
|
handleIncomingPacketsNesting += 1;
|
|
|
|
/* If we're no longer defering packets, handle any queued ones (first the
|
|
deferred DDP datagrams then the deferred DDP packet queue). */
|
|
|
|
while (deferIncomingPacketsCount is 0 and
|
|
(headOfDeferredDdpDatagramList isnt empty or
|
|
headOfDeferredDdpPacketList isnt empty))
|
|
{
|
|
/* DDP datagram queue (DeliverDdp). */
|
|
|
|
while(headOfDeferredDdpDatagramList isnt empty)
|
|
{
|
|
deferredDdpDatagram = headOfDeferredDdpDatagramList;
|
|
headOfDeferredDdpDatagramList = headOfDeferredDdpDatagramList->next;
|
|
if (headOfDeferredDdpDatagramList is empty)
|
|
tailOfDeferredDdpDatagramList = empty;
|
|
if ((currentDeferredDdpDatagramCount -= 1) < 0)
|
|
{
|
|
ErrorLog("HandleIncomingPackets", ISevError, __LINE__, UnknownPort,
|
|
IErrDdpBadDeferCount, IMsgDdpBadDeferCount,
|
|
Insert0());
|
|
currentDeferredDdpDatagramCount = 0;
|
|
}
|
|
LeaveCriticalSection();
|
|
DeliverDdpOnPort(deferredDdpDatagram->sourcePort,
|
|
deferredDdpDatagram->source,
|
|
deferredDdpDatagram->destination,
|
|
deferredDdpDatagram->protocol,
|
|
deferredDdpDatagram->datagram,
|
|
deferredDdpDatagram->datagramLength,
|
|
deferredDdpDatagram->zoneMulticastAddress,
|
|
deferredDdpDatagram->completionRoutine,
|
|
deferredDdpDatagram->userData);
|
|
Free(deferredDdpDatagram);
|
|
EnterCriticalSection();
|
|
}
|
|
|
|
/* DDP packet queue (DdpPacketIn). */
|
|
|
|
while(headOfDeferredDdpPacketList isnt empty)
|
|
{
|
|
deferredDdpPacket = headOfDeferredDdpPacketList;
|
|
headOfDeferredDdpPacketList = headOfDeferredDdpPacketList->next;
|
|
if (headOfDeferredDdpPacketList is empty)
|
|
tailOfDeferredDdpPacketList = empty;
|
|
if ((currentDeferredDdpPacketCount -= 1) < 0)
|
|
{
|
|
ErrorLog("HandleIncomingPackets", ISevError, __LINE__, UnknownPort,
|
|
IErrDdpBadDeferCount, IMsgDdpBadDeferCount,
|
|
Insert0());
|
|
currentDeferredDdpPacketCount = 0;
|
|
}
|
|
LeaveCriticalSection();
|
|
DdpPacketIn(deferredDdpPacket->port,
|
|
deferredDdpPacket->packet,
|
|
deferredDdpPacket->length,
|
|
deferredDdpPacket->freePacket,
|
|
deferredDdpPacket->extendedDdpHeader,
|
|
deferredDdpPacket->alapSourceNode,
|
|
deferredDdpPacket->alapDestinationNode);
|
|
Free(deferredDdpPacket);
|
|
EnterCriticalSection();
|
|
}
|
|
}
|
|
|
|
handleIncomingPacketsNesting -= 1;
|
|
LeaveCriticalSection();
|
|
return;
|
|
|
|
} /* HandleIncomingPackets */
|
|
#endif
|
|
|
|
void _near _fastcall InvokeSocketHandler(OpenSocket openSocket,
|
|
int port,
|
|
AppleTalkAddress source,
|
|
int protocolType,
|
|
char far *datagram,
|
|
int datagramLength,
|
|
AppleTalkAddress actualDestination)
|
|
{
|
|
int index;
|
|
void far *copy;
|
|
IncomingDdpHandler *handler, *ddpReadHandler;
|
|
long unsigned userData, ddpReadUserData;
|
|
OutstandingDdpRead outstandingDdpRead;
|
|
long destinationSocket = openSocket->socket;
|
|
long bufferLength;
|
|
AppleTalkErrorCode error;
|
|
|
|
/* Trying to close? */
|
|
|
|
if (openSocket->closing)
|
|
{
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
/* First check for queued DdpRead handlers... */
|
|
|
|
TakeLock(DdpLock);
|
|
if (openSocket->outstandingDdpReads isnt Empty)
|
|
{
|
|
/* Untread the handler... */
|
|
|
|
outstandingDdpRead = openSocket->outstandingDdpReads;
|
|
openSocket->outstandingDdpReads = outstandingDdpRead->next;
|
|
ReleaseLock(DdpLock);
|
|
handler = outstandingDdpRead->handler;
|
|
userData = outstandingDdpRead->userData;
|
|
copy = outstandingDdpRead->opaqueDatagram;
|
|
bufferLength = outstandingDdpRead->bufferLength;
|
|
Free(outstandingDdpRead);
|
|
|
|
/* Copy the datagram and invoke the handler. Make sure we use the
|
|
proper size. */
|
|
|
|
if (bufferLength >= datagramLength)
|
|
{
|
|
bufferLength = datagramLength;
|
|
error = ATnoError;
|
|
}
|
|
else
|
|
error = ATddpBufferTooSmall;
|
|
|
|
MoveToOpaque(copy, 0, datagram, bufferLength);
|
|
#if IamNot an OS2
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
#endif
|
|
(*handler)(error, userData, port, source, destinationSocket,
|
|
protocolType, copy, datagramLength, actualDestination);
|
|
#if Iam an OS2
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* Was a handler passed to OpenSocketOnNode? */
|
|
|
|
if (openSocket->handler is empty)
|
|
{
|
|
ReleaseLock(DdpLock);
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return; /* No handler... drop the packet... */
|
|
}
|
|
|
|
/* Do we have to make a copy at all? */
|
|
|
|
handler = openSocket->handler;
|
|
userData = openSocket->userData;
|
|
if (openSocket->validDatagramBuffers is 0)
|
|
{
|
|
if (not openSocket->eventHandler)
|
|
{
|
|
/* We're a listener, just pass off the packet. */
|
|
|
|
ReleaseLock(DdpLock);
|
|
#if IamNot an OS2
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
#endif
|
|
(*handler)(ATnoError, userData, port, source, destinationSocket,
|
|
protocolType, datagram, datagramLength, actualDestination);
|
|
#if Iam an OS2
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
/* We're an event handler. */
|
|
|
|
long bytesAccepted;
|
|
DeferredDatagramEvent deferredEvent = Empty;
|
|
|
|
/* Here we will set the datagramEvent to True. This will queue up all
|
|
the rest of the incoming events - We *must* queue them up FIFO as
|
|
otherwise we could potentially screw up the application doing some
|
|
sort of sequencing. We indicate an event - if accepted partially/
|
|
completely, we are done with the datagram, we loop until all queued
|
|
events are dealt with. */
|
|
|
|
if (openSocket->eventQueue.datagramEventInProgress)
|
|
{
|
|
/* There is an event in progress, just queue up the event and
|
|
return. Get out of the critical section while we're allocating
|
|
and copying (we'll check again later). */
|
|
|
|
ReleaseLock(DdpLock);
|
|
if ((deferredEvent = (DeferredDatagramEvent)
|
|
Malloc(sizeof(*deferredEvent) + datagramLength)) is Empty)
|
|
{
|
|
ErrorLog("InvokeSocketHandler", ISevError, __LINE__, UnknownPort,
|
|
IErrDdpOutOfMemory, IMsgDdpOutOfMemory,
|
|
Insert0());
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
deferredEvent->next = Empty;
|
|
deferredEvent->port = port;
|
|
deferredEvent->source = source;
|
|
deferredEvent->destinationSocket = destinationSocket;
|
|
deferredEvent->protocolType = protocolType;
|
|
deferredEvent->datagramLength = datagramLength;
|
|
deferredEvent->actualDestination = actualDestination;
|
|
|
|
/* In the fullness of time and if it turns out that we really
|
|
need to defer events with any regularity, we should look at
|
|
keeping a reference count on the datagram rather than making
|
|
this copy. */
|
|
|
|
MoveMem(deferredEvent->datagram, datagram, datagramLength);
|
|
|
|
/* Okay, now check if we're still deferring, if not plow ahead;
|
|
otherwise, stick the defer node on the queue. */
|
|
|
|
TakeLock(DdpLock);
|
|
if (openSocket->eventQueue.datagramEventInProgress)
|
|
{
|
|
if (openSocket->eventQueue.first is Empty)
|
|
openSocket->eventQueue.first =
|
|
openSocket->eventQueue.last = deferredEvent;
|
|
else
|
|
{
|
|
openSocket->eventQueue.last->next = deferredEvent;
|
|
openSocket->eventQueue.last = deferredEvent;
|
|
}
|
|
|
|
ReleaseLock(DdpLock);
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Now an event is in progress */
|
|
|
|
openSocket->eventQueue.datagramEventInProgress = True;
|
|
ReleaseLock(DdpLock);
|
|
|
|
/* Handle the obscure case of, above, when we we're allocating and
|
|
copying, that we stopped deferring, free the defer node. */
|
|
|
|
if (deferredEvent isnt Empty)
|
|
Free(deferredEvent);
|
|
|
|
/* Indicate the event. */
|
|
|
|
bytesAccepted = (*handler)(ATnoError, userData, port, source,
|
|
destinationSocket, protocolType, datagram,
|
|
datagramLength, actualDestination);
|
|
|
|
/* Now if the datagram was accepted, (bytesAccepted is non-zero), then
|
|
we don't care about this datagram anymore. If not accepted we need
|
|
to check for posted DdpReads, if any are there, satisfy one of them
|
|
with the just-indicated packet. If not accepted, and no queued
|
|
DdpReads, drop the packet on the floor. Lastly, go through the
|
|
deferred queue until it's empty. */
|
|
|
|
TakeLock(DdpLock);
|
|
while (True)
|
|
{
|
|
if (bytesAccepted is 0 and
|
|
openSocket->outstandingDdpReads isnt Empty)
|
|
{
|
|
/* Untread the handler... */
|
|
|
|
outstandingDdpRead = openSocket->outstandingDdpReads;
|
|
openSocket->outstandingDdpReads = outstandingDdpRead->next;
|
|
ReleaseLock(DdpLock);
|
|
ddpReadHandler = outstandingDdpRead->handler;
|
|
ddpReadUserData = outstandingDdpRead->userData;
|
|
copy = outstandingDdpRead->opaqueDatagram;
|
|
bufferLength = outstandingDdpRead->bufferLength;
|
|
Free(outstandingDdpRead);
|
|
|
|
/* Copy the datagram and invoke the handler. Make sure we use
|
|
the proper size. */
|
|
|
|
if (bufferLength >= datagramLength)
|
|
{
|
|
bufferLength = datagramLength;
|
|
error = ATnoError;
|
|
}
|
|
else
|
|
error = ATddpBufferTooSmall;
|
|
|
|
MoveToOpaque(copy, 0, datagram, bufferLength);
|
|
(*ddpReadHandler)(error, ddpReadUserData, port, source,
|
|
destinationSocket, protocolType, copy,
|
|
datagramLength, actualDestination);
|
|
TakeLock(DdpLock);
|
|
}
|
|
|
|
/* Go through the deferred event queue. */
|
|
|
|
deferredEvent = openSocket->eventQueue.first;
|
|
if (openSocket->eventQueue.first isnt Empty)
|
|
{
|
|
if (openSocket->eventQueue.first is openSocket->eventQueue.last)
|
|
openSocket->eventQueue.first =
|
|
openSocket->eventQueue.last = Empty;
|
|
else
|
|
openSocket->eventQueue.first =
|
|
openSocket->eventQueue.first->next;
|
|
}
|
|
|
|
/* If no deferred events, we're finished with the puppy! */
|
|
|
|
if (deferredEvent is Empty)
|
|
break;
|
|
|
|
/* These are used at the beginning of the loop, so reset them
|
|
here. */
|
|
|
|
ReleaseLock(DdpLock);
|
|
port = deferredEvent->port;
|
|
source = deferredEvent->source;
|
|
destinationSocket = deferredEvent->destinationSocket;
|
|
protocolType = deferredEvent->protocolType;
|
|
datagram = deferredEvent->datagram;
|
|
datagramLength = deferredEvent->datagramLength;
|
|
actualDestination = deferredEvent->actualDestination;
|
|
|
|
bytesAccepted = (*handler)(ATnoError, userData, port, source,
|
|
destinationSocket, protocolType, datagram,
|
|
datagramLength, actualDestination);
|
|
Free(deferredEvent);
|
|
TakeLock(DdpLock);
|
|
}
|
|
|
|
/* We're set, stop deferring these events. */
|
|
|
|
openSocket->eventQueue.datagramEventInProgress = False;
|
|
|
|
ReleaseLock(DdpLock);
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/* Okay, we need to copy, find a free datagram buffer... */
|
|
|
|
for (index = 0; index < openSocket->validDatagramBuffers; index += 1)
|
|
if (not openSocket->datagramInUse[index])
|
|
break;
|
|
if (index >= openSocket->validDatagramBuffers)
|
|
{
|
|
ReleaseLock(DdpLock);
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return; /* No free buffers, drop the packet! */
|
|
}
|
|
openSocket->datagramInUse[index] = True;
|
|
ReleaseLock(DdpLock);
|
|
copy = (openSocket->datagramBuffers + (index * MaximumDdpDatagramSize));
|
|
|
|
/* Copy the datagram. */
|
|
|
|
MoveMem(copy, datagram, datagramLength);
|
|
|
|
/* Invoke the handler. */
|
|
|
|
#if IamNot an OS2
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
#endif
|
|
|
|
(*handler)(ATnoError, userData, port, source, destinationSocket,
|
|
protocolType, copy, datagramLength, actualDestination);
|
|
|
|
#if IamNot an OS2
|
|
DeferTimerChecking();
|
|
DeferIncomingPackets();
|
|
#endif
|
|
|
|
openSocket->datagramInUse[index] = False;
|
|
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
|
|
return;
|
|
|
|
} /* InvokeSocketHandler */
|
|
|
|
AppleTalkErrorCode far DeliverDdpOnPort(int sourcePort,
|
|
AppleTalkAddress source,
|
|
AppleTalkAddress destination,
|
|
int protocol,
|
|
BufferDescriptor datagram,
|
|
int datagramLength,
|
|
char far *zoneMulticastAddress,
|
|
TransmitCompleteHandler
|
|
*completionRoutine,
|
|
long unsigned userData)
|
|
{
|
|
#if not defined(DeferIncomingPackets)
|
|
Boolean deferralCandidate = False;
|
|
DeferredDdpDatagram deferredDdpDatagram;
|
|
#endif
|
|
ActiveNode activeNode, nextActiveNode;
|
|
Boolean broadcast = False, delivered = False;
|
|
Boolean shouldBeRouted;
|
|
AppleTalkErrorCode errorCode = ATnoError;
|
|
OpenSocket openSocket;
|
|
BufferDescriptor chain;
|
|
|
|
/* Do we like our address arguments. */
|
|
|
|
if (not appleTalkRunning or not PortDescriptor(sourcePort)->portActive)
|
|
errorCode = ATappleTalkShutDown;
|
|
else if (destination.networkNumber > LastValidNetworkNumber)
|
|
errorCode = ATbadNetworkNumber;
|
|
else if (destination.nodeNumber > MaximumUsableAppleTalkNode and
|
|
destination.nodeNumber isnt AppleTalkBroadcastNodeNumber)
|
|
errorCode = ATbadNodeNumber;
|
|
else if (destination.socketNumber < FirstStaticSocket or
|
|
destination.socketNumber > LastValidSocket)
|
|
errorCode = ATbadSocketNumber;
|
|
if (PortDescriptor(sourcePort)->portType isnt NonAppleTalkHalfPort)
|
|
{
|
|
if (source.networkNumber > LastValidNetworkNumber)
|
|
errorCode = ATbadNetworkNumber;
|
|
else if (source.nodeNumber < MinimumUsableAppleTalkNode or
|
|
source.nodeNumber > MaximumUsableAppleTalkNode)
|
|
errorCode = ATbadNodeNumber;
|
|
else if (source.socketNumber < FirstStaticSocket or
|
|
source.socketNumber > LastValidSocket)
|
|
errorCode = ATbadSocketNumber;
|
|
}
|
|
if (errorCode isnt ATnoError)
|
|
{
|
|
FreeBufferChain(datagram);
|
|
return(errorCode);
|
|
}
|
|
|
|
/* Don't bother for awhile... */
|
|
|
|
DeferTimerChecking();
|
|
DeferIncomingPackets();
|
|
|
|
/* If we're defering packets, we need to defer any packet that is either:
|
|
|
|
1. Broadcast (either cable-wide or dest-network matches any of
|
|
our port's nodes).
|
|
|
|
2. Addressed to any node on our current port.
|
|
|
|
*/
|
|
|
|
broadcast = destination.nodeNumber is AppleTalkBroadcastNodeNumber;
|
|
#if not defined(DeferIncomingPackets)
|
|
deferralCandidate = False;
|
|
TakeLock(DdpLock);
|
|
for (activeNode = PortDescriptor(sourcePort)->activeNodes;
|
|
not deferralCandidate and activeNode isnt empty;
|
|
activeNode = activeNode->next)
|
|
{
|
|
if ((destination.networkNumber is CableWideBroadcastNetworkNumber or
|
|
destination.networkNumber is
|
|
activeNode->extendedNode.networkNumber) and
|
|
(broadcast or
|
|
#if Iam an AppleTalkRouter
|
|
destination.nodeNumber is AnyRouterNodeNumber or
|
|
#endif
|
|
destination.nodeNumber is activeNode->extendedNode.nodeNumber))
|
|
deferralCandidate = True;
|
|
}
|
|
ReleaseLock(DdpLock);
|
|
|
|
/* The following "> 1" is NOT supposed to be "> 0" because we've called
|
|
"DeferIncomingPackets" once, just above; we don't want to count this
|
|
one. */
|
|
|
|
EnterCriticalSection();
|
|
if (deferIncomingPacketsCount > 1 and deferralCandidate)
|
|
{
|
|
if (currentDeferredDdpDatagramCount is MaximumDeferredDdpDatagrams)
|
|
{
|
|
LeaveCriticalSection();
|
|
ErrorLog("DeliverDdpOnPort", ISevWarning, __LINE__, sourcePort,
|
|
IErrDdpLosingData, IMsgDdpLosingData,
|
|
Insert0());
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
FreeBufferChain(datagram);
|
|
return(AToutOfMemory);
|
|
}
|
|
LeaveCriticalSection();
|
|
deferredDdpDatagram =
|
|
(DeferredDdpDatagram)Malloc(sizeof(*deferredDdpDatagram));
|
|
if (deferredDdpDatagram is empty)
|
|
{
|
|
ErrorLog("DeliverDdpOnPort", ISevError, __LINE__, sourcePort,
|
|
IErrDdpOutOfMemory, IMsgDdpOutOfMemory,
|
|
Insert0());
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
FreeBufferChain(datagram);
|
|
return(AToutOfMemory);
|
|
}
|
|
|
|
/* Fill in the data strcuture, and place the packet at the end of the
|
|
queue. */
|
|
|
|
deferredDdpDatagram->next = empty;
|
|
deferredDdpDatagram->sourcePort = sourcePort;
|
|
deferredDdpDatagram->source = source;
|
|
deferredDdpDatagram->destination = destination;
|
|
deferredDdpDatagram->protocol = (short)protocol;
|
|
deferredDdpDatagram->zoneMulticastAddress = zoneMulticastAddress;
|
|
deferredDdpDatagram->datagramLength = (short)datagramLength;
|
|
deferredDdpDatagram->datagram = datagram;
|
|
deferredDdpDatagram->completionRoutine = completionRoutine;
|
|
deferredDdpDatagram->userData = userData;
|
|
|
|
EnterCriticalSection();
|
|
if (tailOfDeferredDdpDatagramList is empty)
|
|
tailOfDeferredDdpDatagramList = headOfDeferredDdpDatagramList =
|
|
deferredDdpDatagram;
|
|
else
|
|
{
|
|
tailOfDeferredDdpDatagramList->next = deferredDdpDatagram;
|
|
tailOfDeferredDdpDatagramList = deferredDdpDatagram;
|
|
}
|
|
|
|
/* All set... return. */
|
|
|
|
currentDeferredDdpDatagramCount += 1;
|
|
LeaveCriticalSection();
|
|
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
|
|
/* Don't free the buffer chain! It's attached to the deferral node! */
|
|
|
|
return(ATnoError);
|
|
}
|
|
else
|
|
LeaveCriticalSection();
|
|
#endif
|
|
|
|
/* Okay, first walk through our list of nodes, to see if we can find a home
|
|
for this packet. */
|
|
|
|
TakeLock(DdpLock);
|
|
activeNode = Link(PortDescriptor(sourcePort)->activeNodes);
|
|
ReleaseLock(DdpLock);
|
|
while (not delivered and activeNode isnt empty)
|
|
{
|
|
while (True) /* Something to break out of. */
|
|
{
|
|
if ((destination.networkNumber is CableWideBroadcastNetworkNumber or
|
|
destination.networkNumber is
|
|
activeNode->extendedNode.networkNumber) and
|
|
(broadcast or
|
|
destination.nodeNumber is activeNode->extendedNode.nodeNumber))
|
|
{
|
|
/* If we're aimed at a proxy node (one that is the node used by an
|
|
active remote access port), send the packet out the proxy port. */
|
|
|
|
if (activeNode->proxyNode)
|
|
{
|
|
/* Make a copy, we don't want to free a buffer chain twice! */
|
|
|
|
if ((chain = CopyBufferChain(datagram)) is Empty)
|
|
{
|
|
ErrorLog("DeliverDdpOnPort", ISevError, __LINE__, sourcePort,
|
|
IErrDdpOutOfMemory, IMsgDdpOutOfMemory,
|
|
Insert0());
|
|
break;
|
|
}
|
|
if (not TransmitDdp(activeNode->proxyPort, source, destination,
|
|
protocol, chain, datagramLength, 0, Empty,
|
|
Empty, Empty, 0))
|
|
ErrorLog("DeliverDdpOnPort", ISevWarning, __LINE__,
|
|
activeNode->proxyPort,
|
|
IErrDdpForwardError, IMsgDdpForwardError,
|
|
Insert0());
|
|
if (not broadcast and
|
|
destination.networkNumber isnt
|
|
CableWideBroadcastNetworkNumber)
|
|
delivered = True;
|
|
break;
|
|
}
|
|
|
|
/* We're aiming at a node on the current port... if the socket is
|
|
open, deliver the packet. */
|
|
|
|
if (broadcast)
|
|
destination.nodeNumber = activeNode->extendedNode.nodeNumber;
|
|
if ((openSocket = MapAddressToOpenSocket(sourcePort,
|
|
destination)) isnt empty)
|
|
{
|
|
if (broadcast)
|
|
destination.nodeNumber = AppleTalkBroadcastNodeNumber;
|
|
|
|
/* Turn a possibly chunked buffer chain into one chunk; if it's one
|
|
chunk already, no change will be made. */
|
|
|
|
if ((chain = CoalesceBufferChain(datagram)) is Empty)
|
|
{
|
|
ErrorLog("DeliverDdpOnPort", ISevError, __LINE__, sourcePort,
|
|
IErrDdpOutOfMemory, IMsgDdpOutOfMemory,
|
|
Insert0());
|
|
UnlinkOpenSocket(openSocket);
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
datagram->transmitCompleteHandler = completionRoutine;
|
|
datagram->userData = userData;
|
|
datagram->errorCode = AToutOfMemory;
|
|
TransmitComplete(datagram);
|
|
return(ATnoError);
|
|
}
|
|
datagram = chain;
|
|
InvokeSocketHandler(openSocket, sourcePort, source,
|
|
protocol, datagram->data,
|
|
datagramLength, destination);
|
|
UnlinkOpenSocket(openSocket);
|
|
DeferTimerChecking();
|
|
DeferIncomingPackets();
|
|
}
|
|
if (not broadcast and
|
|
destination.networkNumber isnt CableWideBroadcastNetworkNumber)
|
|
delivered = True;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* Okay, on to next active node. */
|
|
|
|
TakeLock(DdpLock);
|
|
nextActiveNode = Link(activeNode->next);
|
|
ReleaseLock(DdpLock);
|
|
UnlinkActiveNode(activeNode);
|
|
activeNode = nextActiveNode;
|
|
}
|
|
|
|
UnlinkActiveNode(activeNode); /* May, or may not, be Empty. */
|
|
|
|
/* If we're not broadcast and we found a home for the packet, we're
|
|
finished. */
|
|
|
|
if (delivered)
|
|
{
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
datagram->transmitCompleteHandler = completionRoutine;
|
|
datagram->userData = userData;
|
|
TransmitComplete(datagram);
|
|
return(ATnoError);
|
|
}
|
|
if (broadcast)
|
|
destination.nodeNumber = AppleTalkBroadcastNodeNumber;
|
|
|
|
/* If we're a router, does the router have the best chance of dealing with
|
|
this packet? */
|
|
|
|
#if Iam an AppleTalkRouter
|
|
TakeLock(DdpLock);
|
|
shouldBeRouted = (PortDescriptor(sourcePort)->routerRunning and
|
|
destination.networkNumber isnt
|
|
CableWideBroadcastNetworkNumber and
|
|
not IsWithinNetworkRange(destination.networkNumber,
|
|
&PortDescriptor(sourcePort)->
|
|
thisCableRange) and
|
|
not IsWithinNetworkRange(destination.networkNumber,
|
|
&startupNetworkRange));
|
|
ReleaseLock(DdpLock);
|
|
#else
|
|
shouldBeRouted = False;
|
|
#endif
|
|
|
|
/* If we're a router and the packet isn't destined for the target ports local
|
|
network, let our router handle it -- rather than sending to whatever
|
|
the "best router" is or to "a router". */
|
|
|
|
#if Iam an AppleTalkRouter
|
|
if (shouldBeRouted)
|
|
{
|
|
/* Let the router have a crack at it. */
|
|
|
|
Router(sourcePort, source, destination, protocol,
|
|
datagram->data, datagramLength, 0, False);
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
|
|
/* The router will have finished with the packet, one way or another
|
|
(either really delivered it, or copied it, or whatever), so we
|
|
can "complete" our call now. */
|
|
|
|
datagram->transmitCompleteHandler = completionRoutine;
|
|
datagram->userData = userData;
|
|
TransmitComplete(datagram);
|
|
return(ATnoError);
|
|
}
|
|
#endif
|
|
|
|
/* Otherwise, blast the beast out... */
|
|
|
|
TransmitDdp(sourcePort, source, destination, protocol,
|
|
datagram, datagramLength, 0,
|
|
zoneMulticastAddress, empty, completionRoutine,
|
|
userData);
|
|
|
|
/* TransmitDdp() will handle the freeing of the buffer chain, so don't
|
|
do it here! */
|
|
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* DeliverDdpOnPort */
|
|
|
|
ExternForVisibleFunction BestRouterEntry far
|
|
*FindInBestRouterCache(int port,
|
|
AppleTalkAddress destination)
|
|
{
|
|
ExtendedAppleTalkNodeNumber node;
|
|
int index;
|
|
|
|
/* Given a destination node, can we find it in our "best router" cache? */
|
|
|
|
if (not PortDescriptor(port)->seenRouterRecently)
|
|
return(empty);
|
|
|
|
node.networkNumber = destination.networkNumber;
|
|
node.nodeNumber = destination.nodeNumber;
|
|
index = HashExtendedAppleTalkNode(node) % BestRouterHashBuckets;
|
|
|
|
if (not PortDescriptor(port)->bestRouterCache[index].valid or
|
|
not ExtendedAppleTalkNodesEqual(&node,
|
|
&PortDescriptor(port)->
|
|
bestRouterCache[index].target))
|
|
return(empty);
|
|
|
|
return(&PortDescriptor(port)->bestRouterCache[index]);
|
|
|
|
} /* FindInBestRouterCache */
|
|
|
|
ExternForVisibleFunction Boolean
|
|
SendPacket(BufferDescriptor packet,
|
|
int packetLength,
|
|
int port,
|
|
char far *knownAddress,
|
|
char far *knownRoutingInfo,
|
|
int knownRoutingInfoLength,
|
|
ExtendedAppleTalkNodeNumber actualDestination,
|
|
Boolean broadcast,
|
|
AppleTalkAddress source,
|
|
Boolean retry,
|
|
TransmitCompleteHandler *completionRoutine,
|
|
long unsigned userData)
|
|
{
|
|
PortHandlers portHandlers;
|
|
char far *onTheWirePacket;
|
|
AddressMappingNode mapNode;
|
|
int index, tempLength;
|
|
BufferDescriptor probe;
|
|
ExtendedAppleTalkNodeNumber sourceNode;
|
|
RetryNode retryNode;
|
|
|
|
portHandlers = &portSpecificInfo[PortDescriptor(port)->portType];
|
|
|
|
/* For RemoteAccess ports and Half ports, just send the packet down the
|
|
pipe! Both are internally, if not logically, "extended networks". */
|
|
|
|
if (PortDescriptor(port)->portType is AppleTalkRemoteAccess or
|
|
PortDescriptor(port)->portType is NonAppleTalkHalfPort)
|
|
{
|
|
if (PortDescriptor(port)->portType is AppleTalkRemoteAccess)
|
|
onTheWirePacket = (*portHandlers->buildHeader)(packet->data,
|
|
packetLength,
|
|
port, knownAddress,
|
|
knownRoutingInfo,
|
|
knownRoutingInfoLength,
|
|
(LogicalProtocol)
|
|
(ArapPacketDataFlag +
|
|
ArapLastGroupFlag));
|
|
else
|
|
onTheWirePacket = (*portHandlers->buildHeader)(packet->data,
|
|
packetLength,
|
|
port, knownAddress,
|
|
knownRoutingInfo,
|
|
knownRoutingInfoLength,
|
|
AppleTalk);
|
|
packetLength += (packet->data - onTheWirePacket);
|
|
AdjustBufferDescriptor(packet, packet->data - onTheWirePacket);
|
|
return((*portHandlers->packetOut)(port, packet, packetLength,
|
|
completionRoutine, userData));
|
|
}
|
|
|
|
/* If we already know where we're headed, just blast it out. Also,
|
|
If we're broadcasting, just do it (as Nike would say). "knownAddress"
|
|
will be empty if we're broadcasting and that will cause the
|
|
BuildHeader guys to make a broadcast packet. */
|
|
|
|
if (knownAddress isnt empty or broadcast or
|
|
actualDestination.networkNumber is CableWideBroadcastNetworkNumber)
|
|
{
|
|
if (PortDescriptor(port)->extendedNetwork)
|
|
onTheWirePacket = (*portHandlers->buildHeader)(packet->data,
|
|
packetLength,
|
|
port, knownAddress,
|
|
knownRoutingInfo,
|
|
knownRoutingInfoLength,
|
|
AppleTalk);
|
|
else
|
|
onTheWirePacket = (*portHandlers->buildHeader)(packet->data, True,
|
|
port, knownAddress,
|
|
knownRoutingInfo,
|
|
knownRoutingInfoLength,
|
|
AppleTalk);
|
|
packetLength += (packet->data - onTheWirePacket);
|
|
AdjustBufferDescriptor(packet, packet->data - onTheWirePacket);
|
|
return((*portHandlers->packetOut)(port, packet, packetLength,
|
|
completionRoutine, userData));
|
|
}
|
|
|
|
/* On non-extended networks, just send the packet to the desired node --
|
|
no AARP games here. */
|
|
|
|
if (not PortDescriptor(port)->extendedNetwork)
|
|
{
|
|
onTheWirePacket = (*portHandlers->buildHeader)
|
|
(packet->data, True, port,
|
|
(char *)&actualDestination.nodeNumber,
|
|
empty, 0, AppleTalk);
|
|
packetLength += (packet->data - onTheWirePacket);
|
|
AdjustBufferDescriptor(packet, packet->data - onTheWirePacket);
|
|
return((*portHandlers->packetOut)(port, packet, packetLength,
|
|
completionRoutine, userData));
|
|
}
|
|
|
|
/* We're sending to a particular node, do we know its hardware address?
|
|
If so, send it out. */
|
|
|
|
TakeLock(RoutingLock);
|
|
index = HashExtendedAppleTalkNode(actualDestination) %
|
|
NumberOfAddressMapHashBuckets;
|
|
for (mapNode = PortDescriptor(port)->addressMappingTable[index];
|
|
mapNode isnt empty;
|
|
mapNode = mapNode->next)
|
|
if (ExtendedAppleTalkNodesEqual(&actualDestination, &mapNode->target))
|
|
break;
|
|
if (mapNode isnt empty)
|
|
{
|
|
knownAddress = mapNode->hardwareAddress;
|
|
onTheWirePacket = (*portHandlers->buildHeader)(packet->data, packetLength,
|
|
port, knownAddress,
|
|
mapNode->routingInfo,
|
|
mapNode->routingInfoLength,
|
|
AppleTalk);
|
|
ReleaseLock(RoutingLock);
|
|
packetLength += (packet->data - onTheWirePacket);
|
|
AdjustBufferDescriptor(packet, packet->data - onTheWirePacket);
|
|
return((*portHandlers->packetOut)(port, packet, packetLength,
|
|
completionRoutine, userData));
|
|
}
|
|
ReleaseLock(RoutingLock);
|
|
|
|
/* Well, we have a slight problem here. We know what logical address we'd
|
|
like to send to, but we don't know it's physical address. We could just
|
|
drop the packet on the floor and assume that upper layers would retry
|
|
and we would have learned the physical address by then... but, John
|
|
Saunders made me feel too guilty about that approach in the AppleTalk
|
|
phase I implementation. So, send out an AARP request to the logical
|
|
address, and retry send the packet in a little while. Only retry once
|
|
though! */
|
|
|
|
packet->transmitCompleteHandler = completionRoutine;
|
|
packet->userData = userData;
|
|
if (retry)
|
|
{
|
|
packet->errorCode = ATdestinationNotAvailable;
|
|
TransmitComplete(packet);
|
|
return(False);
|
|
}
|
|
|
|
sourceNode.networkNumber = source.networkNumber;
|
|
sourceNode.nodeNumber = source.nodeNumber;
|
|
probe = BuildAarpRequestTo(port, portHandlers->hardwareAddressLength,
|
|
sourceNode, actualDestination, &tempLength);
|
|
retryNode = (RetryNode)Malloc(sizeof(*retryNode));
|
|
if (probe is Empty or retryNode is Empty)
|
|
{
|
|
ErrorLog("SendPacket", ISevError, __LINE__, port,
|
|
IErrDdpOutOfMemory, IMsgDdpOutOfMemory,
|
|
Insert0());
|
|
packet->errorCode = AToutOfMemory;
|
|
TransmitComplete(packet);
|
|
if (probe isnt Empty)
|
|
FreeBufferChain(probe);
|
|
return(False);
|
|
}
|
|
|
|
if (not (*portHandlers->packetOut)(port, probe, tempLength, Empty, 0))
|
|
{
|
|
ErrorLog("SendPacket", ISevError, __LINE__, port,
|
|
IErrDdpBadAarpReqSend, IMsgDdpBadAarpReqSend,
|
|
Insert0());
|
|
packet->errorCode = ATdestinationNotAvailable;
|
|
TransmitComplete(packet);
|
|
return(False);
|
|
}
|
|
|
|
/* Copy the needed info. */
|
|
|
|
retryNode->packet = packet;
|
|
retryNode->packetLength = packetLength;
|
|
retryNode->port = port;
|
|
retryNode->actualDestination = actualDestination;
|
|
retryNode->source = source;
|
|
retryNode->completionRoutine = completionRoutine;
|
|
retryNode->userData = userData;
|
|
StartTimer(RetryTimerExpired, RetryTimerSeconds, sizeof(retryNode),
|
|
(char *)&retryNode);
|
|
|
|
/* A little wishfull thinking here! Don't free the buffer chain, it's
|
|
attached to the retry node. */
|
|
|
|
return(True);
|
|
|
|
} /* SendPacket */
|
|
|
|
ExternForVisibleFunction void far
|
|
RetryTimerExpired(long unsigned timerId,
|
|
int additionalDataSize,
|
|
char far *additionalData)
|
|
{
|
|
RetryNode retryNode;
|
|
|
|
/* "Use" unused formal. */
|
|
|
|
timerId;
|
|
|
|
/* Validate args. */
|
|
|
|
if (additionalDataSize isnt sizeof(retryNode))
|
|
{
|
|
ErrorLog("RetryTimerExpired", ISevError, __LINE__, UnknownPort,
|
|
IErrDdpBadData, IMsgDdpBadData,
|
|
Insert0());
|
|
return;
|
|
}
|
|
retryNode = *(RetryNode *)additionalData;
|
|
|
|
/* Try to send the packet now, after we've waited a little while to learn
|
|
the correct physical address. */
|
|
|
|
if (not SendPacket(retryNode->packet, retryNode->packetLength,
|
|
retryNode->port, empty, empty, 0,
|
|
retryNode->actualDestination, False,
|
|
retryNode->source, True,
|
|
retryNode->completionRoutine, retryNode->userData))
|
|
ErrorLog("RetryTimerExpired", ISevError, __LINE__, retryNode->port,
|
|
IErrDdpBadRetrySend, IMsgDdpBadRetrySend,
|
|
Insert0());
|
|
|
|
/* Hopefully better now. */
|
|
|
|
Free(retryNode);
|
|
HandleDeferredTimerChecks();
|
|
|
|
return;
|
|
|
|
} /* RetryTimerExpired */
|