AppleTalk

AppleTalk support in Winsock has been around for a while, although few people are aware of it. AppleTalk probably won't be a protocol you choose to use unless you are communicating with Macintosh computers. AppleTalk is somewhat similar to NetBIOS in that it is name-based on a per-process basis. That is, a server dynamically registers a particular name that it will be known as. Clients use this name to establish a connection. However, AppleTalk names are substantially more complicated than NetBIOS names. The next section will discuss how computers using the AppleTalk protocol are addressed on the network.

Addressing

An AppleTalk name is actually based on three separate names: the name, the type, and the zone. Each name can be up to 32 characters in length. The name identifies the process and its associated socket on a machine. The type is a subgrouping mechanism for zones. Traditionally, a zone is a network of AppleTalk-enabled computers physically located on the same loop. Microsoft's implementation of AppleTalk allows a Windows machine to specify the default zone it is located within. Multiple networks can be bridged together. These human-friendly names map to a socket number, a node number, and a network number. An AppleTalk name must be unique within the given type and zone. This requirement is enforced by the Name Binding Protocol (NBP), which broadcasts a query to see whether the name is already in use. Under the hood, AppleTalk uses the Routing Table Maintenance Protocol (RTMP) to dynamically discover routes to the different AppleTalk networks linked together.

The following structure provides the basis for addressing AppleTalk hosts from Winsock:

 typedef struct sockaddr_at { USHORT sat_family; USHORT sat_net; UCHAR sat_node; UCHAR sat_socket; } SOCKADDR_AT, *PSOCKADDR_AT; 

Notice that the address structure contains only characters or short integers and not friendly names. The SOCKADDR_AT structure is passed into Winsock calls such as bind, connect, and WSAConnect, but in order to translate the human-readable names you must query the network to either resolve or register that name first. This is done by using a call to getsockopt or setsockopt, respectively.

Registering an AppleTalk name

A server that wants to register a particular name so that clients can easily connect to it calls setsockopt with the SO_REGISTER_NAME option. For all socket options involving AppleTalk names, use the WSH_NBP_NAME structure, which is defined as

 typedef struct { CHAR ObjectNameLen; CHAR ObjectName[MAX_ENTITY]; CHAR TypeNameLen; CHAR TypeName[MAX_ENTITY]; CHAR ZoneNameLen; CHAR ZoneName[MAX_ENTITY]; } WSH_NBP_NAME, *PWSH_NBP_NAME; 

A number of types—such as WSH_REGISTER_NAME, WSH_DEREGISTER_NAME, and WSH_REMOVE_NAME—are defined based on the WSH_NBP_NAME structure. Using the appropriate type depends on whether you look up a name, register a name, or remove a name.

The following code sample illustrates how to register an AppleTalk name.

 #define MY_ZONE "*" #define MY_TYPE "Winsock-Test-App" #define MY_OBJECT "AppleTalk-Server" WSH_REGISTER_NAME atname; SOCKADDR_AT ataddr; SOCKET s; // // Fill in the name to register // strcpy(atname.ObjectName, MY_OBJECT); atname.ObjectNameLen = strlen(MY_OBJECT); strcpy(atname.TypeName, MY_TYPE); atname.TypeNameLen = strlen(MY_TYPE); strcpy(atname.ZoneName, MY_ZONE); atname.ZoneNameLen = strlen(MY_ZONE); s = socket(AF_APPLETALK, SOCK_STREAM, ATPROTO_ADSP); if (s == INVALID_SOCKET) { // Error } ataddr.sat_socket = 0; ataddr.sat_family = AF_APPLETALK; if (bind(s, (SOCKADDR *)&ataddr, sizeof(ataddr)) == SOCKET_ERROR) { // Unable to open an endpoint on the AppleTalk network } if (setsockopt(s, SOL_APPLETALK, SO_REGISTER_NAME, (char *)&atname, sizeof(WSH_NBP_NAME)) == SOCKET_ERROR) { // Name registration failed! } 

