< NetBIOS Client Example | Winsock2 Supported Protocols Main | AppleTalk Receiver-Sender Example >


 

 

Winsock 2: Other Supported Protocols 4 Part 7

 

 

What do we have in this chapter 4 part 7?

  1. AppleTalk

  2. The Addressing Scheme

  3. Registering an AppleTalk Name

  4. Resolving an AppleTalk Name

  5. AppleTalk Zone Program Example

 

AppleTalk

 

Note: The AppleTalk Protocol Is Not Available in Windows XP. We make it available on Windows XP using the following 'quick' and 'dirty' steps just for program example testing.

First of all copy the following six files from Windows 2000 machine or installation CD. In this case we extract these files form Windows 2000 server installation CD using the EXPAND command, expanding and copying to their respective folders.

 

C:\WINDOWS\INF\netatlk.inf

C:\ WINDOWS \SYSTEM32\DRIVERS\sfmatalk.sys

C:\ WINDOWS \SYSTEM32\sfmatmsg.dll

C:\ WINDOWS \SYSTEM32\sfmmon.dll

C:\ WINDOWS \SYSTEM32\sfmwshat.dll

C:\ WINDOWS \SYSTEM32\SPOOL\PRTPROCS\W32X86\sfmpsprt.dll

 

Those files should be under the X:\i386 folder in old dos compressed file format.

 

AppleTalk: The nettalk.ini file

 

If you copy those files from the existing Windows 2000 machine, paste them to their respective folders (the WINNT should be replaced with WINDOWS for Windows XP). Next, restart your machine.

Open the Local Area Connection property page. Click the Install button.

 

AppleTalk: Installing the Appletalk protocol

 

Select the Protocol network component and click the Add button.

 

AppleTalk: Selecting the protocol network component

 

Select AppleTalk Protocol from the Network Protocol pane and click OK.

 

AppleTalk: Selecting the Appletalk protocol

 

The AppleTalk Protocol should be visible as shown below.

 

AppleTalk: Appletalk protocol is visible in the Local Area Connection Properties

 

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.

 

The Addressing Scheme

 

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.

 

AppleTalk Zone Program Example

 

The following sample 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.

 

-----------------------------------------------------------

Appletalk: a program example that illustrates how to look up a name - Creating a new empty Win32 console mode application and adding the project/solution name.

 

Add the following source code.

 

#include <winsock.h>

#include <atalkwsh.h>

#include <stdio.h>

 

#define DEFAULT_ZONE       "*"

#define DEFAULT_TYPE       "Windows Sockets"

#define DEFAULT_OBJECT     "AppleTalk-Server_Bodo"

char szZone[MAX_ENTITY], szType[MAX_ENTITY], szObject[MAX_ENTITY];

BOOL bFindName = FALSE, bListZones = FALSE, bListMyZone = FALSE;

 

void usage()

{

    printf("Usage: appletalkzone [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_s(szZone, sizeof(szZone),DEFAULT_ZONE);

            strcpy_s(szType, sizeof(szType),DEFAULT_TYPE);

            strcpy_s(szObject, sizeof(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_s(szZone, sizeof(szZone),&argv[i][3], MAX_ENTITY);

                                                bFindName = TRUE;

                                                break;

                                    case 't':        // Specify a type name

                                                if (strlen(argv[i]) > 3)

                                                            strncpy_s(szType, sizeof(szType),&argv[i][3], MAX_ENTITY);

                                                bFindName = TRUE;

                                                break;

                                    case 'o':        // Specify an object name

                                                if (strlen(argv[i]) > 3)

                                                            strncpy_s(szObject, sizeof(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;

    }

    printf("WSAStartup() is OK! Library loaded...\n");

 

    ValidateArgs(argc, argv);

 

    atlookup = (PWSH_LOOKUP_NAME)cLookupBuffer;

    zonelookup = (PWSH_LOOKUP_ZONES)cLookupBuffer;

    if (bFindName)

    {

        // Fill in the name to look up

        strcpy_s(atlookup->LookupTuple.NbpName.ObjectName, sizeof(atlookup->LookupTuple.NbpName.ObjectName), szObject);

        atlookup->LookupTuple.NbpName.ObjectNameLen = strlen(szObject);

        strcpy_s(atlookup->LookupTuple.NbpName.TypeName, sizeof(atlookup->LookupTuple.NbpName.TypeName),szType);

        atlookup->LookupTuple.NbpName.TypeNameLen = strlen(szType);

        strcpy_s(atlookup->LookupTuple.NbpName.ZoneName, sizeof(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 with error code %d\n", WSAGetLastError());

        return 1;

    }

    else

        printf("socket() is OK for AppleTalk!\n");

 

    // 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 with error code %d\n", WSAGetLastError());

        return 1;

    }

    else

        printf("bind() is OK!\n");

 

    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 with error code %d\n", WSAGetLastError());

            return 1;

        }

        else

            printf("getsockopt(SO_LOOKUP_NAME) is OK!\n");

 

        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<(int)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 with error code %d\n", WSAGetLastError());

            return 1;

        }

        else

            printf("getsockopt(SO_LOOKUP_NAME) looks fine!\n");

 

        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<(int)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 with error code %d\n", WSAGetLastError());

            return 1;

        }

        else

            printf("getsockopt(SO_LOOKUP_NAME) is OK lor!\n");

 

        printf("My Zone: '%s'\n", cLookupBuffer);

    }

    else

        usage();

 

    WSACleanup();

    return 0;

}

 

Build and run the project.

 

Appletalk: a program example that illustrates how to look up a name - a sample output

 

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.

 

 

 


< NetBIOS Client Example | Winsock2 Supported Protocols Main | AppleTalk Receiver-Sender Example >