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.
1957 lines
67 KiB
1957 lines
67 KiB
/* Nbp.c, /appletalk/ins, Garth Conboy, 11/01/88 */
|
|
/* Copyright (c) 1988 by Pacer Software Inc., La Jolla, CA */
|
|
|
|
/* GC - Initial coding.
|
|
GC - (12/08/89): AppleTalk phase II comes to town.
|
|
GC - (02/04/90): Added router support; name changed from "nbpStub.c"
|
|
to "nbp.c".
|
|
GC - (08/18/90): New error logging mechanism.
|
|
GC - (03/30/92): Updated for BufferDescritpors.
|
|
GC - (04/05/92): Made the Encode and Decode tuple routines externally
|
|
visible for use by Arap.
|
|
GC - (06/16/92): Corrected WildMatch to not allow "abcd=cdef" (assume
|
|
"=" is approx equal) to match "abcdef". Fix from
|
|
ASCOM/Timeplex.
|
|
GC - (06/27/92): All buffers coming from user space are now "opaque," they
|
|
may or may not be "char *." We now use the new routines
|
|
MoveFromOpaque() and MoveToOpaque() to access these
|
|
"buffer"s rather than MemMove().
|
|
GC - (07/24/92): Well, on second thought, the "object, type, and zone"
|
|
arguments to NbpAction and NbpRemove can stay "char *".
|
|
Copies can be made by our callers if needed; buffers
|
|
being written into user space (e.g. tuple buffers)
|
|
remain "opaque".
|
|
|
|
*** Make the PVCS source control system happy:
|
|
$Header$
|
|
$Log$
|
|
***
|
|
|
|
NBP handling for a node (routing and non-routing).
|
|
|
|
*/
|
|
|
|
#define IncludeNbpErrors 1
|
|
|
|
#include "atalk.h"
|
|
|
|
ExternForVisibleFunction void DoNameLookup(int port,
|
|
NbpTuple far *nbpTuple,
|
|
int nbpId,
|
|
long whatSocket);
|
|
|
|
ExternForVisibleFunction Boolean WildMatch(char far *wildString,
|
|
char far *masterString);
|
|
|
|
ExternForVisibleFunction Boolean SendNbpRequest(PendingName pendingName);
|
|
|
|
#if Iam an AppleTalkRouter
|
|
ExternForVisibleFunction void SendLookupDatagram(int port,
|
|
char far *zone,
|
|
BufferDescriptor
|
|
lookupDatagram,
|
|
int length);
|
|
#endif
|
|
|
|
ExternForVisibleFunction TimerHandler NbpTimerExpired;
|
|
|
|
/* An item on the pending names list can be removed either due to an incoming
|
|
packet OR a timer going off. When a timer goes off the handler is passed
|
|
the address of the pending name structure. It is not sufficient to just
|
|
walk the list looking for the given address; a packet could have come in
|
|
just before the timer handler got around to defering incoming packets, and
|
|
that packet could have removed to item from the pending name list, and if
|
|
we're very unlucky, the address could have been reused, and we could get
|
|
very confused. So, we identify each member of the pending name list with
|
|
both its address as well as a 32 bit ID. */
|
|
|
|
typedef struct {long id;
|
|
long socket;
|
|
PendingName pendingName;
|
|
} AdditionalData;
|
|
static long nextId = 0;
|
|
|
|
long far NbpPacketIn(AppleTalkErrorCode errorCode,
|
|
long unsigned userData,
|
|
int port,
|
|
AppleTalkAddress source,
|
|
long destinationSocket,
|
|
int protocolType,
|
|
char far *datagram,
|
|
int datagramLength,
|
|
AppleTalkAddress actualDestination)
|
|
{
|
|
StaticForSmallStack char tupleBuffer[MaximumNbpTupleLength];
|
|
StaticForSmallStack NbpTuple nbpTuple, usedTuple;
|
|
StaticForSmallStack PendingName pendingName, previousPendingName;
|
|
StaticForSmallStack long nextTupleOffset;
|
|
StaticForSmallStack long nextUsedTupleOffset;
|
|
#if Iam an AppleTalkRouter
|
|
StaticForSmallStack BufferDescriptor lookupDatagram, copy;
|
|
#endif
|
|
short nbpCommand;
|
|
short tupleCount;
|
|
short nbpId;
|
|
OpenSocket openSocket;
|
|
int currentTupleCount, currentUsedTupleCount;
|
|
Boolean repeatTuple, foundIt;
|
|
NbpCompletionHandler *completionRoutine;
|
|
void far *opaqueBuffer;
|
|
AppleTalkAddress tempAddress;
|
|
int totalTuples;
|
|
short length;
|
|
long onWhosBehalf;
|
|
#if Iam an AppleTalkRouter
|
|
int index;
|
|
StaticForSmallStack RoutingTableEntry routingTableEntry;
|
|
StaticForSmallStack AppleTalkAddress destination;
|
|
#endif
|
|
|
|
/* "Use" unneeded actual parameters. */
|
|
|
|
source, datagramLength, actualDestination;
|
|
|
|
/* Only play if we've been asked nicely! */
|
|
|
|
if (errorCode is ATsocketClosed)
|
|
return((long)True);
|
|
else if (errorCode isnt ATnoError)
|
|
{
|
|
ErrorLog("NbpPacketIn", ISevError, __LINE__, port,
|
|
IErrNbpBadIncomingCode, IMsgNbpBadIncomingCode,
|
|
Insert0());
|
|
return((long)True);
|
|
}
|
|
|
|
/* We only want NBP packets! */
|
|
|
|
if (protocolType isnt DdpProtocolNbp)
|
|
return((long)True); /* Why are these guys talking to us??? */
|
|
|
|
DeferTimerChecking();
|
|
DeferIncomingPackets();
|
|
|
|
/* Get static NBP information out of the packet, then switch on command. */
|
|
|
|
nbpCommand = (short)((datagram[NbpControlOffset] >> 4) & 0xF);
|
|
tupleCount = (short)(datagram[NbpControlOffset] & 0xF);
|
|
nbpId = (unsigned char)datagram[NbpIdOffset];
|
|
|
|
switch(nbpCommand)
|
|
{
|
|
case NbpBroadcastRequest:
|
|
case NbpForwardRequest:
|
|
|
|
#if IamNot an AppleTalkRouter
|
|
/* We're just a lowly node (not a router), so ignore these guys. */
|
|
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
#else
|
|
|
|
/* Only one tuple for these guys. */
|
|
|
|
if (tupleCount isnt 1)
|
|
{
|
|
ErrorLog("NbpPacketIn", ISevWarning, __LINE__, port,
|
|
IErrNbpTupleCountBadReq, IMsgNbpTupleCountBadReq,
|
|
Insert0());
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
|
|
/* Decode our tuple. */
|
|
|
|
if (DecodeNbpTuple(datagram, NbpFirstTupleOffset, False,
|
|
&nbpTuple) is 0)
|
|
{
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True); /* Bad format in tuple... */
|
|
}
|
|
|
|
/* On extended networks, no '*' zone is allowed; non non-extended
|
|
change '*' to the node's zone. */
|
|
|
|
if (portDescriptors[port].extendedNetwork)
|
|
{
|
|
if (nbpTuple.zone[0] is 0 or
|
|
(nbpTuple.zone[0] is '*' and
|
|
nbpTuple.zone[1] is 0))
|
|
{
|
|
ErrorLog("NbpPacketIn", ISevWarning, __LINE__, port,
|
|
IErrNbpBadZoneReq, IMsgNbpBadZoneReq,
|
|
Insert0());
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
}
|
|
else if (nbpTuple.zone[0] is 0 or
|
|
(nbpTuple.zone[0] is '*' and
|
|
nbpTuple.zone[1] is 0))
|
|
{
|
|
if (portDescriptors[port].thisCableRange.firstNetworkNumber isnt
|
|
source.networkNumber)
|
|
{
|
|
ErrorLog("NbpPacketIn", ISevWarning, __LINE__, port,
|
|
IErrNbpBadLTZone, IMsgNbpBadLTZone,
|
|
Insert0());
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
if (not portDescriptors[port].thisZoneValid)
|
|
{
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
strcpy(nbpTuple.zone, portDescriptors[port].thisZone);
|
|
}
|
|
|
|
/* Okay, build up a forward-request or a looklup DDP packet.
|
|
Allocate a buffer descriptor for the beast. */
|
|
|
|
if ((lookupDatagram = NewBufferDescriptor(NbpFirstTupleOffset +
|
|
MaximumNbpTupleLength)) is
|
|
Empty)
|
|
{
|
|
ErrorLog("NbpPacketIn", ISevError, __LINE__, port,
|
|
IErrNbpOutOfMemory, IMsgNbpOutOfMemory,
|
|
Insert0());
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
lookupDatagram->data[NbpIdOffset] = (unsigned char)nbpId;
|
|
length = EncodeNbpTuple(&nbpTuple, lookupDatagram->data +
|
|
NbpFirstTupleOffset);
|
|
length += NbpFirstTupleOffset;
|
|
|
|
/* A forward request should be turned into a broadcast on the
|
|
directly connected network. */
|
|
|
|
if (nbpCommand is NbpForwardRequest)
|
|
{
|
|
lookupDatagram->data[NbpControlOffset] =
|
|
(unsigned char)((NbpLookup << 4) + 1);
|
|
|
|
SendLookupDatagram(port, nbpTuple.zone,
|
|
lookupDatagram, length);
|
|
|
|
/* That's all for forward requests; SendLookupDatagram will cause
|
|
the buffer chain to be freed. */
|
|
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
|
|
/* Okay, now for broadcast requests... walk through the routing
|
|
tables sending either a forward request or a lookup (broadcast)
|
|
to each network that contains the specified zone. */
|
|
|
|
for (index = 0; index < NumberOfRtmpHashBuckets; index += 1)
|
|
for (routingTableEntry = routingTable[index];
|
|
routingTableEntry isnt empty;
|
|
routingTableEntry = routingTableEntry->next)
|
|
{
|
|
/* If the network is zero hops away (it's directly connected);
|
|
use the zone list in the portDescriptors rather than the
|
|
routing table -- the routing table may not yet be filled
|
|
with a zone list (due to the normal ZipQuery mechanism). */
|
|
|
|
if (not routingTableEntry->zoneListValid)
|
|
{
|
|
if (routingTableEntry->numberOfHops isnt 0)
|
|
continue;
|
|
if (not ZoneOnList(nbpTuple.zone,
|
|
portDescriptors[routingTableEntry->
|
|
port].thisZoneList))
|
|
continue;
|
|
}
|
|
else if (not ZoneOnList(nbpTuple.zone,
|
|
routingTableEntry->zoneList))
|
|
continue;
|
|
|
|
/* Copy the lookup datagram so we can alter it, and allow it to
|
|
asynchronously freed! */
|
|
|
|
if ((copy = CopyBufferChain(lookupDatagram)) is Empty)
|
|
{
|
|
ErrorLog("NbpPacketIn", ISevError, __LINE__, port,
|
|
IErrNbpOutOfMemory, IMsgNbpOutOfMemory,
|
|
Insert0());
|
|
FreeBufferChain(lookupDatagram);
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
|
|
/* If not a local network, just send a forward request. */
|
|
|
|
if (routingTableEntry->numberOfHops isnt 0)
|
|
{
|
|
copy->data[NbpControlOffset] =
|
|
(unsigned char)((NbpForwardRequest << 4) + 1);
|
|
destination.networkNumber =
|
|
routingTableEntry->networkRange.firstNetworkNumber;
|
|
destination.nodeNumber = AnyRouterNodeNumber;
|
|
destination.socketNumber = NamesInformationSocket;
|
|
if (DeliverDdp(destinationSocket, destination,
|
|
DdpProtocolNbp, copy, length,
|
|
Empty, Empty, 0) isnt ATnoError)
|
|
ErrorLog("NbpPacketIn", ISevError, __LINE__, port,
|
|
IErrNbpBadFwdReqSend, IMsgNbpBadFwdReqSend,
|
|
Insert0());
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise, send a lookup. */
|
|
|
|
copy->data[NbpControlOffset] =
|
|
(unsigned char)((NbpLookup << 4) + 1);
|
|
|
|
SendLookupDatagram(routingTableEntry->port,
|
|
nbpTuple.zone,
|
|
copy, length);
|
|
}
|
|
}
|
|
|
|
/* All set! We only used the copys of the lookup datagram when
|
|
we're taking this exit, so free the original. */
|
|
|
|
FreeBufferChain(lookupDatagram);
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
#endif
|
|
|
|
case NbpLookup:
|
|
if (tupleCount isnt 1)
|
|
{
|
|
ErrorLog("NbpPacketIn", ISevWarning, __LINE__, port,
|
|
IErrNbpTupleCountBadLookup, IMsgNbpTupleCountBadLookup,
|
|
Insert0());
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
if (DecodeNbpTuple(datagram, NbpFirstTupleOffset, False,
|
|
&nbpTuple) is 0)
|
|
{
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True); /* Bad format in tuple... */
|
|
}
|
|
DoNameLookup(port, &nbpTuple, nbpId, destinationSocket);
|
|
break;
|
|
case NbpLookupReply:
|
|
/* This had better be a response to a previous LookUp... get an
|
|
open socket on our node. */
|
|
|
|
if ((openSocket = MapSocketToOpenSocket(destinationSocket)) is empty)
|
|
{
|
|
ErrorLog("NbpPacketIn", ISevWarning, __LINE__, port,
|
|
IErrNbpCouldntMapSocket, IMsgNbpCouldntMapSocket,
|
|
Insert0());
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
|
|
/* Okay, look on our pending names lists for all sockets within our
|
|
node for the nbpId that we're handling the response for. */
|
|
|
|
foundIt = False;
|
|
for (openSocket = openSocket->activeNode->openSockets ;
|
|
openSocket isnt empty and not foundIt;
|
|
openSocket = openSocket->next)
|
|
{
|
|
for (pendingName = openSocket->pendingNames,
|
|
previousPendingName = empty;
|
|
pendingName isnt empty and not foundIt;
|
|
previousPendingName = pendingName,
|
|
pendingName = pendingName->next)
|
|
if (pendingName->nbpId is nbpId)
|
|
{
|
|
foundIt = True;
|
|
break;
|
|
}
|
|
if (foundIt)
|
|
break; /* We need pendingName and openSocket to be valid! */
|
|
}
|
|
if (not foundIt)
|
|
{
|
|
#if Verbose & 0
|
|
/* This will happen when a lookup completes with "buffer full"
|
|
and more replies are still coming in... */
|
|
|
|
ErrorLog("NbpPacketIn", ISevVerbose, __LINE__, port,
|
|
IErrNbpDeadReply, IMsgNbpDeadReply,
|
|
Insert0());
|
|
#endif
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
|
|
/* Okay, handle the incoming reply based on why we were doing the
|
|
request: */
|
|
|
|
if (pendingName->whyPending is ForRegister or
|
|
pendingName->whyPending is ForConfirm)
|
|
{
|
|
if (tupleCount is 0)
|
|
{
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
else if (tupleCount isnt 1)
|
|
ErrorLog("NbpPacketIn", ISevWarning, __LINE__, port,
|
|
IErrNbpTupleCountBadReply, IMsgNbpTupleCountBadReply,
|
|
Insert0());
|
|
if (DecodeNbpTuple(datagram, NbpFirstTupleOffset, False,
|
|
&nbpTuple) is 0)
|
|
{
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True); /* Bad format reply, ignore it */
|
|
}
|
|
|
|
/* Does the reply match the one we're trying to register? */
|
|
|
|
if (not CompareCaseInsensitive(pendingName->object,
|
|
nbpTuple.object) or
|
|
not CompareCaseInsensitive(pendingName->type,
|
|
nbpTuple.type))
|
|
{
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True); /* No match, who cares... */
|
|
}
|
|
|
|
completionRoutine = pendingName->completionRoutine;
|
|
userData = pendingName->userData;
|
|
onWhosBehalf = pendingName->socket;
|
|
|
|
/* For confirm, check that the network and node numbers match,
|
|
if so, check socket. */
|
|
|
|
if (pendingName->whyPending is ForConfirm)
|
|
{
|
|
if (pendingName->confirming.networkNumber isnt
|
|
nbpTuple.address.networkNumber or
|
|
pendingName->confirming.nodeNumber isnt
|
|
nbpTuple.address.nodeNumber)
|
|
{
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True); /* Not even close... */
|
|
}
|
|
|
|
/* Okay, we have a match, remove the pending name from our
|
|
pending list... */
|
|
|
|
if (previousPendingName is empty)
|
|
openSocket->pendingNames = pendingName->next;
|
|
else
|
|
previousPendingName->next = pendingName->next;
|
|
CancelTimer(pendingName->timerId);
|
|
|
|
/* Return the correct error code to the completion routine. */
|
|
|
|
if (pendingName->confirming.socketNumber is
|
|
nbpTuple.address.socketNumber)
|
|
{
|
|
Free(pendingName);
|
|
tempAddress = nbpTuple.address;
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(ATnoError, userData, ForConfirm,
|
|
onWhosBehalf, nbpId, tempAddress);
|
|
return((long)True);
|
|
}
|
|
else
|
|
{
|
|
Free(pendingName);
|
|
tempAddress = nbpTuple.address;
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(ATnbpConfirmedWithNewSocket, userData,
|
|
ForConfirm, onWhosBehalf, nbpId,
|
|
tempAddress);
|
|
return((long)True);
|
|
}
|
|
}
|
|
|
|
/* Okay, we're a register and somebody our there has our name!
|
|
Remove the name from our pending list. */
|
|
|
|
if (previousPendingName is empty)
|
|
openSocket->pendingNames = pendingName->next;
|
|
else
|
|
previousPendingName->next = pendingName->next;
|
|
CancelTimer(pendingName->timerId);
|
|
Free(pendingName);
|
|
|
|
/* Call the completion routine, informing it of our bad luck. */
|
|
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(ATnbpNameInUse, userData, ForRegister,
|
|
onWhosBehalf, nbpId);
|
|
return((long)True);
|
|
}
|
|
else if (pendingName->whyPending is ForLookup)
|
|
{
|
|
if (tupleCount is 0)
|
|
{
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True); /* No added information... */
|
|
}
|
|
|
|
/* Process each tuple in our lookup reply packet... */
|
|
|
|
nextTupleOffset = NbpFirstTupleOffset;
|
|
for (currentTupleCount = 1;
|
|
currentTupleCount <= tupleCount;
|
|
currentTupleCount += 1)
|
|
{
|
|
if ((nextTupleOffset = DecodeNbpTuple(datagram, nextTupleOffset,
|
|
False, &nbpTuple)) is 0)
|
|
{
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True); /* Bad format tuple... ignore the rest. */
|
|
}
|
|
|
|
/* Now we have to walk through our list of already stored tuples
|
|
and see if we're a repeat... */
|
|
|
|
nextUsedTupleOffset = 0;
|
|
repeatTuple = False;
|
|
for (currentUsedTupleCount = 1;
|
|
not repeatTuple and
|
|
currentUsedTupleCount <= pendingName->totalTuples;
|
|
currentUsedTupleCount += 1)
|
|
{
|
|
if ((nextUsedTupleOffset =
|
|
DecodeNbpTuple(pendingName->opaqueBuffer,
|
|
nextUsedTupleOffset, True,
|
|
&usedTuple)) is 0)
|
|
{
|
|
ErrorLog("NbpPacketIn", ISevError, __LINE__, port,
|
|
IErrNbpBadTupleStored, IMsgNbpBadTupleStored,
|
|
Insert0());
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
if (nbpTuple.address.networkNumber is
|
|
usedTuple.address.networkNumber and
|
|
nbpTuple.address.nodeNumber is
|
|
usedTuple.address.nodeNumber and
|
|
nbpTuple.address.socketNumber is
|
|
usedTuple.address.socketNumber and
|
|
nbpTuple.enumerator is usedTuple.enumerator)
|
|
repeatTuple = True;
|
|
|
|
} /* Process all previously stored tuples... */
|
|
|
|
/* If we're a repeat, just get the next one off the wire... */
|
|
|
|
if (repeatTuple)
|
|
continue;
|
|
if (nextUsedTupleOffset isnt pendingName->nextTupleOffset)
|
|
{
|
|
ErrorLog("NbpPacketIn", ISevError, __LINE__, port,
|
|
IErrNbpTuplesMunged, IMsgNbpTuplesMunged,
|
|
Insert0());
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
|
|
/* Grab some information out of the pending name structure that
|
|
we may need to call the completion routine (if needed). */
|
|
|
|
completionRoutine = pendingName->completionRoutine;
|
|
userData = pendingName->userData;
|
|
onWhosBehalf = pendingName->socket;
|
|
opaqueBuffer = pendingName->opaqueBuffer;
|
|
totalTuples = pendingName->totalTuples;
|
|
|
|
/* Encode the new tuple into a scratch buffer. Is there room in
|
|
buffer for this new tuple? */
|
|
|
|
length = EncodeNbpTuple(&nbpTuple, tupleBuffer);
|
|
if (pendingName->nextTupleOffset + length >
|
|
pendingName->bufferSize)
|
|
{
|
|
/* We can't fit the new one... remove us from the pending
|
|
list, return what we can, report the error. */
|
|
|
|
if (previousPendingName is empty)
|
|
openSocket->pendingNames = pendingName->next;
|
|
else
|
|
previousPendingName->next = pendingName->next;
|
|
CancelTimer(pendingName->timerId);
|
|
Free(pendingName);
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(ATnbpBufferNotBigEnough, userData,
|
|
ForLookup, onWhosBehalf, nbpId,
|
|
opaqueBuffer, totalTuples);
|
|
return((long)True);
|
|
}
|
|
|
|
/* Okay, we're pretty well set. Move the new tuple into the
|
|
user's buffer... */
|
|
|
|
MoveToOpaque(pendingName->opaqueBuffer,
|
|
pendingName->nextTupleOffset, tupleBuffer, length);
|
|
pendingName->nextTupleOffset += length;
|
|
totalTuples = (pendingName->totalTuples += 1);
|
|
|
|
/* Lastly, have we gotten as many tuples as the user wanted? */
|
|
|
|
if (pendingName->totalTuples is pendingName->maximumTuples)
|
|
{
|
|
/* Remove from the pending name list and call the completion
|
|
routine... */
|
|
|
|
if (previousPendingName is empty)
|
|
openSocket->pendingNames = pendingName->next;
|
|
else
|
|
previousPendingName->next = pendingName->next;
|
|
CancelTimer(pendingName->timerId);
|
|
Free(pendingName);
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(ATnoError, userData, ForLookup,
|
|
onWhosBehalf, nbpId,
|
|
opaqueBuffer, totalTuples);
|
|
return((long)True);
|
|
}
|
|
|
|
} /* Process all incoming tuples.. */
|
|
}
|
|
else
|
|
ErrorLog("NbpPacketIn", ISevError, __LINE__, port,
|
|
IErrNbpBadPendingFlag, IMsgNbpBadPendingFlag,
|
|
Insert0());
|
|
|
|
break;
|
|
default:
|
|
ErrorLog("NbpPacketIn", ISevWarning, __LINE__, port,
|
|
IErrNbpBadCommandType, IMsgNbpBadCommandType,
|
|
Insert0());
|
|
break;
|
|
}
|
|
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
|
|
} /* NbpPacketIn */
|
|
|
|
#if (Iam a WindowsNT)
|
|
AppleTalkErrorCode far _cdecl
|
|
#else
|
|
AppleTalkErrorCode far
|
|
#endif
|
|
NbpAction(WhyPending reason,
|
|
long socket,
|
|
char far *object,
|
|
char far *type,
|
|
char far *zone,
|
|
int nbpId,
|
|
int broadcastInterval,
|
|
int numberOfBroadcasts,
|
|
NbpCompletionHandler *completionRoutine,
|
|
long unsigned userData,
|
|
...)
|
|
{
|
|
/* Perform an NBP name registration, lookup or confirmation for specified
|
|
AppleTalk address. We return an integer:
|
|
|
|
< 0 - Error code (from AppleDcls.h).
|
|
0 - ATnoError
|
|
|
|
The error code indicates whether the operation got STARTED succesfully, if
|
|
it is less that zero, the completion routine will never be called.
|
|
|
|
BEFORE this routine is called, GetNextNbpIdForNode MUST be called to
|
|
identify the next NbpAction (get the next nbpID). This will set the
|
|
NBP ID that is passed to both NbpAction and NbpAction's completion
|
|
routine. Note that the completion may occure BEFORE NbpAction returns.
|
|
|
|
Both broadcastInterval and numberOfBroadcasts may be specified as zero,
|
|
in which case default values will be used.
|
|
|
|
For "register" requests, "zone" must be "empty" or "*".
|
|
|
|
No meta-characters ("=") are allowed in the "object" or "type" fields
|
|
for register or confirm actions.
|
|
|
|
If "reason" is ForConfirm then a single "additionalArgument" should
|
|
be passed: "confirming" argument must be specified as the expected
|
|
AppleTalk address of the specified object.
|
|
|
|
If "reason" is ForLookup then three "additionalArgument"s should be
|
|
passed: "opaqueBuffer" is the address of opaque buffer for the
|
|
reception of matching tuples, "bufferSize" is the length, in bytes, of
|
|
"buffer", "maximumTuples" is the maximum number of tuples to return (zero
|
|
means no maximum, just try to fill the buffer).
|
|
|
|
At the end of the operation we will call the specified completion routine:
|
|
|
|
(*completionRoutine)(errorCode, userData, reason, onWhosBehalf, nbpId,
|
|
...)
|
|
|
|
The common arguments are:
|
|
|
|
errorCode - Integer; specifies the completion status of the
|
|
operation (i.e. register-okay, name-in-use, etc.).
|
|
userData - Long unsigned; as passed to this routine.
|
|
reason - Integer; the type of operation operation that has
|
|
completed (ForLookup, ForRegister, or ForConfirm).
|
|
onWhosBehalf - socket; the socket that was performing the
|
|
lookup/register/confirm.
|
|
nbpId - Integer; the value that was returned from the
|
|
GetNextNbpIdForNode call preceeding this call to
|
|
NbpAction (and passed to NbpAction) to indicate what
|
|
action is complete. Note that nbpId's are only
|
|
unique within a single node; so, onWhosBehalf will
|
|
be needed for true uniqueness.
|
|
|
|
The additional arguments depend of the "reason":
|
|
|
|
ForRegister:
|
|
|
|
none.
|
|
|
|
ForConfirm:
|
|
|
|
confirmedAddress: AppleTalk address that was confirmed; if the
|
|
error code is no-error, this will be the same
|
|
as the "cofirming" address passed to NbpAction,
|
|
if the error code is new-socket, this will be
|
|
the same as the "confiming" address except with
|
|
a new socket.
|
|
|
|
ForLookup:
|
|
|
|
opaqueBuffer: The same address passed to NbpAction, this is the
|
|
buffer full of matching tuples.
|
|
tupleCount: Integer, the number of matching tuples that can
|
|
be found in buffer.
|
|
|
|
*/
|
|
|
|
void far *opaqueBuffer;
|
|
int bufferSize;
|
|
int maximumTuples;
|
|
StaticForSmallStack NbpTuple tuple;
|
|
StaticForSmallStack AppleTalkAddress replyTo, confirming;
|
|
OpenSocket openSocket;
|
|
PendingName pendingList;
|
|
RegisteredName nameList;
|
|
short enumerator;
|
|
short length;
|
|
Boolean foundMatch;
|
|
AdditionalData additionalData;
|
|
va_list ap;
|
|
int port;
|
|
|
|
/* Grab the additional arguments, as needed: */
|
|
|
|
va_start(ap, userData);
|
|
if (reason is ForConfirm)
|
|
confirming = va_arg(ap, AppleTalkAddress);
|
|
else if (reason is ForLookup)
|
|
{
|
|
opaqueBuffer = va_arg(ap, void far *);
|
|
bufferSize = va_arg(ap, int);
|
|
maximumTuples = va_arg(ap, int);
|
|
if (maximumTuples < 0 or bufferSize < 0)
|
|
return(ATnbpBadParameter);
|
|
}
|
|
va_end(ap);
|
|
|
|
/* A little error checking... */
|
|
|
|
if ((length = (short)strlen(object)) < 1 or
|
|
length > MaximumEntityFieldLength or
|
|
(length = (short)strlen(type)) < 1 or
|
|
length > MaximumEntityFieldLength)
|
|
return(ATnbpBadObjectOrTypeOrZone);
|
|
if (zone isnt empty and
|
|
((length = (short)strlen(zone)) < 1 or
|
|
length > MaximumEntityFieldLength))
|
|
return(ATnbpBadObjectOrTypeOrZone);
|
|
|
|
if (zone is empty)
|
|
zone = "*";
|
|
|
|
if (reason is ForRegister or reason is ForConfirm)
|
|
{
|
|
if (object[0] is '=' or type[0] is '=' or
|
|
strchr(object, NbpWildCharacter) isnt empty or
|
|
strchr(type, NbpWildCharacter) isnt empty)
|
|
return(ATnbpNoWildcardsAllowed);
|
|
if (reason is ForRegister)
|
|
if (zone[0] isnt '*' or zone[1] isnt 0)
|
|
return(ATnbpZoneNotAllowed);
|
|
}
|
|
|
|
/* Set timer values to defaults, if requested: */
|
|
|
|
if (broadcastInterval <= 0)
|
|
broadcastInterval = NbpBroadcastIntervalSeconds;
|
|
if (numberOfBroadcasts <= 0)
|
|
numberOfBroadcasts = NbpNumberOfBroadcasts;
|
|
|
|
/* We're going to the mucking with the PendingNames table, don't allow any
|
|
lookup-replies to bother us... */
|
|
|
|
DeferTimerChecking();
|
|
DeferIncomingPackets();
|
|
|
|
/* Validate the socket... */
|
|
|
|
if ((openSocket = MapSocketToOpenSocket(socket)) is empty)
|
|
{
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATsocketNotOpen);
|
|
}
|
|
port = openSocket->port;
|
|
|
|
/* For extended networks, set the zone name correctly. */
|
|
|
|
if (zone[0] is '*' and zone[1] is 0 and
|
|
portDescriptors[port].extendedNetwork and
|
|
portDescriptors[port].thisZoneValid)
|
|
zone = portDescriptors[port].thisZone;
|
|
|
|
/* We want replies to go the NamesInformationSocket in the requesting node. */
|
|
|
|
if (MapSocketToAddress(socket, &replyTo) isnt ATnoError)
|
|
{
|
|
ErrorLog("NbpAction", ISevError, __LINE__, port,
|
|
IErrNbpCouldntFormAddress, IMsgNbpCouldntFormAddress,
|
|
Insert0());
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATinternalError);
|
|
}
|
|
replyTo.socketNumber = NamesInformationSocket;
|
|
|
|
/* For a register, we have to find an enumerator that is unique amoung all
|
|
registered and names pending for registration within the socket... */
|
|
|
|
if (reason is ForRegister)
|
|
for (enumerator = 0; enumerator <= 255; enumerator += 1)
|
|
{
|
|
foundMatch = False;
|
|
for (nameList = openSocket->registeredNames;
|
|
nameList isnt empty and not foundMatch;
|
|
nameList = nameList->next)
|
|
if (nameList->enumerator is enumerator)
|
|
{
|
|
foundMatch = True;
|
|
break;
|
|
}
|
|
if (foundMatch)
|
|
continue;
|
|
for (pendingList = openSocket->pendingNames;
|
|
pendingList isnt empty and not foundMatch;
|
|
pendingList = pendingList->next)
|
|
if (pendingList->whyPending is ForRegister and
|
|
pendingList->enumerator is enumerator)
|
|
{
|
|
foundMatch = True;
|
|
break;
|
|
}
|
|
if (not foundMatch)
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
foundMatch = False;
|
|
enumerator = 0;
|
|
}
|
|
|
|
/* Did we find one? */
|
|
|
|
if (foundMatch)
|
|
{
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnbpTooManyRegisteredNames);
|
|
}
|
|
|
|
/* Okay, all looks good, start building up the PendingName structure. */
|
|
|
|
if ((pendingList = (PendingName)Calloc(sizeof(*pendingList), 1)) is empty)
|
|
{
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
ErrorLog("NbpAction", ISevError, __LINE__, port,
|
|
IErrNbpOutOfMemory, IMsgNbpOutOfMemory,
|
|
Insert0());
|
|
return(AToutOfMemory);
|
|
}
|
|
|
|
pendingList->whyPending = reason;
|
|
pendingList->socket = socket;
|
|
strcpy(pendingList->object, object);
|
|
strcpy(pendingList->type, type);
|
|
strcpy(pendingList->zone, zone);
|
|
pendingList->enumerator = enumerator;
|
|
pendingList->broadcastInterval = (short)broadcastInterval;
|
|
pendingList->remainingBroadcasts = (short)(numberOfBroadcasts - 1);
|
|
if (reason is ForConfirm)
|
|
pendingList->confirming = confirming;
|
|
pendingList->nbpId = (short)nbpId;
|
|
pendingList->completionRoutine = completionRoutine;
|
|
pendingList->userData = userData;
|
|
if (reason is ForLookup)
|
|
{
|
|
pendingList->opaqueBuffer = opaqueBuffer;
|
|
pendingList->nextTupleOffset = 0;
|
|
pendingList->bufferSize = bufferSize;
|
|
pendingList->maximumTuples = (short)maximumTuples;
|
|
}
|
|
|
|
/* Hook it up to the pending names list... */
|
|
|
|
pendingList->next = openSocket->pendingNames;
|
|
openSocket->pendingNames = pendingList;
|
|
|
|
/* We're going to send a directed Lookup for confirms, or either a Broadcast-
|
|
request or a Lookup for registers or lookups depending if we know about
|
|
a router or not. We don't have to bother checking the RegisteredNames
|
|
list, for register, in our node because the broadcast will eventually get
|
|
to us and we'll handle it then! Request packet, with one tuple: */
|
|
|
|
if (reason is ForConfirm) /* Send to confirming node... */
|
|
pendingList->datagram[NbpControlOffset] = (NbpLookup << 4) + 1;
|
|
else
|
|
if (portDescriptors[port].seenRouterRecently)
|
|
pendingList->datagram[NbpControlOffset] = (NbpBroadcastRequest << 4) + 1;
|
|
else
|
|
pendingList->datagram[NbpControlOffset] = (NbpLookup << 4) + 1;
|
|
|
|
pendingList->datagram[NbpIdOffset] = (char)nbpId;
|
|
|
|
/* Okay, encode the tuple into the DDP packet. */
|
|
|
|
tuple.address = replyTo;
|
|
tuple.enumerator = enumerator;
|
|
strcpy(tuple.object, pendingList->object);
|
|
strcpy(tuple.type, pendingList->type);
|
|
strcpy(tuple.zone, pendingList->zone);
|
|
|
|
length = EncodeNbpTuple(&tuple, pendingList->datagram + NbpFirstTupleOffset);
|
|
length += NbpFirstTupleOffset; /* True DDP packet length */
|
|
pendingList->datagramLength = length;
|
|
|
|
/* Start the re-transmit timer... We'll need the address of the pending-name
|
|
structure when the timer expires... */
|
|
|
|
pendingList->id = additionalData.id = nextId++;
|
|
additionalData.socket = pendingList->socket;
|
|
additionalData.pendingName = pendingList;
|
|
pendingList->timerId = StartTimer(NbpTimerExpired, broadcastInterval,
|
|
sizeof(additionalData),
|
|
(char *)&additionalData);
|
|
|
|
/* Send the packet to the packet. */
|
|
|
|
if (not SendNbpRequest(pendingList))
|
|
ErrorLog("NbpAction", ISevError, __LINE__, port,
|
|
IErrNbpBadSend, IMsgNbpBadSend,
|
|
Insert0());
|
|
|
|
/* The deed is done... */
|
|
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* NbpAction */
|
|
|
|
AppleTalkErrorCode far NbpRemove(long socket,
|
|
char far *object,
|
|
char far *type,
|
|
char far *zone)
|
|
{
|
|
/* Remove a registered NBP name. "Zone" must be either empty or "*". */
|
|
|
|
OpenSocket openSocket;
|
|
int length;
|
|
StaticForSmallStack RegisteredName registeredName,
|
|
previousRegisteredName = empty;
|
|
|
|
/* A little error checking... */
|
|
|
|
if ((length = (short)strlen(object)) < 1 or
|
|
length > MaximumEntityFieldLength or
|
|
(length = (short)strlen(type)) < 1 or
|
|
length > MaximumEntityFieldLength)
|
|
return(ATnbpBadObjectOrTypeOrZone);
|
|
if (zone isnt empty and
|
|
((length = (short)strlen(zone)) < 1 or
|
|
length > MaximumEntityFieldLength))
|
|
return(ATnbpBadObjectOrTypeOrZone);
|
|
|
|
if (zone is empty)
|
|
zone = "*";
|
|
|
|
if (zone[0] isnt '*' or zone[1] isnt 0)
|
|
return(ATnbpBadObjectOrTypeOrZone);
|
|
if (object[0] is '=' or type[0] is '=' or
|
|
strchr(object, NbpWildCharacter) isnt empty or
|
|
strchr(type, NbpWildCharacter) isnt empty)
|
|
return(ATnbpNoWildcardsAllowed);
|
|
|
|
/* We're going to the mucking with the PendingNames table, don't allow any
|
|
lookup-replies bother us... */
|
|
|
|
DeferTimerChecking();
|
|
DeferIncomingPackets();
|
|
|
|
/* We need to be doing the action on behalf of an open socket... */
|
|
|
|
if ((openSocket = MapSocketToOpenSocket(socket)) is empty)
|
|
{
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATsocketNotOpen);
|
|
}
|
|
|
|
/* Okay, look in the socket's registered name list for the entity. */
|
|
|
|
for (registeredName = openSocket->registeredNames;
|
|
registeredName isnt empty;
|
|
previousRegisteredName = registeredName,
|
|
registeredName = registeredName->next)
|
|
if (CompareCaseInsensitive(object, registeredName->object) and
|
|
CompareCaseInsensitive(type, registeredName->type))
|
|
break;
|
|
if (registeredName is empty)
|
|
{
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnbpNameNotRegistered);
|
|
}
|
|
|
|
/* Remove from the list... */
|
|
|
|
if (previousRegisteredName is empty)
|
|
openSocket->registeredNames = registeredName->next;
|
|
else
|
|
previousRegisteredName->next = registeredName->next;
|
|
Free(registeredName);
|
|
|
|
/* All set! */
|
|
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* NbpRemove */
|
|
|
|
int far GetNextNbpIdForNode(long socket)
|
|
{
|
|
/* Set and return an available NbpId prepatory to a call to NbpAction. */
|
|
|
|
OpenSocket openSocket, socketList;
|
|
int nbpId;
|
|
PendingName pendingList;
|
|
Boolean foundMatch;
|
|
|
|
/* We're going to the mucking with the PendingNames table, don't allow any
|
|
lookup-replies bother us... */
|
|
|
|
DeferTimerChecking();
|
|
DeferIncomingPackets();
|
|
|
|
/* Validate the socket */
|
|
|
|
if ((openSocket = MapSocketToOpenSocket(socket)) is empty)
|
|
{
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATsocketNotOpen);
|
|
}
|
|
|
|
/* We need to find an "nbpId" to identify the next NbpAction request...
|
|
look for an unused one in all of the pending-name-lists in all of the
|
|
sockets within this node. */
|
|
|
|
for (nbpId = 0; nbpId <= 255; nbpId += 1)
|
|
{
|
|
foundMatch = False;
|
|
for (socketList = openSocket->activeNode->openSockets;
|
|
socketList isnt empty and not foundMatch;
|
|
socketList = socketList->next)
|
|
for (pendingList = socketList->pendingNames;
|
|
pendingList isnt empty and not foundMatch;
|
|
pendingList = pendingList->next)
|
|
if (pendingList->nbpId is nbpId)
|
|
{
|
|
foundMatch = True;
|
|
break;
|
|
}
|
|
if (not foundMatch)
|
|
break;
|
|
}
|
|
|
|
/* Did we find a free nbpId? */
|
|
|
|
if (foundMatch)
|
|
{
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnbpTooManyNbpActionsPending);
|
|
}
|
|
|
|
/* All set! */
|
|
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(nbpId);
|
|
|
|
} /* GetNextNbpIdForSocket */
|
|
|
|
void _near NbpCloseSocket(OpenSocket socket)
|
|
{
|
|
PendingName pendingName, nextPendingName;
|
|
RegisteredName registeredName, nextRegisteredName;
|
|
|
|
/* Before we free the socket, free the registered and pending names
|
|
lists; cancel any outstanding timers... */
|
|
|
|
for (registeredName = socket->registeredNames;
|
|
registeredName isnt empty;
|
|
registeredName = nextRegisteredName)
|
|
{
|
|
nextRegisteredName = registeredName->next;
|
|
Free(registeredName);
|
|
}
|
|
for (pendingName = socket->pendingNames;
|
|
pendingName isnt empty;
|
|
pendingName = nextPendingName)
|
|
{
|
|
nextPendingName = pendingName->next;
|
|
(*pendingName->completionRoutine)(ATsocketClosed,
|
|
pendingName->userData,
|
|
pendingName->whyPending,
|
|
pendingName->socket,
|
|
pendingName->nbpId);
|
|
CancelTimer(pendingName->timerId);
|
|
Free(pendingName);
|
|
}
|
|
|
|
return;
|
|
|
|
} /* NbpCloseSocket */
|
|
|
|
long DecodeNbpTuple(void far *buffer,
|
|
long offset,
|
|
Boolean bufferIsOpaque,
|
|
NbpTuple far *tuple)
|
|
{
|
|
short length;
|
|
unsigned char temp;
|
|
|
|
/* Accept an "on the wire" representation of an NBP tuple, decode it into a
|
|
workable structure, return the position in the buffer past the tuple. */
|
|
|
|
/* First the address.networkNumber. */
|
|
|
|
if (bufferIsOpaque)
|
|
MoveFromOpaque(&temp, buffer, offset++, 1);
|
|
else
|
|
temp = (unsigned char)((char far *)buffer)[offset++];
|
|
tuple->address.networkNumber = (unsigned short)(temp << 8);
|
|
if (bufferIsOpaque)
|
|
MoveFromOpaque(&temp, buffer, offset++, 1);
|
|
else
|
|
temp = (unsigned char)((char far *)buffer)[offset++];
|
|
tuple->address.networkNumber += (unsigned short)temp;
|
|
|
|
/* Next the address.nodeNumber. */
|
|
|
|
if (bufferIsOpaque)
|
|
MoveFromOpaque(&temp, buffer, offset++, 1);
|
|
else
|
|
temp = (unsigned char)((char far *)buffer)[offset++];
|
|
tuple->address.nodeNumber = temp;
|
|
|
|
/* Next the address.socketNumber. */
|
|
|
|
if (bufferIsOpaque)
|
|
MoveFromOpaque(&temp, buffer, offset++, 1);
|
|
else
|
|
temp = (unsigned char)((char far *)buffer)[offset++];
|
|
tuple->address.socketNumber = temp;
|
|
|
|
/* Next the enumerator. */
|
|
|
|
if (bufferIsOpaque)
|
|
MoveFromOpaque(&temp, buffer, offset++, 1);
|
|
else
|
|
temp = (unsigned char)((char far *)buffer)[offset++];
|
|
tuple->enumerator = temp;
|
|
|
|
/* Now the Nbp object. */
|
|
|
|
if (bufferIsOpaque)
|
|
MoveFromOpaque(&temp, buffer, offset++, 1);
|
|
else
|
|
temp = (unsigned char)((char far *)buffer)[offset++];
|
|
length = temp;
|
|
if (length < 1 or length > MaximumEntityFieldLength)
|
|
return(0);
|
|
if (bufferIsOpaque)
|
|
MoveFromOpaque(tuple->object, buffer, offset, length);
|
|
else
|
|
MoveMem(tuple->object, (char far *)buffer + offset, length);
|
|
tuple->object[length] = 0;
|
|
offset += length;
|
|
|
|
/* Now the Nbp type. */
|
|
|
|
if (bufferIsOpaque)
|
|
MoveFromOpaque(&temp, buffer, offset++, 1);
|
|
else
|
|
temp = (unsigned char)((char far *)buffer)[offset++];
|
|
length = temp;
|
|
if (length < 1 or length > MaximumEntityFieldLength)
|
|
return(0);
|
|
if (bufferIsOpaque)
|
|
MoveFromOpaque(tuple->type, buffer, offset, length);
|
|
else
|
|
MoveMem(tuple->type, (char far *)buffer + offset, length);
|
|
tuple->type[length] = 0;
|
|
offset += length;
|
|
|
|
/* Last the zone name. */
|
|
|
|
if (bufferIsOpaque)
|
|
MoveFromOpaque(&temp, buffer, offset++, 1);
|
|
else
|
|
temp = (unsigned char)((char far *)buffer)[offset++];
|
|
length = temp;
|
|
if (length is 0)
|
|
{
|
|
tuple->zone[0] = '*';
|
|
tuple->zone[1] = 0;
|
|
return(offset);
|
|
}
|
|
if (length < 1 or length > MaximumEntityFieldLength)
|
|
return(0);
|
|
if (bufferIsOpaque)
|
|
MoveFromOpaque(tuple->zone, buffer, offset, length);
|
|
else
|
|
MoveMem(tuple->zone, (char far *)buffer + offset, length);
|
|
tuple->zone[length] = 0;
|
|
offset += length;
|
|
|
|
/* All set. */
|
|
|
|
return(offset);
|
|
|
|
} /* DecodeNbpTuple */
|
|
|
|
short far EncodeNbpTuple(NbpTuple far *tuple,
|
|
char far *buffer)
|
|
{
|
|
char far *bufferStart = buffer;
|
|
short length;
|
|
|
|
/* Accept our internal NbpTuple representation and produce an equivalent
|
|
"on the wire" representation; fill a user buffer with the new
|
|
representation, and return the number of bytes used by the encoded
|
|
representation. */
|
|
|
|
*buffer++ = (char)(tuple->address.networkNumber >> 8);
|
|
*buffer++ = (char)(tuple->address.networkNumber & 0xFF);
|
|
*buffer++ = (char)(tuple->address.nodeNumber & 0xFF);
|
|
*buffer++ = (char)(tuple->address.socketNumber & 0xFF);
|
|
*buffer++ = (char)(tuple->enumerator & 0xFF);
|
|
|
|
length = (short)strlen(tuple->object);
|
|
*buffer++ = (char)length;
|
|
MoveMem(buffer, tuple->object, length);
|
|
buffer += length;
|
|
|
|
length = (short)strlen(tuple->type);
|
|
*buffer++ = (char)length;
|
|
MoveMem(buffer, tuple->type, length);
|
|
buffer += length;
|
|
|
|
length = (short)strlen(tuple->zone);
|
|
if (length is 0)
|
|
{
|
|
*buffer++ = 1;
|
|
*buffer++ = '*';
|
|
}
|
|
else
|
|
{
|
|
*buffer++ = (char)length;
|
|
MoveMem(buffer, tuple->zone, length);
|
|
buffer += length;
|
|
}
|
|
|
|
return((short)(buffer - bufferStart));
|
|
|
|
} /* EncodeNbpTuple */
|
|
|
|
ExternForVisibleFunction void far
|
|
NbpTimerExpired(long unsigned timerId,
|
|
int additionalDataSize,
|
|
char far *incomingAdditionalData)
|
|
{
|
|
StaticForSmallStack PendingName pendingName, currentPendingName,
|
|
previousPendingName;
|
|
StaticForSmallStack OpenSocket openSocket;
|
|
long onWhosBehalf;
|
|
RegisteredName registeredName;
|
|
int nbpId;
|
|
WhyPending reason;
|
|
void far *opaqueBuffer;
|
|
int totalTuples;
|
|
NbpCompletionHandler *completionRoutine;
|
|
long unsigned userData;
|
|
long id;
|
|
AdditionalData far *additionalData =
|
|
(AdditionalData far *)incomingAdditionalData;
|
|
AppleTalkErrorCode errorCode;
|
|
|
|
/* "Use" unneeded actual parameter. */
|
|
|
|
timerId;
|
|
|
|
previousPendingName = empty;
|
|
|
|
/* Grab the pointer to pending-name structure, that should be our timer's
|
|
additional data. */
|
|
|
|
if (additionalDataSize isnt sizeof(AdditionalData))
|
|
{
|
|
ErrorLog("NbpTimerExpired", ISevError, __LINE__, UnknownPort,
|
|
IErrNbpBadData, IMsgNbpBadData,
|
|
Insert0());
|
|
return;
|
|
}
|
|
|
|
/* We may need to mess with the pending names lists, don't handle incoming
|
|
packets now... */
|
|
|
|
DeferIncomingPackets();
|
|
|
|
pendingName = additionalData->pendingName;
|
|
id = additionalData->id;
|
|
onWhosBehalf = additionalData->socket;
|
|
|
|
/* Try to find the current (expired) pendingName on the pending names
|
|
list of the current socket. There is a vauge chance that it has been
|
|
freed out from under us. */
|
|
|
|
if ((openSocket = MapSocketToOpenSocket(onWhosBehalf)) is empty)
|
|
{
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
ErrorLog("NbpTimerExpired", ISevVerbose, __LINE__, UnknownPort,
|
|
IErrNbpSocketClosed, IMsgNbpSocketClosed,
|
|
Insert0());
|
|
return;
|
|
}
|
|
for (currentPendingName = openSocket->pendingNames;
|
|
currentPendingName isnt empty and
|
|
(id isnt currentPendingName->id and
|
|
currentPendingName isnt pendingName);
|
|
previousPendingName = currentPendingName,
|
|
currentPendingName = currentPendingName->next)
|
|
;
|
|
if (currentPendingName is empty)
|
|
{
|
|
ErrorLog("NbpTimerExpired", ISevVerbose, __LINE__, openSocket->port,
|
|
IErrNbpNoLongerPending, IMsgNbpNoLongerPending,
|
|
Insert0());
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
/* Has our timer expired for the last time? */
|
|
|
|
reason = pendingName->whyPending;
|
|
if (pendingName->remainingBroadcasts is 0)
|
|
{
|
|
/* Grab any information that we may need before we free the pending name
|
|
structure. */
|
|
|
|
nbpId = pendingName->nbpId;
|
|
opaqueBuffer = pendingName->opaqueBuffer;
|
|
totalTuples = pendingName->totalTuples;
|
|
completionRoutine = pendingName->completionRoutine;
|
|
userData = pendingName->userData;
|
|
|
|
if (previousPendingName is empty)
|
|
openSocket->pendingNames = pendingName->next;
|
|
else
|
|
previousPendingName->next = pendingName->next;
|
|
|
|
/* Okay, call the completion routine with the correct arguments. */
|
|
|
|
if (reason is ForConfirm)
|
|
{
|
|
Free(pendingName);
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(ATnbpNotConfirmed, userData, ForConfirm,
|
|
onWhosBehalf, nbpId);
|
|
return;
|
|
}
|
|
else if (reason is ForRegister)
|
|
{
|
|
/* Create a RegisteredName node and move our pending name to this
|
|
list. */
|
|
|
|
if ((registeredName =
|
|
(RegisteredName)Calloc(sizeof(*registeredName), 1)) is empty)
|
|
errorCode = AToutOfMemory;
|
|
else
|
|
{
|
|
errorCode = ATnoError;
|
|
strcpy(registeredName->object, pendingName->object);
|
|
strcpy(registeredName->type, pendingName->type);
|
|
registeredName->enumerator = pendingName->enumerator;
|
|
registeredName->next = openSocket->registeredNames;
|
|
openSocket->registeredNames = registeredName;
|
|
}
|
|
|
|
/* Okay, call the completion routine. */
|
|
|
|
Free(pendingName);
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(errorCode, userData, ForRegister, onWhosBehalf,
|
|
nbpId);
|
|
return;
|
|
}
|
|
else if (reason is ForLookup)
|
|
{
|
|
Free(pendingName);
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(ATnoError, userData, ForLookup, onWhosBehalf,
|
|
nbpId, opaqueBuffer, totalTuples);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
Free(pendingName);
|
|
ErrorLog("NbpTimerExpired", ISevError, __LINE__, openSocket->port,
|
|
IErrNbpBadPendingFlag, IMsgNbpBadPendingFlag,
|
|
Insert0());
|
|
}
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
/* Re-start the re-transmit timer... We'll need the address of the pending-
|
|
name structure when the timer expires... */
|
|
|
|
pendingName->timerId = StartTimer(NbpTimerExpired,
|
|
pendingName->broadcastInterval,
|
|
sizeof(AdditionalData),
|
|
(char *)additionalData);
|
|
|
|
/* Otherwise, we need to re-send the inquiry packet... */
|
|
|
|
if (not SendNbpRequest(pendingName))
|
|
ErrorLog("NbpTimerExpired", ISevError, __LINE__, openSocket->port,
|
|
IErrNbpBadBrRqSend, IMsgNbpBadBrRqSend,
|
|
Insert0());
|
|
pendingName->remainingBroadcasts -= 1;
|
|
|
|
/* The deed is done... */
|
|
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
|
|
} /* NbpTimerExpired */
|
|
|
|
ExternForVisibleFunction void DoNameLookup(int port,
|
|
NbpTuple far *nbpTuple,
|
|
int nbpId,
|
|
long whatSocket)
|
|
{
|
|
StaticForSmallStack BufferDescriptor packet;
|
|
StaticForSmallStack NbpTuple matchedTuple;
|
|
StaticForSmallStack OpenSocket openSocket, currentOpenSocket;
|
|
StaticForSmallStack RegisteredName nameList;
|
|
StaticForSmallStack PendingName pendingList;
|
|
StaticForSmallStack AppleTalkAddress ourAddress;
|
|
int index;
|
|
short tupleCount = 0;
|
|
|
|
/* Does the requester at least have the zone correct? */
|
|
|
|
if (nbpTuple->zone[0] isnt '*' or
|
|
nbpTuple->zone[1] isnt 0)
|
|
while(True)
|
|
{
|
|
/* If we know our zone and it matches, we're okay. */
|
|
|
|
if (portDescriptors[port].thisZoneValid and
|
|
CompareCaseInsensitive(nbpTuple->zone,
|
|
portDescriptors[port].thisZone))
|
|
break;
|
|
|
|
/* If we're non-extended we won't know our zone name (unless we're
|
|
a router), so assume the best. A router shouldn't have forwarded
|
|
a lookup to the wrong zone! */
|
|
|
|
if (not portDescriptors[port].extendedNetwork)
|
|
break;
|
|
|
|
/* Otherwise, wrong zone -- ignore the request. */
|
|
|
|
return;
|
|
}
|
|
|
|
/* Find an open socket on the correct node. */
|
|
|
|
if ((openSocket = MapSocketToOpenSocket(whatSocket)) is empty)
|
|
{
|
|
ErrorLog("DoNameLookup", ISevError, __LINE__, port,
|
|
IErrNbpOpenSocketMissing, IMsgNbpOpenSocketMissing,
|
|
Insert0());
|
|
return;
|
|
}
|
|
if (MapSocketToAddress(whatSocket, &ourAddress) isnt ATnoError)
|
|
{
|
|
ErrorLog("DoNameLookup", ISevError, __LINE__, port,
|
|
IErrNbpCouldntFormAddress, IMsgNbpCouldntFormAddress,
|
|
Insert0());
|
|
return;
|
|
}
|
|
|
|
/* Allocate a buffer descriptor for the reply tuples and fill in the static
|
|
portions. */
|
|
|
|
if ((packet = NewBufferDescriptor(MaximumDdpDatagramSize)) is Empty)
|
|
{
|
|
ErrorLog("DoNameLookup", ISevError, __LINE__, port,
|
|
IErrNbpOutOfMemory, IMsgNbpOutOfMemory,
|
|
Insert0());
|
|
return;
|
|
}
|
|
packet->data[NbpControlOffset] = (NbpLookupReply << 4);
|
|
packet->data[NbpIdOffset] = (char)(nbpId & 0xFF);
|
|
index = NbpFirstTupleOffset;
|
|
|
|
|
|
/* Walk the registered names lists on all sockets open on this node and see
|
|
if we have a matching name. We have to walk the PendingNames list also
|
|
(but don't answer if we're the node trying to register the name). */
|
|
|
|
for (currentOpenSocket = openSocket->activeNode->openSockets;
|
|
currentOpenSocket isnt empty;
|
|
currentOpenSocket = currentOpenSocket->next)
|
|
{
|
|
/* Check regisered names... */
|
|
|
|
for (nameList = currentOpenSocket->registeredNames;
|
|
nameList isnt empty;
|
|
nameList = nameList->next)
|
|
{
|
|
/* Do the "object"s match? */
|
|
|
|
if (not WildMatch(nbpTuple->object, nameList->object))
|
|
continue;
|
|
|
|
/* Do the "type"s match? */
|
|
|
|
if (not WildMatch(nbpTuple->type, nameList->type))
|
|
continue;
|
|
|
|
/* We have full match, build complete NbpTuple. */
|
|
|
|
matchedTuple.address.networkNumber = ourAddress.networkNumber;
|
|
matchedTuple.address.nodeNumber = ourAddress.nodeNumber;
|
|
matchedTuple.address.socketNumber = currentOpenSocket->actualSocket;
|
|
matchedTuple.enumerator = nameList->enumerator;
|
|
strcpy(matchedTuple.object, nameList->object);
|
|
strcpy(matchedTuple.type, nameList->type);
|
|
matchedTuple.zone[0] = '*';
|
|
matchedTuple.zone[1] = 0;
|
|
|
|
/* Encode the matching tuple into the packet. */
|
|
|
|
index += EncodeNbpTuple(&matchedTuple, packet->data + index);
|
|
tupleCount += 1;
|
|
|
|
/* Can the packet hold another tuple? */
|
|
|
|
if (index + MaximumNbpTupleLength > MaximumDdpDatagramSize or
|
|
tupleCount is 0xF)
|
|
{
|
|
packet->data[NbpControlOffset] &= 0xF0;
|
|
packet->data[NbpControlOffset] |= (char)(tupleCount & 0xF);
|
|
if (DeliverDdp(whatSocket, nbpTuple->address, DdpProtocolNbp,
|
|
packet, index, Empty, Empty, 0) isnt ATnoError)
|
|
ErrorLog("DoNameLookup", ISevError, __LINE__, port,
|
|
IErrNbpBadReplySend, IMsgNbpBadReplySend,
|
|
Insert0());
|
|
|
|
/* Allocate a new bufer descriptor. */
|
|
|
|
if ((packet = NewBufferDescriptor(MaximumDdpDatagramSize)) is Empty)
|
|
{
|
|
ErrorLog("DoNameLookup", ISevError, __LINE__, port,
|
|
IErrNbpOutOfMemory, IMsgNbpOutOfMemory,
|
|
Insert0());
|
|
return;
|
|
}
|
|
packet->data[NbpControlOffset] = (NbpLookupReply << 4);
|
|
packet->data[NbpIdOffset] = (char)(nbpId & 0xFF);
|
|
index = NbpFirstTupleOffset;
|
|
tupleCount = 0;
|
|
}
|
|
}
|
|
|
|
/* Check pending names... */
|
|
|
|
for (pendingList = currentOpenSocket->pendingNames;
|
|
pendingList isnt empty;
|
|
pendingList = pendingList->next)
|
|
{
|
|
|
|
if (pendingList->whyPending isnt ForRegister)
|
|
continue; /* Confirms or lookups don't count! */
|
|
|
|
/* Don't say we have a match if we run into the register that we're
|
|
trying to do... */
|
|
|
|
if (ourAddress.networkNumber is nbpTuple->address.networkNumber and
|
|
ourAddress.nodeNumber is nbpTuple->address.nodeNumber and
|
|
nbpId is pendingList->nbpId)
|
|
continue;
|
|
|
|
/* Do the "object"s match? */
|
|
|
|
if (not WildMatch(nbpTuple->object, pendingList->object))
|
|
continue;
|
|
|
|
/* Do the "type"s match? ('=' is a wildcard) */
|
|
|
|
if (not WildMatch(nbpTuple->type, pendingList->type))
|
|
continue;
|
|
|
|
/* We have full match, build complete NbpTuple. */
|
|
|
|
matchedTuple.address.networkNumber = ourAddress.networkNumber;
|
|
matchedTuple.address.nodeNumber = ourAddress.nodeNumber;
|
|
matchedTuple.address.socketNumber = currentOpenSocket->actualSocket;
|
|
matchedTuple.enumerator = pendingList->enumerator;
|
|
strcpy(matchedTuple.object, pendingList->object);
|
|
strcpy(matchedTuple.type, pendingList->type);
|
|
matchedTuple.zone[0] = '*';
|
|
matchedTuple.zone[1] = 0;
|
|
|
|
/* Encode the matching tuple into the packet. */
|
|
|
|
index += EncodeNbpTuple(&matchedTuple, packet->data + index);
|
|
tupleCount += 1;
|
|
|
|
/* Can the packet hold another tuple? */
|
|
|
|
if (index + MaximumNbpTupleLength > MaximumDdpDatagramSize or
|
|
tupleCount is 0xF)
|
|
{
|
|
packet->data[NbpControlOffset] &= 0xF0;
|
|
packet->data[NbpControlOffset] |= (char)(tupleCount & 0xF);
|
|
if (DeliverDdp(whatSocket, nbpTuple->address, DdpProtocolNbp,
|
|
packet, index, Empty, Empty, 0) isnt ATnoError)
|
|
ErrorLog("DoNameLookup", ISevError, __LINE__, port,
|
|
IErrNbpBadReplySend, IMsgNbpBadReplySend,
|
|
Insert0());
|
|
|
|
/* Allocate a new bufer descriptor. */
|
|
|
|
if ((packet = NewBufferDescriptor(MaximumDdpDatagramSize)) is Empty)
|
|
{
|
|
ErrorLog("DoNameLookup", ISevError, __LINE__, port,
|
|
IErrNbpOutOfMemory, IMsgNbpOutOfMemory,
|
|
Insert0());
|
|
return;
|
|
}
|
|
packet->data[NbpControlOffset] = (NbpLookupReply << 4);
|
|
packet->data[NbpIdOffset] = (char)(nbpId & 0xFF);
|
|
index = NbpFirstTupleOffset;
|
|
tupleCount = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Do we have a partially filled packet of tuples? */
|
|
|
|
if (tupleCount isnt 0)
|
|
{
|
|
packet->data[NbpControlOffset] &= 0xF0;
|
|
packet->data[NbpControlOffset] |= (char)(tupleCount & 0xF);
|
|
if (DeliverDdp(whatSocket, nbpTuple->address, DdpProtocolNbp,
|
|
packet, index, Empty, Empty, 0) isnt ATnoError)
|
|
ErrorLog("DoNameLookup", ISevError, __LINE__, port,
|
|
IErrNbpBadReplySend, IMsgNbpBadReplySend,
|
|
Insert0());
|
|
}
|
|
else
|
|
FreeBufferChain(packet); /* Free the unused buffer chain. */
|
|
|
|
/* The deed is done. */
|
|
|
|
return;
|
|
|
|
} /* DoNameLookup */
|
|
|
|
#if Verbose or (Iam a Primos)
|
|
void DumpNbpNamesFor(long socket)
|
|
{
|
|
OpenSocket openSocket;
|
|
PendingName pendingName;
|
|
RegisteredName registeredName;
|
|
|
|
/* Walk the pending and registered lists and tell all. */
|
|
|
|
DeferTimerChecking();
|
|
DeferIncomingPackets();
|
|
|
|
if ((openSocket = MapSocketToOpenSocket(socket)) is empty)
|
|
{
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
printf("Bad socket supplied.\n");
|
|
return;
|
|
}
|
|
|
|
printf("Pending NBP names on %d are:\n", socket);
|
|
for (pendingName = openSocket->pendingNames;
|
|
pendingName isnt empty;
|
|
pendingName = pendingName->next)
|
|
printf(" Object = \"%s\", type = \"%s\", enumerator = %d.\n",
|
|
pendingName->object, pendingName->type, pendingName->enumerator);
|
|
|
|
printf("Registered NBP names on %d are:\n", socket);
|
|
for (registeredName = openSocket->registeredNames;
|
|
registeredName isnt empty;
|
|
registeredName = registeredName->next)
|
|
printf(" Object = \"%s\", type = \"%s\", enumerator = %d.\n",
|
|
registeredName->object, registeredName->type,
|
|
registeredName->enumerator);
|
|
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
|
|
} /* DumpNbpNamesFor */
|
|
#endif
|
|
|
|
ExternForVisibleFunction Boolean WildMatch(char far *wildString,
|
|
char far *masterString)
|
|
{
|
|
char far *before, far *after, far *wild;
|
|
int beforeLength, afterLength, masterLength;
|
|
|
|
/* Match using NBP wildcards. See Inside AppleTalk for details. */
|
|
|
|
if (wildString[0] is 0)
|
|
return(True);
|
|
if (wildString[0] is '=' and
|
|
wildString[1] is 0)
|
|
return(True);
|
|
if ((wild = strchr(wildString, NbpWildCharacter)) is empty)
|
|
return(CompareCaseInsensitive(wildString, masterString));
|
|
|
|
beforeLength = (wild - wildString);
|
|
before = wildString;
|
|
after = wild + 1;
|
|
afterLength = (short)strlen(after);
|
|
masterLength = (short)strlen(masterString);
|
|
|
|
if (masterLength >= (beforeLength + afterLength) and
|
|
FixedCompareCaseInsensitive(before, beforeLength,
|
|
masterString, beforeLength) and
|
|
CompareCaseInsensitive(after, masterString + masterLength -
|
|
afterLength))
|
|
return(True);
|
|
else
|
|
return(False);
|
|
|
|
} /* WildMatch */
|
|
|
|
ExternForVisibleFunction Boolean SendNbpRequest(PendingName pendingName)
|
|
{
|
|
AppleTalkAddress replyTo, destination;
|
|
long replySocket;
|
|
OpenSocket openSocket;
|
|
int port;
|
|
BufferDescriptor descriptor;
|
|
|
|
/* We want to a reply to come to the NIS in the requesting node. */
|
|
|
|
if ((openSocket = MapSocketToOpenSocket(pendingName->socket)) is empty)
|
|
{
|
|
ErrorLog("SendNbpRequest", ISevError, __LINE__, UnknownPort,
|
|
IErrNbpCouldntMapSocket, IMsgNbpCouldntMapSocket,
|
|
Insert0());
|
|
return(False);
|
|
}
|
|
port = openSocket->port;
|
|
if (MapSocketToAddress(pendingName->socket, &replyTo) isnt ATnoError)
|
|
{
|
|
ErrorLog("SendNbpRequest", ISevError, __LINE__, port,
|
|
IErrNbpCouldntFormAddress, IMsgNbpCouldntFormAddress,
|
|
Insert0());
|
|
return(False);
|
|
}
|
|
replyTo.socketNumber = NamesInformationSocket;
|
|
if ((replySocket = MapAddressToSocket(port, replyTo)) < ATnoError)
|
|
{
|
|
ErrorLog("SendNbpRequest", ISevError, __LINE__, port,
|
|
IErrNbpCouldntFormAddress, IMsgNbpCouldntFormAddress,
|
|
Insert0());
|
|
return(False);
|
|
}
|
|
|
|
/* Pick destination... */
|
|
|
|
if (pendingName->whyPending is ForConfirm) /* Send to confirming node... */
|
|
{
|
|
destination = pendingName->confirming;
|
|
destination.socketNumber = NamesInformationSocket;
|
|
}
|
|
else
|
|
{
|
|
if (portDescriptors[port].seenRouterRecently)
|
|
{
|
|
destination.networkNumber =
|
|
portDescriptors[port].aRouter.networkNumber;
|
|
destination.nodeNumber = portDescriptors[port].aRouter.nodeNumber;
|
|
destination.socketNumber = NamesInformationSocket;
|
|
}
|
|
else
|
|
{
|
|
destination.networkNumber = CableWideBroadcastNetworkNumber;
|
|
destination.nodeNumber = AppleTalkBroadcastNodeNumber;
|
|
destination.socketNumber = NamesInformationSocket;
|
|
}
|
|
}
|
|
|
|
/* Build a buffer descriptor for the datagram. Copy the datagram due
|
|
to the possibility of asynchronous transmit completion. */
|
|
|
|
if ((descriptor = NewBufferDescriptor(pendingName->datagramLength)) is Empty)
|
|
{
|
|
ErrorLog("SendNbpRequest", ISevError, __LINE__, port,
|
|
IErrNbpOutOfMemory, IMsgNbpOutOfMemory,
|
|
Insert0());
|
|
return(False);
|
|
}
|
|
MoveMem(descriptor->data, pendingName->datagram, pendingName->datagramLength);
|
|
|
|
/* Do the deed. */
|
|
|
|
if (DeliverDdp(replySocket, destination, DdpProtocolNbp,
|
|
descriptor, pendingName->datagramLength,
|
|
Empty, Empty, 0) is ATnoError)
|
|
return(True);
|
|
else
|
|
return(False);
|
|
|
|
} /* SendNbpRequest */
|
|
|
|
#if Iam an AppleTalkRouter
|
|
ExternForVisibleFunction void
|
|
SendLookupDatagram(int port,
|
|
char far *zone,
|
|
BufferDescriptor lookupDatagram,
|
|
int length)
|
|
{
|
|
AppleTalkAddress source, destination;
|
|
char far *multicastAddress;
|
|
long sourceSocket;
|
|
|
|
/* Send from the NIS of our router on the given port. */
|
|
|
|
source.networkNumber = portDescriptors[port].aRouter.networkNumber;
|
|
source.nodeNumber = portDescriptors[port].aRouter.nodeNumber;
|
|
source.socketNumber = NamesInformationSocket;
|
|
if ((sourceSocket = MapAddressToSocket(port, source)) < 0)
|
|
{
|
|
ErrorLog("SendLookupDatagram", ISevError, __LINE__, port,
|
|
IErrNbpBadSourceSocket, IMsgNbpBadSourceSocket,
|
|
Insert0());
|
|
return;
|
|
}
|
|
|
|
/* To the target network... */
|
|
|
|
destination.nodeNumber = AppleTalkBroadcastNodeNumber;
|
|
destination.socketNumber = NamesInformationSocket;
|
|
if (portDescriptors[port].extendedNetwork)
|
|
{
|
|
/* Send to "0000FF" at correct zone multicast address. */
|
|
|
|
destination.networkNumber = CableWideBroadcastNetworkNumber;
|
|
multicastAddress = MulticastAddressForZoneOnPort(port, zone);
|
|
if (multicastAddress is empty)
|
|
{
|
|
ErrorLog("SendLookupDatagram", ISevError, __LINE__, port,
|
|
IErrNbpBadMutlicastAddr, IMsgNbpBadMutlicastAddr,
|
|
Insert0());
|
|
return;
|
|
}
|
|
if (DeliverDdp(sourceSocket, destination,
|
|
DdpProtocolNbp, lookupDatagram, length,
|
|
multicastAddress, Empty, 0) isnt ATnoError)
|
|
ErrorLog("SendLookupDatagram", ISevError, __LINE__, port,
|
|
IErrNbpBadMulticastSend, IMsgNbpBadMulticastSend,
|
|
Insert0());
|
|
}
|
|
else
|
|
{
|
|
/* Send to "nnnnFF" as braodcast. */
|
|
|
|
destination.networkNumber =
|
|
portDescriptors[port].thisCableRange.firstNetworkNumber;
|
|
if (DeliverDdp(sourceSocket, destination,
|
|
DdpProtocolNbp, lookupDatagram, length,
|
|
Empty, Empty, 0) isnt ATnoError)
|
|
ErrorLog("SendLookupDatagram", ISevError, __LINE__, port,
|
|
IErrNbpBadBroadcastSend, IMsgNbpBadBroadcastSend,
|
|
Insert0());
|
|
}
|
|
|
|
return;
|
|
|
|
} /* SendLookupDatagram */
|
|
#endif
|
|
|