The first thing you'll notice is the MY_ZONE, MY_TYPE, and MY_OBJECT strings. Remember that an AppleTalk name is three-tiered. Notice that the zone is an asterisk (*). This is a special character used in the zone field to specify the "current" zone the computer is located in. Next we create a socket of type SOCK_STREAM of the AppleTalk protocol ADSP. Following socket creation, you'll notice a call to the bind function with an address structure that has a zeroed-out sat_socket field and only the protocol family field set. This is important because it creates an endpoint on the AppleTalk network for your application to make requests from. Note that while this call to bind allows you to perform simple actions on the network, it doesn't by itself allow your application to accept incoming connection requests from clients. To accept client connections, you must register your name on the network, which is the next step.

Registering an AppleTalk name is simple. Make the call to setsockopt by passing SOL_APPLETALK as the level parameter and SO_REGISTER_NAME as the optname parameter. The last two parameters are a pointer to our WSH_REGISTER_NAME structure and its size. If the call succeeds, our server name was successfully registered. If the call fails, the requested name is probably already in use by someone else. The Winsock error returned is WSAEADDRINUSE (10048 or 0x02740h). Note that for both datagram-oriented and stream-oriented AppleTalk protocols, a process that wants to receive data must register a name that clients can either send datagrams to or connect to.

Resolving an AppleTalk name

