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.
1642 lines
57 KiB
1642 lines
57 KiB
/* FullZip.c, atalk-ii/router, Garth Conboy, 01/16/90 */
|
|
/* Copyright (c) 1989 by Pacer Software Inc., La Jolla, CA */
|
|
|
|
/* GC - Initial coding.
|
|
GC - (08/18/90): New error logging mechanism.
|
|
GC - (01/19/92): Fix from newest OS/2 base [DCH/Jameel]. Corrected zone
|
|
name overlap checking in GetZoneList processing.
|
|
GC - (03/31/92): Updated for BufferDescriptors.
|
|
GC - (11/28/92): Locks and reference counts come to town.
|
|
GC - (12/05/92): Fixed GetZoneListFor() to correctly acquire the
|
|
port's default zone.
|
|
GC - (12/06/92): Introduced a zone table for zone storage/management.
|
|
GC - (12/10/92): Half port fix from Eric Smith at Telebit.
|
|
|
|
*** Make the PVCS source control system happy:
|
|
$Header$
|
|
$Log$
|
|
***
|
|
|
|
ZIP handling for a router.
|
|
|
|
*/
|
|
|
|
#define IncludeFullZipErrors 1
|
|
|
|
#include "atalk.h"
|
|
|
|
#if (Iam an AppleTalkRouter) and (IamNot an AppleTalkStack)
|
|
#include "atpconst.h"
|
|
#endif
|
|
|
|
#if IamNot an AppleTalkRouter
|
|
/* Empty file */
|
|
#else
|
|
|
|
ExternForVisibleFunction TimerHandler ZipQueryTimerExpired;
|
|
|
|
ExternForVisibleFunction Boolean GetZoneListFor(int port);
|
|
|
|
static Boolean firstCall = True;
|
|
|
|
Boolean far StartZipProcessingOnPort(int port)
|
|
{
|
|
|
|
/* Switch the incoming ZIP packet handler to be the router version. */
|
|
|
|
CloseSocketOnNodeIfOpen(port, PortDescriptor(port)->aRouter,
|
|
ZonesInformationSocket);
|
|
if (OpenSocketOnNode(empty, port, &PortDescriptor(port)->aRouter,
|
|
ZonesInformationSocket,
|
|
ZipPacketInRouter, (long)0, False, empty, 0,
|
|
empty) isnt ATnoError)
|
|
{
|
|
ErrorLog("StartZipProcessingOnPort", ISevError, __LINE__, port,
|
|
IErrFullZipBadSocketOpen, IMsgFullZipBadSocketOpen,
|
|
Insert0());
|
|
return(False);
|
|
}
|
|
|
|
/* Try to get or set the zone information... */
|
|
|
|
if (PortDescriptor(port)->portType is NonAppleTalkHalfPort)
|
|
{
|
|
PortDescriptor(port)->thisZoneValid = False;
|
|
PortDescriptor(port)->theseZones = empty;
|
|
}
|
|
else if (not GetZoneListFor(port))
|
|
return(False);
|
|
|
|
/* Start the zip query timer. */
|
|
|
|
if (firstCall)
|
|
{
|
|
StartTimer(ZipQueryTimerExpired, ZipQueryTimerSeconds, 0, empty);
|
|
firstCall = False;
|
|
}
|
|
|
|
return(True);
|
|
|
|
} /* StartZipProcessingOnPort */
|
|
|
|
void far ShutdownFullZip(void)
|
|
{
|
|
|
|
firstCall = True;
|
|
|
|
} /* ShutdownFullZip */
|
|
|
|
long far ZipPacketInRouter(AppleTalkErrorCode errorCode,
|
|
long unsigned userData,
|
|
int port,
|
|
AppleTalkAddress source,
|
|
long destinationSocket,
|
|
int protocolType,
|
|
char far *datagram,
|
|
int datagramLength,
|
|
AppleTalkAddress actualDestination)
|
|
{
|
|
StaticForSmallStack char zoneName[MaximumZoneLength + 1];
|
|
BufferDescriptor packet;
|
|
RoutingTableEntry routingTableEntry;
|
|
int commandType;
|
|
int zoneCount, currentZoneIndex;
|
|
unsigned short networkNumber;
|
|
int zoneNameLength, numberOfZonesOnTheNetwork;
|
|
Boolean newPacket, packetFull, localZonesOnly;
|
|
Boolean extendedZipReply = False, useDefaultZone = False;
|
|
int unsentNetworkCount;
|
|
int index, outIndex, currentReplyType, nextReplyType;
|
|
int totalNetworkCount, networkCount;
|
|
int transactionId;
|
|
int zipCommand;
|
|
int startIndex;
|
|
int hashBucket;
|
|
Zones zoneList, zone;
|
|
UniqueZone uniqueZone, nextUniqueZone;
|
|
|
|
PortHandlers portHandlers;
|
|
|
|
/* "Use" unneeded actual parameters. */
|
|
|
|
destinationSocket, userData;
|
|
|
|
/* Only play if we've been asked nicely! */
|
|
|
|
if (errorCode is ATsocketClosed)
|
|
return((long)True);
|
|
else if (errorCode isnt ATnoError)
|
|
{
|
|
ErrorLog("ZipPacketInRouter", ISevError, __LINE__, port,
|
|
IErrFullZipBadIncomingError, IMsgFullZipBadIncomingError,
|
|
Insert0());
|
|
return((long)True);
|
|
}
|
|
if (not appleTalkRunning or not PortDescriptor(port)->portActive)
|
|
return((long)True);
|
|
if (protocolType isnt DdpProtocolZip and
|
|
protocolType isnt DdpProtocolAtp)
|
|
return((long)True);
|
|
|
|
/* Okay, process the request. */
|
|
|
|
DeferTimerChecking();
|
|
DeferIncomingPackets();
|
|
|
|
if (protocolType is DdpProtocolZip)
|
|
{
|
|
commandType = (unsigned char)datagram[ZipCommandOffset];
|
|
if (datagramLength < ZipFirstNetworkOffset)
|
|
{
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True); /* All ZIP commands have a command and network count! */
|
|
}
|
|
networkCount = (unsigned char)datagram[ZipNetworkCountOffset];
|
|
|
|
/* For a ZIP extended reply the "network count" is really not the
|
|
numbers of networks contained in the packet, it's the total number
|
|
of zones on the single network that is described by the reply. */
|
|
|
|
numberOfZonesOnTheNetwork = networkCount;
|
|
|
|
switch (commandType)
|
|
{
|
|
case ZipNetInfoReplyCommand:
|
|
case ZipNotifyCommand:
|
|
|
|
/* Don't be tellin us... we're a router! */
|
|
|
|
break;
|
|
|
|
case ZipGetNetInfoCommand:
|
|
|
|
if (not PortDescriptor(port)->extendedNetwork)
|
|
{
|
|
ErrorLog("ZipPacketInRouter", ISevWarning, __LINE__, port,
|
|
IErrFullZipNonExtended, IMsgFullZipNonExtended,
|
|
Insert0());
|
|
break;
|
|
}
|
|
if (PortDescriptor(port)->theseZones is Empty)
|
|
break; /* Not fully up yet... */
|
|
|
|
/* Get the zone name out of the request. */
|
|
|
|
if (datagramLength < ZipRequestedZoneNameOffset)
|
|
{
|
|
ErrorLog("ZipPacketInRouter", ISevWarning, __LINE__, port,
|
|
IErrFullZipMissingZoneLen, IMsgFullZipMissingZoneLen,
|
|
Insert0());
|
|
break;
|
|
}
|
|
zoneNameLength =
|
|
(unsigned char)datagram[ZipRequestedZoneLengthOffset];
|
|
if (zoneNameLength > MaximumZoneLength or
|
|
datagramLength < ZipRequestedZoneNameOffset + zoneNameLength)
|
|
{
|
|
ErrorLog("ZipPacketInRouter", ISevWarning, __LINE__, port,
|
|
IErrFullZipBadZone, IMsgFullZipBadZone,
|
|
Insert0());
|
|
break;
|
|
}
|
|
MoveMem(zoneName, datagram + ZipRequestedZoneNameOffset,
|
|
zoneNameLength);
|
|
zoneName[zoneNameLength] = 0;
|
|
|
|
/* Get a buffer descriptor for our reply. */
|
|
|
|
if ((packet = NewBufferDescriptor(MaximumDdpDatagramSize)) is Empty)
|
|
{
|
|
ErrorLog("ZipPacketInRouter", ISevError, __LINE__, port,
|
|
IErrFullZipOutOfMemory, IMsgFullZipOutOfMemory,
|
|
Insert0());
|
|
break;
|
|
}
|
|
|
|
/* Format a GetNetInfo reply command. */
|
|
|
|
packet->data[ZipCommandOffset] = ZipNetInfoReplyCommand;
|
|
packet->data[ZipFlagsOffset] = 0;
|
|
if (ElementsOnList(PortDescriptor(port)->theseZones) is 1)
|
|
packet->data[ZipFlagsOffset] |= (unsigned char)ZipOnlyOneZoneFlag;
|
|
TakeLock(RoutingLock);
|
|
if (zoneNameLength is 0 or
|
|
not ZoneInZones(zoneName, PortDescriptor(port)->theseZones))
|
|
{
|
|
packet->data[ZipFlagsOffset] |= (unsigned char)ZipZoneInvalidFlag;
|
|
useDefaultZone = True;
|
|
}
|
|
|
|
/* Add our cable range. */
|
|
|
|
packet->data[ZipFirstNetworkOffset] =
|
|
(char)(PortDescriptor(port)->thisCableRange.firstNetworkNumber
|
|
>> 8);
|
|
packet->data[ZipFirstNetworkOffset + 1] =
|
|
(char)(PortDescriptor(port)->thisCableRange.firstNetworkNumber
|
|
& 0xFF);
|
|
packet->data[ZipLastNetworkOffset] =
|
|
(char)(PortDescriptor(port)->thisCableRange.lastNetworkNumber
|
|
>> 8);
|
|
packet->data[ZipLastNetworkOffset + 1] =
|
|
(char)(PortDescriptor(port)->thisCableRange.lastNetworkNumber
|
|
& 0xFF);
|
|
|
|
/* Echo back the requested zone name. */
|
|
|
|
packet->data[ZipRequestedZoneLengthOffset] = (char)zoneNameLength;
|
|
MoveMem(packet->data + ZipRequestedZoneNameOffset, zoneName,
|
|
zoneNameLength);
|
|
index = ZipRequestedZoneNameOffset + zoneNameLength;
|
|
|
|
/* Place in the correct zone multicast address. */
|
|
|
|
portHandlers = &portSpecificInfo[PortDescriptor(port)->portType];
|
|
packet->data[index++] = (char)portHandlers->hardwareAddressLength;
|
|
if (useDefaultZone)
|
|
MoveMem(packet->data + index,
|
|
MulticastAddressForZoneOnPort(port,
|
|
PortDescriptor(port)->thisDefaultZone),
|
|
portHandlers->hardwareAddressLength);
|
|
else
|
|
MoveMem(packet->data + index,
|
|
MulticastAddressForZoneOnPort(port, zoneName),
|
|
portHandlers->hardwareAddressLength);
|
|
index += portHandlers->hardwareAddressLength;
|
|
|
|
/* If we need it, add in the default zone. */
|
|
|
|
if (useDefaultZone)
|
|
{
|
|
zoneNameLength = strlen(PortDescriptor(port)->thisDefaultZone);
|
|
packet->data[index++] = (char)zoneNameLength;
|
|
MoveMem(packet->data + index,
|
|
PortDescriptor(port)->thisDefaultZone,
|
|
zoneNameLength);
|
|
index += zoneNameLength;
|
|
}
|
|
|
|
/* If the request came as a cable-wide broadcast and its source
|
|
network is not valid for this port, then we want to respond
|
|
to cable-wide broadcast rather than the source. */
|
|
|
|
if (actualDestination.networkNumber is
|
|
CableWideBroadcastNetworkNumber and
|
|
actualDestination.nodeNumber is AppleTalkBroadcastNodeNumber and
|
|
not IsWithinNetworkRange(source.networkNumber,
|
|
&PortDescriptor(port)->thisCableRange)
|
|
and
|
|
not IsWithinNetworkRange(source.networkNumber,
|
|
&startupNetworkRange))
|
|
{
|
|
source.networkNumber = CableWideBroadcastNetworkNumber;
|
|
source.nodeNumber = AppleTalkBroadcastNodeNumber;
|
|
}
|
|
|
|
/* Okay, finally deliver our reply. */
|
|
|
|
ReleaseLock(RoutingLock);
|
|
if (DeliverDdp(destinationSocket, source, DdpProtocolZip, packet,
|
|
index, Empty, Empty, 0) isnt ATnoError)
|
|
ErrorLog("ZipPacketInRouter", ISevError, __LINE__, port,
|
|
IErrFullZipBadInfoReplySend, IMsgFullZipBadInfoReplySend,
|
|
Insert0());
|
|
|
|
break;
|
|
|
|
case ZipQueryCommand:
|
|
|
|
/* Walk thru the query packet building reply packets that have
|
|
as much information as we know. The "+ sizeof(short)" means
|
|
that we have complete network number.
|
|
|
|
When sending replies, we will always send a reply about a
|
|
network that has more than one zone as an extended reply.
|
|
As were walking the query list, and we encounter a couple of
|
|
networks that have only one zone, we'll pack as many of these
|
|
as we can into a non-extended reply. */
|
|
|
|
newPacket = True;
|
|
unsentNetworkCount = 0;
|
|
|
|
/* Allocate an initial buffer descriptor. */
|
|
|
|
if ((packet = NewBufferDescriptor(MaximumDdpDatagramSize)) is Empty)
|
|
{
|
|
ErrorLog("ZipPacketInRouter", ISevError, __LINE__, port,
|
|
IErrFullZipOutOfMemory, IMsgFullZipOutOfMemory,
|
|
Insert0());
|
|
break;
|
|
}
|
|
|
|
for (index = ZipFirstNetworkOffset,
|
|
totalNetworkCount = 0;
|
|
totalNetworkCount < networkCount and
|
|
index + (int)sizeof(short) <= datagramLength;
|
|
index += (int)sizeof(short),
|
|
totalNetworkCount += 1)
|
|
{
|
|
/* Grab the next network number from the query packet, if we
|
|
don't know about the network, or we don't know the zone
|
|
name, continue with the next network number. */
|
|
|
|
networkNumber = (unsigned short)
|
|
(((unsigned char)datagram[index]) << 8);
|
|
networkNumber += (unsigned char)datagram[index + 1];
|
|
|
|
/* Try port descriptors first... we'll get queries from
|
|
ourselfs to fill the initial routing table entry's zone
|
|
list! */
|
|
|
|
routingTableEntry = Empty;
|
|
if (PortDescriptor(port)->thisCableRange.firstNetworkNumber is
|
|
networkNumber and
|
|
PortDescriptor(port)->theseZones isnt empty)
|
|
zoneList = PortDescriptor(port)->theseZones;
|
|
else if ((routingTableEntry =
|
|
FindInRoutingTable(networkNumber)) is empty or
|
|
routingTableEntry->networkRange.firstNetworkNumber isnt
|
|
networkNumber or
|
|
not routingTableEntry->zonesValid)
|
|
{
|
|
UnlinkRoutingTableEntry(routingTableEntry);
|
|
continue;
|
|
}
|
|
else
|
|
zoneList = routingTableEntry->zones;
|
|
|
|
/* What type of reponse does this network want? */
|
|
|
|
if ((numberOfZonesOnTheNetwork = ElementsOnList(zoneList)) is 0)
|
|
{
|
|
UnlinkRoutingTableEntry(routingTableEntry);
|
|
ErrorLog("ZipPacketInRouter", ISevError, __LINE__, port,
|
|
IErrFullZipBadZoneList, IMsgFullZipBadZoneList,
|
|
Insert0());
|
|
continue;
|
|
}
|
|
else if (numberOfZonesOnTheNetwork is 1)
|
|
nextReplyType = ZipReplyCommand;
|
|
else
|
|
{
|
|
/* We start a new packet for each extended network, if we
|
|
have a previous one, send it! */
|
|
|
|
if (unsentNetworkCount > 0)
|
|
{
|
|
packet->data[ZipCommandOffset] = (char)currentReplyType;
|
|
if (currentReplyType is ZipReplyCommand)
|
|
packet->data[ZipNetworkCountOffset] =
|
|
(char)unsentNetworkCount;
|
|
|
|
/* Send the packet! */
|
|
|
|
if (DeliverDdp(destinationSocket, source,
|
|
DdpProtocolZip, packet,
|
|
outIndex, Empty, Empty, 0) isnt ATnoError)
|
|
ErrorLog("ZipPacketInRouter", ISevError, __LINE__, port,
|
|
IErrFullZipBadReplySend, IMsgFullZipBadReplySend,
|
|
Insert0());
|
|
|
|
if ((packet = NewBufferDescriptor(MaximumDdpDatagramSize))
|
|
is Empty)
|
|
{
|
|
UnlinkRoutingTableEntry(routingTableEntry);
|
|
ErrorLog("ZipPacketInRouter", ISevError, __LINE__, port,
|
|
IErrFullZipOutOfMemory, IMsgFullZipOutOfMemory,
|
|
Insert0());
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
|
|
unsentNetworkCount = 0;
|
|
}
|
|
nextReplyType = ZipExtendedReplyCommand;
|
|
newPacket = True;
|
|
packet->data[ZipNetworkCountOffset] =
|
|
(char)numberOfZonesOnTheNetwork;
|
|
}
|
|
|
|
/* Walk the zone list. */
|
|
|
|
for (zone = zoneList;
|
|
zone isnt empty;
|
|
zone = zone->next)
|
|
{
|
|
|
|
/* If we're starting to build a new reply packet due to
|
|
either:
|
|
|
|
1. first time through
|
|
2. packet full
|
|
3. switching reply types
|
|
|
|
set the index to the first tuple position. */
|
|
|
|
if (newPacket or
|
|
currentReplyType isnt nextReplyType)
|
|
{
|
|
/* If a previous packet has been built, send it. */
|
|
|
|
if (unsentNetworkCount > 0)
|
|
{
|
|
packet->data[ZipCommandOffset] = (char)currentReplyType;
|
|
if (currentReplyType is ZipReplyCommand)
|
|
packet->data[ZipNetworkCountOffset] =
|
|
(char)unsentNetworkCount;
|
|
|
|
/* Send the packet! */
|
|
|
|
if (DeliverDdp(destinationSocket, source,
|
|
DdpProtocolZip, packet,
|
|
outIndex, Empty, Empty, 0) isnt ATnoError)
|
|
ErrorLog("ZipPacketInRouter", ISevError, __LINE__, port,
|
|
IErrFullZipBadReplySend, IMsgFullZipBadReplySend,
|
|
Insert0());
|
|
|
|
if ((packet = NewBufferDescriptor(MaximumDdpDatagramSize))
|
|
is Empty)
|
|
{
|
|
UnlinkRoutingTableEntry(routingTableEntry);
|
|
ErrorLog("ZipPacketInRouter", ISevError, __LINE__, port,
|
|
IErrFullZipOutOfMemory, IMsgFullZipOutOfMemory,
|
|
Insert0());
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
|
|
unsentNetworkCount = 0;
|
|
}
|
|
|
|
outIndex = ZipFirstNetworkOffset;
|
|
currentReplyType = nextReplyType;
|
|
newPacket = False;
|
|
}
|
|
|
|
/* Unfortunately, we seem to know the answer to the question.
|
|
Pack a new network/zone tuple into the reply packet. */
|
|
|
|
packet->data[outIndex++] = (char)((networkNumber >> 8) & 0xFF);
|
|
packet->data[outIndex++] = (char)(networkNumber & 0xFF);
|
|
packet->data[outIndex++] = (char)(zoneNameLength =
|
|
strlen(zone->zone->zone));
|
|
MoveMem(packet->data + outIndex, zone->zone->zone,
|
|
zoneNameLength);
|
|
outIndex += zoneNameLength;
|
|
unsentNetworkCount += 1;
|
|
|
|
/* If we can't hold another big tuple, signal that we should
|
|
send on the next pass. */
|
|
|
|
if (outIndex + sizeof(short) + sizeof(char) +
|
|
MaximumZoneLength >= MaximumDdpDatagramSize)
|
|
newPacket = True;
|
|
|
|
} /* Walk zone list. */
|
|
|
|
UnlinkRoutingTableEntry(routingTableEntry);
|
|
|
|
} /* Walk through each network in the query packet. */
|
|
|
|
/* If we have unsent networks left over, send them out! */
|
|
|
|
if (unsentNetworkCount > 0)
|
|
{
|
|
packet->data[ZipCommandOffset] = (char)currentReplyType;
|
|
if (currentReplyType is ZipReplyCommand)
|
|
packet->data[ZipNetworkCountOffset] =
|
|
(char)unsentNetworkCount;
|
|
|
|
/* Send the packet! */
|
|
|
|
if (DeliverDdp(destinationSocket, source,
|
|
DdpProtocolZip, packet,
|
|
outIndex, Empty, Empty, 0) isnt ATnoError)
|
|
ErrorLog("ZipPacketInRouter", ISevError, __LINE__, port,
|
|
IErrFullZipBadReplySend, IMsgFullZipBadReplySend,
|
|
Insert0());
|
|
|
|
unsentNetworkCount = 0;
|
|
}
|
|
else
|
|
FreeBufferChain(packet); /* Free unused buffer chain. */
|
|
|
|
break;
|
|
|
|
case ZipExtendedReplyCommand:
|
|
|
|
extendedZipReply = True;
|
|
/* And fall through... */
|
|
|
|
case ZipReplyCommand:
|
|
|
|
/* Walk through the reply packet (assuming we asked for the
|
|
contained information). Yes, we're still using "networkCount"
|
|
when processing an extended reply, but that's okay because it
|
|
will certainly be at least the number of zones contained in
|
|
this packet. The "+ 3" garuntees that we really have network
|
|
number and zone length! */
|
|
|
|
for (index = ZipFirstNetworkOffset,
|
|
totalNetworkCount = 0;
|
|
totalNetworkCount < networkCount and
|
|
index + 3 <= datagramLength;
|
|
totalNetworkCount += 1)
|
|
{
|
|
/* Get the next network number, if it's not in our routing
|
|
table (or not the start of a range), then we certainly
|
|
don't care about its zone name. */
|
|
|
|
networkNumber = (unsigned short)
|
|
(((unsigned char)(datagram[index++])) << 8);
|
|
networkNumber += (unsigned char)(datagram[index++]);
|
|
zoneNameLength = (unsigned char)(datagram[index++]);
|
|
routingTableEntry = FindInRoutingTable(networkNumber);
|
|
if (routingTableEntry is empty or
|
|
routingTableEntry->networkRange.firstNetworkNumber isnt
|
|
networkNumber)
|
|
{
|
|
UnlinkRoutingTableEntry(routingTableEntry);
|
|
index += zoneNameLength;
|
|
continue;
|
|
}
|
|
|
|
/* Okay validate the zone name. */
|
|
|
|
if (zoneNameLength is 0 or
|
|
zoneNameLength > MaximumZoneLength)
|
|
{
|
|
UnlinkRoutingTableEntry(routingTableEntry);
|
|
ErrorLog("ZipPacketInRouter", ISevWarning, __LINE__, port,
|
|
IErrFullZipBadReplyPacket, IMsgFullZipBadReplyPacket,
|
|
Insert0());
|
|
break; /* Oops, corrupted packet! */
|
|
}
|
|
if (index + zoneNameLength > datagramLength)
|
|
{
|
|
UnlinkRoutingTableEntry(routingTableEntry);
|
|
ErrorLog("ZipPacketInRouter", ISevWarning, __LINE__, port,
|
|
IErrFullZipBadZone, IMsgFullZipBadZone,
|
|
Insert0());
|
|
break; /* Oops, corrupted packet! */
|
|
}
|
|
|
|
/* Conditionally, move the zone name into our routing table. */
|
|
|
|
MoveMem(zoneName, datagram + index, zoneNameLength);
|
|
index += zoneNameLength;
|
|
zoneName[zoneNameLength] = 0;
|
|
TakeLock(RoutingLock);
|
|
if (ZoneInZones(zoneName, routingTableEntry->zones))
|
|
{
|
|
UnlinkRoutingTableEntry(routingTableEntry);
|
|
continue;
|
|
}
|
|
|
|
/* Check for the obscure case of somebody out there trying to
|
|
add another zone to one of our directly connected networks
|
|
that is non-extended and we already know its zone. */
|
|
|
|
if (routingTableEntry->numberOfHops is 0 and
|
|
not PortDescriptor(routingTableEntry->port)->
|
|
extendedNetwork and
|
|
ElementsOnList(routingTableEntry->zones) is 1)
|
|
{
|
|
ReleaseLock(RoutingLock);
|
|
UnlinkRoutingTableEntry(routingTableEntry);
|
|
ErrorLog("ZipPacketInRouter", ISevWarning, __LINE__, port,
|
|
IErrFullZipTooManyZones, IMsgFullZipTooManyZones,
|
|
Insert0());
|
|
continue;
|
|
}
|
|
|
|
/* Okay, add to list. */
|
|
|
|
if ((routingTableEntry->zones =
|
|
AddZoneToZones(routingTableEntry->zones, zoneName))
|
|
is Empty)
|
|
{
|
|
ReleaseLock(RoutingLock);
|
|
UnlinkRoutingTableEntry(routingTableEntry);
|
|
ErrorLog("ZipPacketInRouter", ISevError, __LINE__, port,
|
|
IErrFullZipCantAddZone, IMsgFullZipCantAddZone,
|
|
Insert0());
|
|
routingTableEntry->zonesValid = False;
|
|
continue;
|
|
}
|
|
ReleaseLock(RoutingLock);
|
|
|
|
/* If we're not an extended reply, we know that we have all of
|
|
the information about a given network contained in this
|
|
packet, so we can go ahead and mark the zone list valid
|
|
now. */
|
|
|
|
if (not extendedZipReply)
|
|
routingTableEntry->zonesValid = True;
|
|
|
|
/* Okay, if we just handled an extended reply, do we now know all
|
|
that we should know about the specfied network? */
|
|
|
|
if (extendedZipReply and
|
|
ElementsOnList(routingTableEntry->zones) >=
|
|
numberOfZonesOnTheNetwork)
|
|
routingTableEntry->zonesValid = True;
|
|
|
|
UnlinkRoutingTableEntry(routingTableEntry);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
ErrorLog("ZipPacketInRouter", ISevWarning, __LINE__, port,
|
|
IErrFullZipFunnyCommand, IMsgFullZipFunnyCommand,
|
|
Insert0());
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* An ATP/ZIP request! */
|
|
|
|
/* This had better be a GetZoneList, a GetLocalZones, or a GetMyZone
|
|
ATP request! */
|
|
|
|
if ((datagram[AtpCommandControlOffset] & AtpFunctionCodeMask) isnt
|
|
AtpRequestFunctionCode)
|
|
{
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True); /* Why are they talking to us??? */
|
|
}
|
|
if (datagram[AtpBitmapOffset] isnt 1)
|
|
{
|
|
ErrorLog("ZipPacketInRouter", ISevWarning, __LINE__, port,
|
|
IErrFullZipLongReplyExpected, IMsgFullZipLongReplyExpected,
|
|
Insert0());
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True); /* More that one reply packet isnt an option! */
|
|
}
|
|
|
|
transactionId = ((unsigned char)datagram[AtpTransactionIdOffset]) << 8;
|
|
transactionId += (unsigned char)datagram[AtpTransactionIdOffset + 1];
|
|
|
|
/* Make sure it's one of the three requests that we know how to deal
|
|
with. */
|
|
|
|
zipCommand = (unsigned char)datagram[AtpZipCommandOffset] & 0xFF;
|
|
if (zipCommand isnt ZipGetZoneListCommand and
|
|
zipCommand isnt ZipGetLocalZonesCommand and
|
|
zipCommand isnt ZipGetMyZoneCommand)
|
|
{
|
|
ErrorLog("ZipPacketInRouter", ISevWarning, __LINE__, port,
|
|
IErrFullZipFunnyRequest, IMsgFullZipFunnyRequest,
|
|
Insert0());
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
|
|
/* Get start index... only meaningfull for GetZoneList and
|
|
GetLocalZones requests. */
|
|
|
|
startIndex = ((unsigned char)datagram[AtpZipStartIndexOffset] << 8);
|
|
startIndex += (unsigned char)datagram[AtpZipStartIndexOffset + 1];
|
|
|
|
/* Allocate a buffer descriptor for the reply. */
|
|
|
|
if ((packet = NewBufferDescriptor(MaximumDdpDatagramSize)) is Empty)
|
|
{
|
|
ErrorLog("ZipPacketInRouter", ISevError, __LINE__, port,
|
|
IErrFullZipOutOfMemory, IMsgFullZipOutOfMemory,
|
|
Insert0());
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
|
|
/* Okay, build the static part of an ATP reply packet. */
|
|
|
|
packet->data[AtpCommandControlOffset] =
|
|
(unsigned char)(AtpResponseFunctionCode + AtpEndOfMessageMask);
|
|
packet->data[AtpSequenceNumberOffset] = 0;
|
|
packet->data[AtpTransactionIdOffset] = (char)((transactionId >> 8) & 0xFF);
|
|
packet->data[AtpTransactionIdOffset + 1] = (char)(transactionId & 0xFF);
|
|
|
|
/* Handle GetMyZone: */
|
|
|
|
if (zipCommand is ZipGetMyZoneCommand)
|
|
{
|
|
/* We really shouldn't be getting this request on an extended network,
|
|
but go ahead and reply with the "default zone" in this case, of
|
|
course, reply with "this zone" for non-extended nets. We are a
|
|
router, so "thisZone" will always be valid -- as will the default
|
|
zone for extended nets and as will "this zone" for non-extended. */
|
|
|
|
packet->data[AtpZipLastFlagOffset] = 0;
|
|
packet->data[AtpZipLastFlagOffset + 1] = 0;
|
|
packet->data[AtpZipStartIndexOffset] = 0;
|
|
packet->data[AtpZipStartIndexOffset + 1] = 1;
|
|
|
|
TakeLock(RoutingLock);
|
|
if (PortDescriptor(port)->extendedNetwork)
|
|
{
|
|
zoneNameLength = strlen(PortDescriptor(port)->thisDefaultZone);
|
|
packet->data[AtpDataOffset] = (char)zoneNameLength;
|
|
MoveMem(packet->data + AtpDataOffset + 1,
|
|
PortDescriptor(port)->thisDefaultZone, zoneNameLength);
|
|
}
|
|
else
|
|
{
|
|
zoneNameLength = strlen(PortDescriptor(port)->thisZone);
|
|
packet->data[AtpDataOffset] = (char)zoneNameLength;
|
|
MoveMem(packet->data + AtpDataOffset + 1,
|
|
PortDescriptor(port)->thisZone, zoneNameLength);
|
|
}
|
|
ReleaseLock(RoutingLock);
|
|
|
|
/* Blast the reply out! */
|
|
|
|
if (DeliverDdp(destinationSocket, source, DdpProtocolAtp,
|
|
packet, AtpDataOffset + 1 + zoneNameLength,
|
|
Empty, Empty, 0)
|
|
isnt ATnoError)
|
|
ErrorLog("ZipPacketInRouter", ISevError, __LINE__, port,
|
|
IErrFullZipBadMyZoneSend, IMsgFullZipBadMyZoneSend,
|
|
Insert0());
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
|
|
/* Okay, A little harder here, fill the GetZoneList or GetLocalZones
|
|
reply packet full of as many zomes as it'll hold; starting at the
|
|
requested start index. */
|
|
|
|
localZonesOnly = (zipCommand is ZipGetLocalZonesCommand);
|
|
|
|
/* Walk the zones table. */
|
|
|
|
index = AtpDataOffset;
|
|
packetFull = False;
|
|
for (hashBucket = 0, zoneCount = 0, currentZoneIndex = 0;
|
|
hashBucket < ZoneTableHashBuckets and not packetFull;
|
|
hashBucket += 1)
|
|
{
|
|
TakeLock(RoutingLock);
|
|
uniqueZone = Link(zoneTable[hashBucket]);
|
|
ReleaseLock(RoutingLock);
|
|
while(uniqueZone isnt Empty and not packetFull)
|
|
{
|
|
while (True) /* Something to break out of. */
|
|
{
|
|
TakeLock(RoutingLock);
|
|
if (localZonesOnly and
|
|
not ZoneInZones(uniqueZone->zone,
|
|
PortDescriptor(port)->theseZones))
|
|
{
|
|
ReleaseLock(RoutingLock);
|
|
break;
|
|
}
|
|
ReleaseLock(RoutingLock);
|
|
|
|
/* Are we at the point that the requester wants us to start from
|
|
yet? */
|
|
|
|
currentZoneIndex += 1;
|
|
if (currentZoneIndex < startIndex)
|
|
break;
|
|
|
|
/* Place a zone name in the packet. */
|
|
|
|
zoneNameLength = strlen(uniqueZone->zone);
|
|
packet->data[index++] = (char)zoneNameLength;
|
|
MoveMem(packet->data + index, uniqueZone->zone, zoneNameLength);
|
|
index += zoneNameLength;
|
|
zoneCount += 1;
|
|
|
|
/* Can this packet hold another of the longest zone? */
|
|
|
|
if (index + sizeof(char) + MaximumZoneLength >=
|
|
MaximumDdpDatagramSize)
|
|
packetFull = True;
|
|
|
|
break;
|
|
}
|
|
|
|
/* Move to next unique zone. */
|
|
|
|
TakeLock(RoutingLock);
|
|
nextUniqueZone = Link(uniqueZone->next);
|
|
ReleaseLock(RoutingLock);
|
|
UnlinkZone(uniqueZone);
|
|
uniqueZone = nextUniqueZone;
|
|
}
|
|
}
|
|
|
|
UnlinkZone(uniqueZone); /* Maybe Empty, maybe not. */
|
|
|
|
/* Okay, we've built a packet, fill in the number of zones field,
|
|
and the Last flag. */
|
|
|
|
packet->data[AtpZipLastFlagOffset] =
|
|
(char)(hashBucket >= ZoneTableHashBuckets and
|
|
uniqueZone is Empty);
|
|
packet->data[AtpZipLastFlagOffset + 1] = 0;
|
|
packet->data[AtpZipZoneCountOffset] =
|
|
(char)((zoneCount >> 8) & 0xFF);
|
|
packet->data[AtpZipZoneCountOffset + 1] =
|
|
(char)(zoneCount & 0xFF);
|
|
|
|
/* Send out the packet. */
|
|
|
|
if (DeliverDdp(destinationSocket, source, DdpProtocolAtp,
|
|
packet, index, Empty, Empty, 0) isnt ATnoError)
|
|
ErrorLog("ZipPacketInRouter", ISevError, __LINE__, port,
|
|
IErrFullZipBadZoneListSend, IMsgFullZipBadZoneListSend,
|
|
Insert0());
|
|
|
|
} /* ZIP/ATP request */
|
|
|
|
/* All set. */
|
|
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
|
|
} /* ZipPacketInRouter */
|
|
|
|
/* *************************************************************************
|
|
LinkZone
|
|
|
|
Given a zone name string, enter it into the zones table, if it's not already
|
|
there. Return the UniqueZone after incrementing the reference count.
|
|
This rotuine should be called with the RoutingLock already held by the
|
|
caller.
|
|
|
|
Returns: UniqueZone
|
|
************************************************************************* */
|
|
|
|
UniqueZone far LinkZone(char far *zone)
|
|
{
|
|
short hash = HashString(zone);
|
|
int index;
|
|
UniqueZone uniqueZone;
|
|
static long uniqueId = 1;
|
|
|
|
/* Is our zone already home? */
|
|
|
|
CheckMod(index, hash, ZoneTableHashBuckets, "LinkZone");
|
|
for (uniqueZone = zoneTable[index];
|
|
uniqueZone isnt Empty;
|
|
uniqueZone = uniqueZone->next)
|
|
if (uniqueZone->zoneHash is hash and
|
|
CompareCaseInsensitive(uniqueZone->zone, zone))
|
|
break;
|
|
|
|
if (uniqueZone isnt Empty)
|
|
{
|
|
/* We're all set, the zone is already in the table. */
|
|
|
|
return(Link(uniqueZone));
|
|
}
|
|
|
|
/* Okay, we need to build a new unique zone. */
|
|
|
|
if ((uniqueZone = Calloc(sizeof(*uniqueZone) + strlen(zone) + 1, 1)) is Empty)
|
|
{
|
|
ErrorLog("LinkZone", ISevError, __LINE__, UnknownPort,
|
|
IErrFullZipOutOfMemory, IMsgFullZipOutOfMemory,
|
|
Insert0());
|
|
return(Empty);
|
|
}
|
|
|
|
/* Okay, build and link the new kid on the block. */
|
|
|
|
uniqueZone->uniqueId = uniqueId++;
|
|
uniqueZone->zoneHash = hash;
|
|
strcpy(uniqueZone->zone, zone);
|
|
uniqueZone->next = zoneTable[index];
|
|
zoneTable[index] = uniqueZone;
|
|
return(Link(uniqueZone));
|
|
|
|
} /* LinkZone */
|
|
|
|
/* *************************************************************************
|
|
UnlinkZone
|
|
|
|
Given a UniqueZone, decrease it's reference count. If we're the last
|
|
referant, remove him from the zones table.
|
|
|
|
Returns: void
|
|
************************************************************************* */
|
|
|
|
void far UnlinkZone(UniqueZone zone)
|
|
{
|
|
short hash;
|
|
int index;
|
|
|
|
/* Undo one link, if we're not the last, return. */
|
|
|
|
if (zone is Empty)
|
|
return;
|
|
hash = HashString(zone->zone);
|
|
CheckMod(index, hash, ZoneTableHashBuckets, "LinkZone");
|
|
TakeLock(RoutingLock);
|
|
if (not UnlinkNoFree(zone))
|
|
{
|
|
ReleaseLock(RoutingLock);
|
|
return;
|
|
}
|
|
|
|
/* Okay, we're the last, free him. */
|
|
|
|
if (not RemoveFromListNoUnlink(zoneTable[index], zone, next))
|
|
{
|
|
ReleaseLock(RoutingLock);
|
|
ErrorLog("UnlinkZone", ISevError, __LINE__, UnknownPort,
|
|
IErrFullZipZoneMissing, IMsgFullZipZoneMissing,
|
|
Insert0());
|
|
return;
|
|
}
|
|
ReleaseLock(RoutingLock);
|
|
Free(zone);
|
|
return;
|
|
|
|
} /* UnlinkZone */
|
|
|
|
/* *************************************************************************
|
|
ZoneInZones
|
|
|
|
Search a Zones list for a given zone. The routine should be called with
|
|
the caller holding the RoutingLock.
|
|
|
|
Returns: Boolean; True if zone on list.
|
|
************************************************************************* */
|
|
|
|
Boolean far ZoneInZones(char far *zone, Zones zones)
|
|
{
|
|
short hash = HashString(zone);
|
|
|
|
/* Anybody home? */
|
|
|
|
while (zones isnt Empty)
|
|
{
|
|
if (zones->zone->zoneHash is hash and
|
|
CompareCaseInsensitive(zones->zone->zone, zone))
|
|
return(True);
|
|
zones = zones->next;
|
|
}
|
|
return(False);
|
|
|
|
} /* ZoneInZones */
|
|
|
|
|
|
/* *************************************************************************
|
|
AddZoneToZones
|
|
|
|
Add a new zone to a Zones list. This routine should be called with the
|
|
caller holding the RoutingLock.
|
|
|
|
Returns: Zones; the new zones list.
|
|
************************************************************************* */
|
|
|
|
Zones far AddZoneToZones(Zones zones, char far *zone)
|
|
{
|
|
Zones newZones;
|
|
|
|
if ((newZones = Calloc(sizeof(*newZones), 1)) is Empty)
|
|
{
|
|
ErrorLog("AddZoneToZones", ISevError, __LINE__, UnknownPort,
|
|
IErrFullZipOutOfMemory, IMsgFullZipOutOfMemory,
|
|
Insert0());
|
|
return(Empty);
|
|
}
|
|
|
|
if ((newZones->zone = LinkZone(zone)) is Empty)
|
|
{
|
|
Free(newZones);
|
|
return(Empty);
|
|
}
|
|
newZones->next = zones;
|
|
return(newZones);
|
|
|
|
} /* AddZoneToZones */
|
|
|
|
/* *************************************************************************
|
|
FreeZones
|
|
|
|
Free a Zones list.
|
|
|
|
Returns: void
|
|
************************************************************************* */
|
|
|
|
void far FreeZones(Zones zones)
|
|
{
|
|
Zones nextZones;
|
|
|
|
while (zones isnt Empty)
|
|
{
|
|
nextZones = zones->next;
|
|
UnlinkZone(zones->zone);
|
|
Free(zones);
|
|
zones = nextZones;
|
|
}
|
|
|
|
} /* FreeZones */
|
|
|
|
/* *************************************************************************
|
|
ZoneListToZones
|
|
|
|
Take a ZoneList and convert it to a Zones list.
|
|
|
|
Returns: Zones; the new zones list.
|
|
************************************************************************* */
|
|
|
|
Zones far ZoneListToZones(ZoneList zoneList)
|
|
{
|
|
Zones zones = Empty, newZones;
|
|
|
|
while (zoneList isnt Empty)
|
|
{
|
|
TakeLock(RoutingLock);
|
|
if ((newZones = AddZoneToZones(zones, zoneList->zone)) is Empty)
|
|
{
|
|
ReleaseLock(RoutingLock);
|
|
FreeZones(zones);
|
|
return(Empty);
|
|
}
|
|
ReleaseLock(RoutingLock);
|
|
zones = newZones;
|
|
zoneList = zoneList->next;
|
|
}
|
|
|
|
return(zones);
|
|
|
|
} /* ZoneListToZones */
|
|
|
|
/* *************************************************************************
|
|
CopyZones
|
|
|
|
Produce a copy of a Zones list. We assume the thing were copying from
|
|
won't be freed out from under us (the list is hanging off a Linked
|
|
RoutingTableEntry).
|
|
|
|
Returns: Zones; the new zones list.
|
|
************************************************************************* */
|
|
|
|
Zones far CopyZones(Zones zones)
|
|
{
|
|
Zones newZones, newZone, newZonesHead = Empty;
|
|
|
|
while (zones isnt Empty)
|
|
{
|
|
if ((newZone = Calloc(sizeof(*newZones), 1)) is Empty)
|
|
{
|
|
ErrorLog("CopyZones", ISevError, __LINE__, UnknownPort,
|
|
IErrFullZipOutOfMemory, IMsgFullZipOutOfMemory,
|
|
Insert0());
|
|
FreeZones(newZonesHead);
|
|
return(Empty);
|
|
}
|
|
TakeLock(RoutingLock);
|
|
if (newZonesHead is Empty)
|
|
{
|
|
newZonesHead = newZones;
|
|
newZones = newZone;
|
|
}
|
|
else
|
|
{
|
|
newZones->next = newZone;
|
|
newZones = newZone;
|
|
}
|
|
newZones->zone = Link(zones->zone);
|
|
zones = zones->next;
|
|
ReleaseLock(RoutingLock);
|
|
}
|
|
|
|
return(newZonesHead);
|
|
|
|
} /* CopyZones */
|
|
|
|
ExternForVisibleFunction void far ZipQueryTimerExpired(long unsigned timerId,
|
|
int additionalDataSize,
|
|
char far *additionalData)
|
|
{
|
|
RoutingTableEntry routingTableEntry, nextRoutingTableEntry;
|
|
BufferDescriptor datagram;
|
|
int hashBucket;
|
|
AppleTalkAddress source, destination;
|
|
long sourceSocket;
|
|
int port;
|
|
|
|
/* "Use" unneeded actual parameters. */
|
|
|
|
timerId, additionalDataSize, additionalData;
|
|
|
|
/* We're going to muck with the routing databases, so defer incoming
|
|
packets while we do the deed. */
|
|
|
|
DeferIncomingPackets();
|
|
|
|
/* We want to iterate through the routing table and send out a query to any
|
|
network that we don't know the zone name of. */
|
|
|
|
/* Walk through the routing table looking for networks that we don't know
|
|
the zone of... */
|
|
|
|
for (hashBucket = 0;
|
|
hashBucket < NumberOfRtmpHashBuckets;
|
|
hashBucket += 1)
|
|
{
|
|
TakeLock(RoutingLock);
|
|
routingTableEntry = Link(routingTable[hashBucket]);
|
|
ReleaseLock(RoutingLock);
|
|
while (routingTableEntry isnt empty)
|
|
{
|
|
while(True) /* Just something to "break" out of. */
|
|
{
|
|
/* If we already know the zone for this network, skip it. */
|
|
|
|
if (routingTableEntry->zonesValid)
|
|
break;
|
|
|
|
/* Get a buffer descriptor for our query. */
|
|
|
|
if ((datagram = NewBufferDescriptor(ZipOneZoneQueryDdpSize)) is Empty)
|
|
{
|
|
ErrorLog("ZipQueryTimerExpired", ISevError, __LINE__, UnknownPort,
|
|
IErrFullZipOutOfMemory, IMsgFullZipOutOfMemory,
|
|
Insert0());
|
|
break;
|
|
}
|
|
|
|
/* Build the static part of the query packet. */
|
|
|
|
datagram->data[ZipCommandOffset] = ZipQueryCommand;
|
|
datagram->data[ZipNetworkCountOffset] = 1;
|
|
|
|
/* Place the network number in question into the query packet. */
|
|
|
|
datagram->data[ZipFirstNetworkOffset] =
|
|
(char)((routingTableEntry->networkRange.firstNetworkNumber
|
|
>> 8) & 0xFF);
|
|
datagram->data[ZipFirstNetworkOffset + 1] =
|
|
(char)(routingTableEntry->networkRange.firstNetworkNumber & 0xFF);
|
|
|
|
/* Compute the source and destination for this request. */
|
|
|
|
source.networkNumber =
|
|
PortDescriptor(routingTableEntry->port)->aRouter.networkNumber;
|
|
source.nodeNumber =
|
|
PortDescriptor(routingTableEntry->port)->aRouter.nodeNumber;
|
|
port = routingTableEntry->port;
|
|
if (PortDescriptor(port)->portType is NonAppleTalkHalfPort and
|
|
source.networkNumber is UnknownNetworkNumber)
|
|
{
|
|
/* If we're sending out a half port, pick a more reasonable
|
|
source, so that the reply will get delivered to the router
|
|
and correctly entered into the routing tables. */
|
|
|
|
for (port = 0; port < MaximumNumberOfPorts; port += 1)
|
|
if (PortDescriptor(port)->portActive and
|
|
PortDescriptor(port)->routerRunning)
|
|
{
|
|
source.networkNumber =
|
|
PortDescriptor(port)->aRouter.networkNumber;
|
|
source.nodeNumber =
|
|
PortDescriptor(port)->aRouter.nodeNumber;
|
|
break;
|
|
}
|
|
if (port > MaximumNumberOfPorts)
|
|
port = routingTableEntry->port;
|
|
}
|
|
source.socketNumber = ZonesInformationSocket;
|
|
|
|
if (routingTableEntry->numberOfHops is 0)
|
|
{
|
|
/* Yes, this is talking to ourselves, but that's an option for
|
|
filling up initial routing tables entries. */
|
|
|
|
destination.networkNumber =
|
|
PortDescriptor(routingTableEntry->port)->aRouter.networkNumber;
|
|
destination.nodeNumber =
|
|
PortDescriptor(routingTableEntry->port)->aRouter.nodeNumber;
|
|
}
|
|
else
|
|
{
|
|
destination.networkNumber =
|
|
routingTableEntry->nextRouter.networkNumber;
|
|
destination.nodeNumber =
|
|
routingTableEntry->nextRouter.nodeNumber;
|
|
}
|
|
destination.socketNumber = ZonesInformationSocket;
|
|
|
|
if ((sourceSocket = MapAddressToSocket(port, source)) < ATnoError)
|
|
{
|
|
ErrorLog("ZipQueryTimerExpired", ISevError, __LINE__,
|
|
routingTableEntry->port,
|
|
IErrFullZipSocketNotOpen, IMsgFullZipSocketNotOpen,
|
|
Insert0());
|
|
FreeBufferChain(datagram);
|
|
break;
|
|
}
|
|
|
|
/* Send the packet! */
|
|
|
|
if (DeliverDdp(sourceSocket, destination, DdpProtocolZip,
|
|
datagram, ZipOneZoneQueryDdpSize, Empty,
|
|
Empty, 0) isnt ATnoError)
|
|
ErrorLog("ZipQueryTimerExpired", ISevError, __LINE__,
|
|
routingTableEntry->port,
|
|
IErrFullZipBadQuerySend, IMsgFullZipBadQuerySend,
|
|
Insert0());
|
|
break;
|
|
}
|
|
|
|
/* On to next routing table entry. */
|
|
|
|
TakeLock(RoutingLock);
|
|
nextRoutingTableEntry = Link(routingTableEntry->next);
|
|
ReleaseLock(RoutingLock);
|
|
UnlinkRoutingTableEntry(routingTableEntry);
|
|
routingTableEntry = nextRoutingTableEntry;
|
|
|
|
} /* Loop through the routing table... */
|
|
|
|
} /* Loop through all hash buckets. */
|
|
|
|
/* Re-arm ZIP query timer... */
|
|
|
|
StartTimer(ZipQueryTimerExpired, ZipQueryTimerSeconds, 0, empty);
|
|
|
|
/* We've done the deed... */
|
|
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
|
|
} /* ZipQueryTimerExpired */
|
|
|
|
ExternForVisibleFunction Boolean GetZoneListFor(int port)
|
|
{
|
|
/* Similar to RTMP finding out the network number attached to port, our
|
|
task is to find out the zone list of the network attached to a
|
|
particular port. We too don't want to mess up a working AppleTalk
|
|
internet. So, spend a little while doing zone queries to see if the
|
|
network already has a zone list -- if we find one, use it; else, we
|
|
had better be a seed router. */
|
|
|
|
BufferDescriptor datagram;
|
|
int numberOfRequests = 0;
|
|
RoutingTableEntry routingTableEntry;
|
|
AppleTalkAddress source, destination;
|
|
PortHandlers portHandlers;
|
|
char far *multicastAddress;
|
|
|
|
/* Set source and destination. */
|
|
|
|
source.networkNumber = PortDescriptor(port)->aRouter.networkNumber;
|
|
source.nodeNumber = PortDescriptor(port)->aRouter.nodeNumber;
|
|
source.socketNumber = ZonesInformationSocket;
|
|
destination.networkNumber = CableWideBroadcastNetworkNumber;
|
|
destination.nodeNumber = AppleTalkBroadcastNodeNumber;
|
|
destination.socketNumber = ZonesInformationSocket;
|
|
|
|
if ((routingTableEntry =
|
|
FindInRoutingTable(PortDescriptor(port)->thisCableRange.
|
|
firstNetworkNumber)) is empty)
|
|
{
|
|
ErrorLog("GetZoneListFor", ISevError, __LINE__, port,
|
|
IErrFullZipRoutingTablesBad, IMsgFullZipRoutingTablesBad,
|
|
Insert0());
|
|
return(False);
|
|
}
|
|
|
|
/* Okay, blast a few of these guys out to see if any other bridge on the
|
|
network knows our zone name. */
|
|
|
|
while (not routingTableEntry->zonesValid and
|
|
numberOfRequests < NumberOfZipQueries)
|
|
{
|
|
/* Get a buffer descriptor for the query. */
|
|
|
|
if ((datagram = NewBufferDescriptor(ZipOneZoneQueryDdpSize)) is Empty)
|
|
{
|
|
ErrorLog("GetZoneListFor", ISevError, __LINE__, port,
|
|
IErrFullZipOutOfMemory, IMsgFullZipOutOfMemory,
|
|
Insert0());
|
|
UnlinkRoutingTableEntry(routingTableEntry);
|
|
return(False);
|
|
}
|
|
|
|
/* Build a ZipQuery packet... */
|
|
|
|
datagram->data[ZipCommandOffset] = ZipQueryCommand;
|
|
datagram->data[ZipNetworkCountOffset] = 1;
|
|
TakeLock(RoutingLock);
|
|
datagram->data[ZipFirstNetworkOffset] =
|
|
(char)((PortDescriptor(port)->thisCableRange.firstNetworkNumber
|
|
>> 8) & 0xFF);
|
|
datagram->data[ZipFirstNetworkOffset + 1] =
|
|
(char)(PortDescriptor(port)->thisCableRange.firstNetworkNumber &
|
|
0xFF);
|
|
ReleaseLock(RoutingLock);
|
|
|
|
if (not TransmitDdp(port, source, destination, DdpProtocolZip,
|
|
datagram, ZipOneZoneQueryDdpSize,
|
|
0, Empty, Empty, Empty, 0))
|
|
{
|
|
UnlinkRoutingTableEntry(routingTableEntry);
|
|
ErrorLog("GetZoneListFor", ISevError, __LINE__, port,
|
|
IErrFullZipBadQuerySend, IMsgFullZipBadQuerySend,
|
|
Insert0());
|
|
return(False);
|
|
}
|
|
numberOfRequests += 1;
|
|
WaitFor(ZipQueryTimerInHundreths, &routingTableEntry->zonesValid);
|
|
}
|
|
|
|
/* If we got an answer, we're set... but, we're going to need to play
|
|
with the routing tables... */
|
|
|
|
DeferTimerChecking();
|
|
DeferIncomingPackets();
|
|
|
|
if (routingTableEntry->zonesValid)
|
|
{
|
|
/* The valid zone list is now in the routing table... move it to the
|
|
port descriptor. */
|
|
|
|
if ((PortDescriptor(port)->theseZones =
|
|
CopyZones(routingTableEntry->zones)) is empty)
|
|
{
|
|
UnlinkRoutingTableEntry(routingTableEntry);
|
|
ErrorLog("GetZoneListFor", ISevError, __LINE__, port,
|
|
IErrFullZipCouldNotCopy, IMsgFullZipCouldNotCopy,
|
|
Insert0());
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(False);
|
|
}
|
|
|
|
UnlinkRoutingTableEntry(routingTableEntry);
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
|
|
/* If we are an extended network, we should already have "thisZone" set
|
|
(due to GetNetInfo's when we allocated this node), if not, try it
|
|
again. Also, find out the true default zone. */
|
|
|
|
if (PortDescriptor(port)->extendedNetwork)
|
|
{
|
|
AppleTalkAddress address;
|
|
long socket;
|
|
|
|
/* The router's Zip packet handler doesn't want to be told about
|
|
zones (it thinks it knows), so it ignores NetInfoReplies, so
|
|
for these GetNetworkInfoForNodes, swich back to the non-router
|
|
Zip handler. */
|
|
|
|
address.networkNumber = PortDescriptor(port)->aRouter.networkNumber;
|
|
address.nodeNumber = PortDescriptor(port)->aRouter.nodeNumber;
|
|
address.socketNumber = ZonesInformationSocket;
|
|
if ((socket = MapAddressToSocket(port, address)) < 0 or
|
|
NewHandlerForSocket(socket, ZipPacketIn, (long)0,
|
|
False) isnt ATnoError)
|
|
{
|
|
ErrorLog("GetZoneListFor", ISevError, __LINE__, port,
|
|
IErrFullZipBadSocketOpen, IMsgFullZipBadSocketOpen,
|
|
Insert0());
|
|
return(False);
|
|
}
|
|
|
|
/* Now fetch the "thisZone" (if needed) and "defaultZone." */
|
|
|
|
if (not PortDescriptor(port)->thisZoneValid and
|
|
not GetNetworkInfoForNode(port, PortDescriptor(port)->aRouter,
|
|
False))
|
|
{
|
|
ErrorLog("GetZoneListFor", ISevError, __LINE__, port,
|
|
IErrFullZipNoThisZone, IMsgFullZipNoThisZone,
|
|
Insert0());
|
|
return(False);
|
|
}
|
|
if (not GetNetworkInfoForNode(port, PortDescriptor(port)->aRouter,
|
|
True))
|
|
{
|
|
ErrorLog("GetZoneListFor", ISevError, __LINE__, port,
|
|
IErrFullZipNoThisZone, IMsgFullZipNoThisZone,
|
|
Insert0());
|
|
return(False);
|
|
}
|
|
|
|
/* Okay, now switch back to the router's Zip handler. */
|
|
|
|
if (NewHandlerForSocket(socket, ZipPacketInRouter, (long)0,
|
|
False) isnt ATnoError)
|
|
{
|
|
ErrorLog("GetZoneListFor", ISevError, __LINE__, port,
|
|
IErrFullZipBadSocketOpen, IMsgFullZipBadSocketOpen,
|
|
Insert0());
|
|
return(False);
|
|
}
|
|
|
|
/* The default zone had better be on the zone list we've just
|
|
received. */
|
|
|
|
TakeLock(RoutingLock);
|
|
if (not ZoneInZones(PortDescriptor(port)->thisDefaultZone,
|
|
PortDescriptor(port)->theseZones) or
|
|
not ZoneInZones(PortDescriptor(port)->thisZone,
|
|
PortDescriptor(port)->theseZones))
|
|
{
|
|
ReleaseLock(RoutingLock);
|
|
ErrorLog("GetZoneListFor", ISevError, __LINE__, port,
|
|
IErrFullZipBadThisZone, IMsgFullZipBadThisZone,
|
|
Insert0());
|
|
return(False);
|
|
}
|
|
ReleaseLock(RoutingLock);
|
|
|
|
/* Okay, we're all set. */
|
|
|
|
return(True);
|
|
}
|
|
|
|
/* On a non-extended network, the one entry on the zone list should
|
|
also be "this zone". */
|
|
|
|
TakeLock(RoutingLock);
|
|
if (PortDescriptor(port)->theseZones isnt Empty)
|
|
{
|
|
strcpy(PortDescriptor(port)->thisZone,
|
|
PortDescriptor(port)->theseZones->zone->zone);
|
|
PortDescriptor(port)->thisZoneValid = True;
|
|
}
|
|
ReleaseLock(RoutingLock);
|
|
return(True);
|
|
}
|
|
|
|
UnlinkRoutingTableEntry(routingTableEntry);
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
|
|
/* Okay, we didn't get an answer, we had better be able to seed. There is
|
|
a vauge chance that we got "this zone" set when allocating our node
|
|
and whatever router told us that went down before we could ask for the
|
|
zone list -- so, deallocate the multicast address, if any first. */
|
|
|
|
portHandlers = &portSpecificInfo[PortDescriptor(port)->portType];
|
|
if (PortDescriptor(port)->extendedNetwork and
|
|
PortDescriptor(port)->thisZoneValid)
|
|
if (not FixedCompareCaseSensitive(PortDescriptor(port)->
|
|
zoneMulticastAddress,
|
|
portHandlers->hardwareAddressLength,
|
|
portHandlers->broadcastAddress,
|
|
portHandlers->hardwareAddressLength))
|
|
(*portHandlers->removeMulticastAddress)(port, 1,
|
|
PortDescriptor(port)->
|
|
zoneMulticastAddress);
|
|
|
|
/* Okay, now we had better know enough to seed! */
|
|
|
|
if (PortDescriptor(port)->initialZoneList is empty)
|
|
{
|
|
ErrorLog("GetZoneListFor", ISevError, __LINE__, port,
|
|
IErrFullZipNeedSeedInfo, IMsgFullZipNeedSeedInfo,
|
|
Insert0());
|
|
return(False);
|
|
}
|
|
|
|
if ((PortDescriptor(port)->theseZones =
|
|
ZoneListToZones(PortDescriptor(port)->initialZoneList)) is empty)
|
|
{
|
|
ErrorLog("GetZoneListFor", ISevError, __LINE__, port,
|
|
IErrFullZipCouldNotCopy, IMsgFullZipCouldNotCopy,
|
|
Insert0());
|
|
return(False);
|
|
}
|
|
if (PortDescriptor(port)->extendedNetwork)
|
|
{
|
|
/* Here we need to seed the default zone too. */
|
|
|
|
strcpy(PortDescriptor(port)->thisDefaultZone,
|
|
PortDescriptor(port)->initialDefaultZone);
|
|
PortDescriptor(port)->thisDefaultZoneValid = True;
|
|
if (PortDescriptor(port)->initialDesiredZone[0] isnt 0)
|
|
strcpy(PortDescriptor(port)->thisZone,
|
|
PortDescriptor(port)->initialDesiredZone);
|
|
else
|
|
strcpy(PortDescriptor(port)->thisZone,
|
|
PortDescriptor(port)->initialDefaultZone);
|
|
PortDescriptor(port)->thisZoneValid = True;
|
|
|
|
/* We should now set up the zone multicast address too... */
|
|
|
|
if ((multicastAddress =
|
|
MulticastAddressForZoneOnPort(port, PortDescriptor(port)->thisZone))
|
|
is empty)
|
|
return(False);
|
|
|
|
if (not FixedCompareCaseSensitive(multicastAddress,
|
|
portHandlers->hardwareAddressLength,
|
|
portHandlers->broadcastAddress,
|
|
portHandlers->hardwareAddressLength))
|
|
(*portHandlers->addMulticastAddress)(port, 1, multicastAddress);
|
|
MoveMem(PortDescriptor(port)->zoneMulticastAddress, multicastAddress,
|
|
portHandlers->hardwareAddressLength);
|
|
}
|
|
else
|
|
{
|
|
/* On non-extended networks, this zone should be the only one on the
|
|
zone list. */
|
|
|
|
TakeLock(RoutingLock);
|
|
if (PortDescriptor(port)->theseZones isnt Empty)
|
|
{
|
|
strcpy(PortDescriptor(port)->thisZone,
|
|
PortDescriptor(port)->theseZones->zone->zone);
|
|
PortDescriptor(port)->thisZoneValid = True;
|
|
}
|
|
ReleaseLock(RoutingLock);
|
|
}
|
|
|
|
/* All set! */
|
|
|
|
return(True);
|
|
|
|
} /* GetZoneListFor */
|
|
|
|
char far * far MulticastAddressForZoneOnPort(int port,
|
|
char far *zone)
|
|
{
|
|
short unsigned checksum;
|
|
StaticForSmallStack char upcasedZone[MaximumZoneLength + 1];
|
|
int index;
|
|
static struct buffDesc descriptor;
|
|
|
|
/* Upcase and checksum the passed zone name. */
|
|
|
|
for (index = 0; zone[index] isnt 0; index += 1)
|
|
if (islower(zone[index]))
|
|
upcasedZone[index] = (char)(toupper(zone[index]));
|
|
else
|
|
upcasedZone[index] = zone[index];
|
|
|
|
descriptor.outBoardDataValid = True;
|
|
descriptor.outBoardBuffer = upcasedZone;
|
|
descriptor.outBoardData = upcasedZone;
|
|
descriptor.outBoardAllocatedSize = index;
|
|
descriptor.outBoardSize = index;
|
|
checksum = DdpChecksumBufferChain(&descriptor, index, (long)0);
|
|
|
|
/* Caclulate the the zone multicast address based on the port type. */
|
|
|
|
switch (PortDescriptor(port)->portType)
|
|
{
|
|
case EthernetNetwork:
|
|
case FddiNetwork:
|
|
return(ethernetZoneMutlicastAddrs[checksum %
|
|
NumberOfEnetZoneMulticastAddrs]);
|
|
|
|
case TokenRingNetwork:
|
|
return(tokenRingZoneMutlicastAddrs[checksum %
|
|
NumberOfTRingZoneMulticastAddrs]);
|
|
|
|
default:
|
|
ErrorLog("MulticastAddressForZoneOnPort", ISevError, __LINE__, port,
|
|
IErrFullZipBadPortType, IMsgFullZipBadPortType,
|
|
Insert0());
|
|
return(empty);
|
|
}
|
|
|
|
} /* MulticastAddressForZoneOnPort */
|
|
|
|
#if Iam a Primos
|
|
void DumpZoneTable(void)
|
|
{
|
|
int index;
|
|
UniqueZone uniqueZone;
|
|
|
|
printf("\n*** Start ZoneTable dump ***\n");
|
|
for (index = 0; index < ZoneTableHashBuckets; index += 1)
|
|
{
|
|
if (zoneTable[index] is Empty)
|
|
continue;
|
|
printf("zoneTable[%d] =\n", index);
|
|
for (uniqueZone = zoneTable[index];
|
|
uniqueZone isnt Empty;
|
|
uniqueZone = uniqueZone->next)
|
|
printf(" Zone = \"%s\"; ref = %d; hash = %d; Uid = %d.\n",
|
|
uniqueZone->zone, uniqueZone->refCount, uniqueZone->zoneHash,
|
|
uniqueZone->uniqueId);
|
|
}
|
|
printf("*** End ZoneTable dump ***\n");
|
|
} /* DumpZoneTable */
|
|
#endif
|
|
|
|
#endif
|