AppleTalk

AppleTalk

AppleTalk support in Winsock has been around for a while, although few people are aware of it. You probably will not choose AppleTalk 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: name, type, and zone. Each name can be up to 32 characters long. 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 if 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 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—which include 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 Data Stream 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 although this call to bind allows you to perform simple actions on the network, by itself it doesn't 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 name is probably already in use. The Winsock error returned is WSAEADDRINUSE (10048). 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 Winsock calls use. 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. The following sample 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.

#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 the returned data in the character buffer after the end of the supplied structure. Take a look at the SO_LOOKUP_NAME section in the above code sample. The variable, cLookupBuffer, is a simple character array used in the call to getsockopt. First, cast it as a PWSH_LOOKUP_NAME and fill in the name information you want to find. Pass the buffer into getsockopt, and upon return, increment the character pointer pTupleBuffer so that it points to the character after the end of the WSH_LOOKUP_NAME structure. Next, 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 you have the proper starting location and type of the tuples, you can walk through the array. Chapter 7 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 specifying 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 4-1 lists the required parameters for each AppleTalk protocol type when you create a socket directly using socket or WSASocket.

Table 4-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

MSAFD AppleTalk [PAP]

AF_APPLETALK

SOCK_RDM

ATPROTO_PAP

MSAFD AppleTalk [RTMP]

SOCK_DGRAM

DDPPROTO_RTMP

MSAFD AppleTalk [ZIP]

SOCK_DGRAM

DDPPROTO_ZIP

note

On the companion CD, you will find an AppleTalk application named ATALK.CPP which can operate as a sender or a receiver application over the PAP and ADSP protocols.



Network Programming for Microsoft Windows
Network Programming for Microsoft Windows (Microsoft Professional Series)
ISBN: 0735605602
EAN: 2147483647
Year: 2001
Pages: 172
Authors: Anthony Jones

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