On the client side of the equation, an application usually knows a server by its friendly name and must resolve that into the network, node, and socket numbers used by Winsock calls. This is accomplished by calling getsockopt with the SO_LOOKUP_NAME option. Performing a name lookup relies on the WSH_LOOKUP_NAME structure. This structure and its dependent structure are defined as

 typedef struct { WSH_ATALK_ADDRESS Address; USHORT Enumerator; WSH_NBP_NAME NbpName; } WSH_NBP_TUPLE, *PWSH_NBP_TUPLE; typedef struct _WSH_LOOKUP_NAME { // Array of NoTuple WSH_NBP_TUPLEs WSH_NBP_TUPLE LookupTuple; ULONG NoTuples; } WSH_LOOKUP_NAME, *PWSH_LOOKUP_NAME; 

When we call getsockopt with the SO_LOOKUP_NAME option, we pass a buffer cast as a WSH_LOOKUP_NAME structure and fill in the WSH_NBP_NAME field within the first LookupTuple member. Upon a successful call, getsockopt returns an array of WSH_NBP_TUPLE elements containing physical address information for that name. Figure 6-1 contains the file Atalknm.c, which illustrates how to look up a name. In addition, it shows how to list all "discovered" AppleTalk zones and how to find your default zone. Zone information can be obtained by using the getsockopt options SO_LOOKUP_ZONES and SO_LOOKUP_MYZONE.

Figure 6-1. AppleTalk name and zone lookup

 #include <winsock.h> #include <atalkwsh.h> #include <stdio.h> #include <stdlib.h> #define DEFAULT_ZONE "*" #define DEFAULT_TYPE "Windows Sockets" #define DEFAULT_OBJECT "AppleTalk-Server" char szZone[MAX_ENTITY], szType[MAX_ENTITY], szObject[MAX_ENTITY]; BOOL bFindName = FALSE, bListZones = FALSE, bListMyZone = FALSE; void usage() { printf("usage: atlookup [options]\n"); printf(" Name Lookup:\n"); printf(" -z:ZONE-NAME\n"); printf(" -t:TYPE-NAME\n"); printf(" -o:OBJECT-NAME\n"); printf(" List All Zones:\n"); printf(" -lz\n"); printf(" List My Zone:\n"); printf(" -lm\n"); ExitProcess(1); } void ValidateArgs(int argc, char **argv) { int i; strcpy(szZone, DEFAULT_ZONE); strcpy(szType, DEFAULT_TYPE); strcpy(szObject, DEFAULT_OBJECT); for(i = 1; i < argc; i++) { if (strlen(argv[i]) < 2) continue; if ((argv[i][0] == '-') || (argv[i][0] == '/')) { switch (tolower(argv[i][1])) { case 'z': // Specify a zone name if (strlen(argv[i]) > 3) strncpy(szZone, &argv[i][3], MAX_ENTITY); bFindName = TRUE; break; case 't': // Specify a type name if (strlen(argv[i]) > 3) strncpy(szType, &argv[i][3], MAX_ENTITY); bFindName = TRUE; break; case 'o': // Specify an object name if (strlen(argv[i]) > 3) strncpy(szObject, &argv[i][3], MAX_ENTITY); bFindName = TRUE; break; case 'l': // List zones information if (strlen(argv[i]) == 3) // List all zones if (tolower(argv[i][2]) == 'z') bListZones = TRUE; // List my zone else if (tolower(argv[i][2]) == 'm') bListMyZone = TRUE; break; default: usage(); } } } } int main(int argc, char **argv) { WSADATA wsd; char cLookupBuffer[16000], *pTupleBuffer = NULL; PWSH_NBP_TUPLE pTuples = NULL; PWSH_LOOKUP_NAME atlookup; PWSH_LOOKUP_ZONES zonelookup; SOCKET s; DWORD dwSize = sizeof(cLookupBuffer); SOCKADDR_AT ataddr; int i; // Load the Winsock library // if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0) { printf("Unable to load Winsock library!\n"); return 1; } ValidateArgs(argc, argv); atlookup = (PWSH_LOOKUP_NAME)cLookupBuffer; zonelookup = (PWSH_LOOKUP_ZONES)cLookupBuffer; if (bFindName) { // Fill in the name to look up // strcpy(atlookup->LookupTuple.NbpName.ObjectName, szObject); atlookup->LookupTuple.NbpName.ObjectNameLen = strlen(szObject); strcpy(atlookup->LookupTuple.NbpName.TypeName, szType); atlookup->LookupTuple.NbpName.TypeNameLen = strlen(szType); strcpy(atlookup->LookupTuple.NbpName.ZoneName, szZone); atlookup->LookupTuple.NbpName.ZoneNameLen = strlen(szZone); } // Create the AppleTalk socket // s = socket(AF_APPLETALK, SOCK_STREAM, ATPROTO_ADSP); if (s == INVALID_SOCKET) { printf("socket() failed: %d\n", WSAGetLastError()); return 1; } // We need to bind in order to create an endpoint on the // AppleTalk network to make our query from // ZeroMemory(&ataddr, sizeof(ataddr)); ataddr.sat_family = AF_APPLETALK; ataddr.sat_socket = 0; if (bind(s, (SOCKADDR *)&ataddr, sizeof(ataddr)) == INVALID_SOCKET) { printf("bind() failed: %d\n", WSAGetLastError()); return 1; } if (bFindName) { printf("Looking up: %s:%s@%s\n", szObject, szType, szZone); if (getsockopt(s, SOL_APPLETALK, SO_LOOKUP_NAME, (char *)atlookup, &dwSize) == INVALID_SOCKET) { printf("getsockopt(SO_LOOKUP_NAME) failed: %d\n", WSAGetLastError()); return 1; } printf("Lookup returned: %d entries\n", atlookup->NoTuples); // // Our character buffer now contains an array of // WSH_NBP_TUPLE structures after our WSH_LOOKUP_NAME // structure // pTupleBuffer = (char *)cLookupBuffer + sizeof(WSH_LOOKUP_NAME); pTuples = (PWSH_NBP_TUPLE) pTupleBuffer; for(i = 0; i < atlookup->NoTuples; i++) { ataddr.sat_family = AF_APPLETALK; ataddr.sat_net = pTuples[i].Address.Network; ataddr.sat_node = pTuples[i].Address.Node; ataddr.sat_socket = pTuples[i].Address.Socket; printf("server address = %lx.%lx.%lx.\n", ataddr.sat_net, ataddr.sat_node, ataddr.sat_socket); } } else if (bListZones) { // It is very important to pass a sufficiently big buffer // for this option. Windows NT 4 SP3 blue screens if it // is too small. // if (getsockopt(s, SOL_APPLETALK, SO_LOOKUP_ZONES, (char *)atlookup, &dwSize) == INVALID_SOCKET) { printf("getsockopt(SO_LOOKUP_NAME) failed: %d\n", WSAGetLastError()); return 1; } printf("Lookup returned: %d zones\n", zonelookup->NoZones); // // The character buffer contains a list of null-separated // strings after the WSH_LOOKUP_ZONES structure // pTupleBuffer = (char *)cLookupBuffer + sizeof(WSH_LOOKUP_ZONES); for(i = 0; i < zonelookup->NoZones; i++) { printf("%3d: '%s'\n", i+1, pTupleBuffer); while (*pTupleBuffer++); } } else if (bListMyZone) { // This option returns a simple string // if (getsockopt(s, SOL_APPLETALK, SO_LOOKUP_MYZONE, (char *)cLookupBuffer, &dwSize) == INVALID_SOCKET) { printf("getsockopt(SO_LOOKUP_NAME) failed: %d\n", WSAGetLastError()); return 1; } printf("My Zone: '%s'\n", cLookupBuffer); } else usage(); WSACleanup(); return 0; } 

When you are using most of the AppleTalk socket options—such as SO_LOOKUP_MYZONE, SO_LOOKUP_ZONES, and SO_LOOKUP_NAME—you need to provide a large character buffer to the getsockopt call. If you call an option that requires you to provide a structure, that structure needs to be at the start of the supplied character buffer. If the call to getsockopt is successful, the function places return data in the character buffer after the end of the supplied structure. Take a look at the SO_LOOKUP_NAME section in Figure 6-1. The variable cLookupBuffer is a simple character array used in the call to getsockopt. First we cast it as a PWSH_LOOKUP_NAME and fill in the name information we want to find. We pass the buffer into getsockopt, and upon return, we increment the character pointer pTupleBuffer so that it points to the character after the end of the WSH_LOOKUP_NAME structure. Next we cast that pointer to a variable of PWSH_NBP_TUPLE because the data returned from a lookup name call is an array of WSH_NBP_TUPLE structures. Once we have the proper starting location and type of the tuples, we can walk through the array. Chapter 9 contains more in-depth information about the various socket options specific to the AppleTalk address family.

Creating a Socket

AppleTalk is available in Winsock 1.1 and later, so you can use either socket-creation routine. Again, you have two options of how to specify the underlying AppleTalk protocols. First you can supply the corresponding define from Atalkwsh.h for the protocol you want, or you can enumerate the protocols using WSAEnumProtocols and passing the WSAPROTOCOL_INFO structure. Table 6-1 lists the required parameters for each AppleTalk protocol type when you create a socket directly using socket or WSASocket.

Table 6-1. AppleTalk protocols and parameters

Protocol Address Family Socket Type Protocol Type
MSAFD AppleTalk [ADSP] SOCK_RDM ATPROTO_ADSP
MSAFD AppleTalk [ADSP] [Pseudo-Stream] SOCK_STREAM ATPROTO_ADSP
AF_APPLETALK
MSAFD AppleTalk [PAP] SOCK_RDM ATPROTO_PAP
MSAFD AppleTalk [RTMP] SOCK_DGRAM DDPPROTO_RTMP
MSAFD AppleTalk [ZIP] SOCK_DGRAM DDPPROTO_ZIP



Network Programming for Microsoft Windows
Linux Server Hacks, Volume Two: Tips & Tools for Connecting, Monitoring, and Troubleshooting
ISBN: 735615799
EAN: 2147483647
Year: 1998
Pages: 159

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net