Introducing Peer-to-Peer Computing

Peer-to-peer computing is a term that has gained a lot of popularity in recent times. Today, organizations and businesses are increasingly depending on collaboration between individuals and groups to perform essential tasks. As a result, collaboration has become more essential at an individual level because these applications form more ad hoc online groups for business, entertainment, and cultural purposes.

Peer-to-peer computing essentially is a set of networked computers that rely on the computing power and bandwidth of the individual computers on the network as opposed to the traditional approach of relying on a smaller number of more powerful server computers on the network. A computer connected to a P2P network is called a node or peer. The nodes in a P2P network usually are connected on an ad hoc basis, and the real power in a P2P network lies in these nodes. The peers are responsible for uploading and downloading data among themselves without the need for a server.

Two types of P2P networks exist: a pure network and a hybrid network. A pure P2P network has no concept of a client or a server; it has only nodes, which act in the capacity of both a server and a client as needed. A hybrid P2P network, on the other hand, has a central server that keeps track of the various peers on the network. This server responds to requests from the peers for information only and does not store any data. The peers are responsible for hosting the information. For example, in a file-sharing P2P application, the files are stored by the peer, and the server is aware only of what files are stored at what peer.

In the real world, pure P2P solutions that implement only peering protocols and do not rely on the concept of clients and server are rare. Most P2P solutions rely on some nonpeer elements in the solution such as Domain Name System (DNS, used to translate computer hostnames to IP addresses). Some of the P2P solutions also have the notion of a superpeer, where other peers are connected to this superpeer in a star-like fashion. Over time, these superpeers could also be used as local servers. The networks in P2P applications are also called meshes (or sometimes a mesh network), akin to a wire mesh. Each node in a mesh at a minimum has bidirectional communication capability with its neighbors. A cloud is a mesh network with a specific address scope. These scopes are closely related to IPv6 scopes, and the peers in a cloud are those that can communicate within the same IPv6 scope.

Why Use P2P?

Usually, the nodes, or peers, in a P2P network are ordinary computers that most people use in their day-to-day life at home or work. Often these computers are on a home Internet connection (such as dial-up or broadband), and on average most of them are available only for a relatively short period of time in a day. Setting up a P2P network is relatively easy, and you do not need to have a technical background in computer science or be an ubergeek. As a result, P2P is popular and has a wide adoption rate among all categories of users.

One of the guiding principles for P2P solutions is that all nodes provide resources to the group such as processing power, bandwidth, storage, and so on. Therefore, when the overall demand increases with the addition of more nodes, so does the capacity. This is significantly different from a traditional client-server model, where adding more clients would slow everyone because more clients are competing for the same set of resources on the server. In addition, the distributed, ad hoc nature of the P2P network increases the resilience of the overall system by eliminating single points of failures by distributing data over multiple peers. Because of this, data can be shared effectively, and a network can be scaled up at a relatively low cost. By its nature, a P2P solution allows support for ad hoc and disconnected networks.

NOTE

Although most users might have come across P2P applications that are used for sharing files, possibly using the likes of Gnutella, Kazaa, Napster, BitTorent, and so on, P2P applications are used across many problem domains and industries such as telephony and video, gaming, data replication, anonymity (such as Publius, Freenet, and so on), instant messaging, distributed computing (such as Distributed.net), and so on.

Broadly speaking, P2P solutions can fall into one of the following solution domains:


Real-time communication:

P2P enables services such as serverless instant messaging and real-time game play. You can use instant messaging with voice and video today, but most implementations require the use of a server to function. If you are in an isolated network environment (such as those defined by many enterprises), then you would not be able to use most instant messaging solutions; but with serverless instant messaging, you could overcome these boundaries. Similarly, the real-time gaming networks are more aligned toward the enthusiastic gamer, allowing them to go head to head with other gamers. However, if you are not a hard-core gamer or if you want to set up an ad hoc game that can communicate in a variety of networking situations, without P2P networking this would be a significant challenge in today's environment.


Collaboration:

P2P allows you to share files, workspaces, and experiences with others. Sharing workspaces allows a user to create an ad hoc workspace that can be populated with content and tools that can be used for solving a common goal. These can also provide collaborative functionality such as message boards; sharing files becomes just another aspect of this workspace. A P2P network allows one to share files in an easy and user-friendly way. Sharing your experiences with others in near real-time is a new opportunity using P2P networks. With the wide availability of wireless networks, it is becoming easier for people to share their day-to-day experiences in a more real-time fashion such as a music concert, snowfall whilst on a holiday, and so on. Similarly, using Groove (which is part of Microsoft Office 2007), a team does not need to be physically in the same office or even country, but they can still work in a secure environment in a virtual office or workspace. This allows them to share and synchronize files, manage projects, host discussion threads, schedule meetings, share text in real-time, and so on.


Content distribution:

P2P allows for the easy distribution of content. This content could be software updates, text, audio, video, and so on. If you want to distribute a large amount of audio and video today, you need fairly big bandwidth requirements that can handle the volume. But when using a P2P, only a small number of peers would need to get the data from the centralized servers, and then they will propagate the content out on the mesh to the next closest peer that would want the content. Similarly, product updates, say, within an organization can be propagated quickly to everyone. For example, many enterprises use this model to distribute leadership content, patches, software updates, policy updates, and so on, throughout the organization. Many of the open source and Linux implementations also use this model to distribute their builds.


Distributed processing:

P2P computing allows one to distribute computing tasks among various peers on the network and aggregate the results later. A large task usually is broken into smaller chunks that the peers can handle. Once each peer is finished with their task, they send the results to a central aggregation point. The peers can be configured to process these tasks only when it is idle if need be, so they do not use the resources of the machine when it is being used. One of the pioneers of this was the SETI@home project (run by the University of California). The SETI@home project brought this concept to the general public to the extent of sparking off contests between peers to see who could process more data in a given unit of time. Some of these contests effectively became bragging rights and showed off either that they had very powerful machines that could crunch more data or that they had more peers connected that collectively did more work.

The Challenges of P2P

P2P challenges can broadly be categorized in two segments: technical and legal. The technical aspects might cover topics such as difficult and complex to build, how one should achieve universal connectivity, and so on. The legal controversy is based on sharing music and movies originally made popular by file-sharing networks such as Napster. P2P networks, like most networks, can also be open to attacks. More specifically, some of the attacks are specifically designed for P2P networks such as poison attacks, polluting attacks, defection attacks, denial of service, and so on. We will cover these challenges and their possible solutions when we explore a typical P2P application stack in the next section.

NOTE

Poison attacks are attacks where the contents are different from the description of that content. Polluting attacks are those where invalid chunks are added to an otherwise valid file. Defection attacks are those where users or software use the network without contributing resources to the network.

P2P faces many other challenges other than the immediate technical implementation details. Currently, there are no standards defined, which means interoperability between different P2P meshes is something that is difficult to achieve. Firewalls are becoming increasingly sophisticated, and although a P2P network can be based purely on IP, there are still many symmetric NAT firewalls out there. This might give the impression that these NATed addresses will cause the P2P mesh to not reach all endpoints. However, this rarely causes any issues and for the majority of the solutions is not a concern. Management and diagnostics are still issues. Because of the nondeterministic flow in a network, trying to diagnose a bug, for example, becomes a daunting task. Also, if you need to manage a P2P network and apply something like a distributed policy, then that also becomes a challenge. For example, many enterprises do not have control because of not being able to apply a distributed policy and because, indirectly, of the accountability. In many situations, this is not acceptable because of various regulatory, legal, and compliance requirements. This also makes it difficult to isolate and locate individual users who can cause security concerns (again because of the lack of user accountability).

On the legal challenges front, there is a perception because of the media coverage that all P2P is illegal and bad. And anonymous P2P networks allow one to share content easily, whether legal or not, so that does not help the cause. Although various companies and entities such as RIAA, Movie Studios, and so on, are fighting the battle in the courts, there is a lot of confusion to the end user. This is partly because the laws are different from country to country, and there is a lot of gray area and interpretation. For example, RIAA has gone after a few thousand users in the United States and is also looking to target some of those in the United Kingdom and other countries. However, certain countries such as France had legalized P2P at one time and later changed the local laws without absolute clarification. All this has led to more blurred distinction between what is legal and what is not.

NOTE

We have kept the legal perception of P2P intentionally vague in this section. Since the local laws change so much based on the jurisdiction, it is not possible to cover all the situations here. The important part to remember is that the legal issues have nothing to do with the technology; rather, P2P is one specific implementation of the technology.

P2P Development Life Cycle

When developing and deploying P2P applications, you face three primary issues: how to achieve end-to-end connectivity; how to provide a common foundation consisting of various state data, identity management, and so on, for peers to use when exchanging state; and how to deploy and scale the solution in a secure manner. Each of these is an important piece of the puzzle to enable the P2P solution to work.


End-to-end connectivity:

Because of the loose and disparate nature of a mesh, from a development and debugging perspective, ensuring that the various peers can seamlessly connect to each other is a challenge. Furthermore, complicating this is the fact that the peers connecting to the mesh might be using one or more communication technologies. Because of the nature of P2P applications, this also needs to support communication over various networks.


Common foundation:

This is the "administrative" functionality that every P2P application needs in order to manage the various peers on the mesh. This includes identity management, contact management, node discovery, node naming, secure session management, multipeer communication, and so on.


Secure and scalable deployment:

This is the ability to build on protocols specifically engineered for large-scale deployment, and it provides built-in security.

How Are Nodes Identified?

On a mesh, each node needs to be identified by a unique ID usually called a peer ID or mesh ID. To resolve these peer IDs to their corresponding Internet address, the Peer Name Resolution Protocol (PNRP) is used instead of DNS. Each peer node irrespective of type (such as computer, user, group, device, service, and so on) can have its own peer ID. This list of IDs is distributed among the peers using a multilevel cache and referral system that allows name resolution to scale to billions of IDs while requiring minimal resources on each node.

An endpoint is defined as a combination of a peer ID, port number, and communication protocol. Using an endpoint, data can be sent between nodes in two ways. One of these is for a peer to directly send the data to another peer. And the other is for a peer to send the data to all the other peers on the same mesh; this is also known as flooding. A flooded message could arrive at the same peer multiple times via different routes on the mesh.

Installing the Windows P2P Networking Stack

Windows P2P networking stack is not installed on Windows XP by default. If you are running Windows XP with Service Pack 2, then perform the following steps to install the P2P networking stack:

  1. Click Start Control Panel Add/Remove Programs.

  2. Click Add/Remove Components.

  3. In Components, click Networking Services, and then select Details.

  4. Select the Peer-to-Peer check box, and then select OK.

  5. Click Next, and follow the instructions on the screen.

If you are running Windows XP with SP1, then you will need install the Windows Advanced Networking Pack for Windows XP, which is a free download available at http://tinyurl.com/6ze98.

If you are running Windows Vista, then this is already installed; however, you might have to enable the firewall exceptions. To do so, follow these steps:

  1. Click Start Control Panel Security.

  2. Under Windows Firewall, select Allow a Program Through Windows Firewall.

  3. Click the Exceptions tab.

  4. Check Windows Peer to Peer Collaboration Foundation.

  5. Click OK.

Windows P2P Networking

Microsoft introduced the Windows peer-to-peer networking stack as a developer platform in Windows XP SP1. This stack is not installed by default; to install it on Windows XP (with SP2), you need to select the Peer-to-Peer option as part of the Networking Services within the Windows components that are available via the Add/Remove Programs option in the Control Panel. If you have only Windows XP SP1, then you need to install the Advanced Networking Pack to get the peer-to-peer networking stack.

Figure 12-1 shows the architecture for P2P networking as defined by Microsoft. The significant components that make up this stack are graphing, grouping, Name Service Provider (NSP), PNRP, and the identity manager. It is worth pointing out that this stack is unmanaged code with only a subset of the functionality exposed via WCF.

Windows PNRP, graphing, grouping, and identity manager networking architecture

Identity Management

P2P solutions usually do not use DNS because of the transient nature of the mesh. In theory, using Dynamic DNS is an option, but in actuality few DNS servers on the Internet support this in the real world. This raises an interesting question about how to resolve peer names to their network addresses (including ports, protocols, and so on). To allow this, Windows P2P networking is using PNRP. Some of the key attributes of PNRP that make it ideal for resolving names are as follows:


Name resolution is distributed and serverless:

Each peer on the mesh caches a portion of the list of names on the mesh and further refers to other peers. Although this is not a true serverless environment, because there is a root node that is used to initiate the process, this node is not used for name resolutions.


Use IDs and not names:

IDs identify peers instead of names. Since IDs are numbers, there are no language or locale issues.


Use multiple IDs:

Since every service on the mesh can have its own identifier, the same node might end up having more than one ID.


Scale to large number of IDs:

Because the list of IDs can grow to a large number, a multilevel cache and referral system is implemented between the peers that does not need significant resources.

Peer Names

Peer names can be registered either as secured or as unsecured. Unsecured names are recommended for use in a private network only, because the names are strings and can easily be spoofed. However, secured names need to be registered and are protected with a certificate and digital signature.

A PNRP ID is 256 bits long; the high-order 128 bits are a hash of the peer name assigned to the endpoint, and the lower 128 bits of the PNRP ID are an autogenerated number used for service location. The format for the peer name is Authority.Classifier. When using a secured network, the Authority is a secure hash (using Secure Hash Algorithm, SHA) of the public key of the peer name in hex. When using an unsecured network, the Authority is the single character 0 (zero). The Classifier is a Unicode string up to 150 characters long that identifies the application. The autogenerated number, used by the lower 128 bits, uniquely identifies different instances using the classifier participating in the same mesh. The combination of 256 bit mesh ID and the service location allow multiple PNPR IDs to be registered from a single computer.

PNRP Name Resolution

When a peer wants to resolve the peer name, it constructs the peer ID as discussed earlier and tries to find that entry in its cache for the ID. If a match is found, then it sends a PNRP request message to the peer and waits for a response. This approach ensures that the target peer node, with which another peer is trying to communicate, is active in the cloud. If no match is found, then an iterative process is used with the target peer that informs the sender of the peer that is the closest match to the ID that is trying to be resolved. It is up to the original sender at this stage to send the same request to the matching peer as the one to which it was pointing. If that new peer the sender was pointed to is also not the correct one, then that in turn will return the next closest matching peer to the sender, and so on.

When a PNRP request message is forwarded, both the nodes that are forwarded to and the responses received are cached. This prevents the situation where things could get into an endless loop. The name records have built-in security because of the public-private key pair. NSP is a mechanism by which you can access an arbitrary name provider; in the Windows P2P network stack, this provider interface is PNRP.

Graphing

A graph is a collection of peer nodes where one node may communicate to another using the neighbor's peer connections. A peer graph is built on the concept of flooding, which makes it possible to send data to all peers connected to that specific graph. To be able to handle deltas in this data, the flooding protocol sends these changes in data to all the peers. This is achieved by associating a unique GUID to each peer, which has an increasing version number or sequence number, and is further qualified by an age or a status. A synchronous process in a graph ensures that peers have the same set of data. The graphs themselves are insecure, and the P2P stack's architecture provides pluggable modules that provide security. These modules can define various aspects both at the connection and at the message level such as authentication, confidentiality, integrity, and so on.

Grouping

Grouping is nothing but a combination of graphing, PNRP, and the peer grouping "security provider" from Microsoft. This security provider provides management of the credentials of the members that are part of the group and supports the secure publication of records. Every group is identified by a unique ID that is used by peers on the network for identification. For groups, the PNRP secure names are used as IDs. Every peer has a set of two credentials—the first of which is to prove ownership to a peer's identity, a unique peer name, and credentials. The second set of credentials proves that a peer is a member of a group. For secure groups, participation is restricted to a known set of peers. Information is spread through the groups using records. A record consists of many pieces of information such as the peer's validity, data for record validity when challenged, a time stamp for validation, and the actual payload containing the record information. Security is a combination of the following:

  • Peer name

  • Group membership certificates

  • Roles

  • Secure publishing

  • Security policies

  • Secure connections

How Does a P2P Mesh Work?

As stated earlier, a mesh network is nothing but a P2P network and is responsible for routing data between nodes. A mesh network also allows for continuous connection, and if there are any blocked paths, they can be reconfigured in a hopping manner, from peer to peer, until a connection is established. This property makes them self-healing, allowing them to continue to operate even when a peer drops out. All peers in a mesh propagate the same mesh name, which gives new peers joining the mesh visibility into other nodes that are on the mesh. Figure 12-2 shows a sample mesh network with multiple peers connected.

Mesh network

Mesh Flavors

Broadly speaking, two types of mesh networks are available, namely, the grouping and peer channel. Each of these options has their respective service models. Grouping is primarily used by the Data Replication service and is available in Windows XP (with SP2). The various peers, in the mesh, exchange messages by replicating records containing the data. Peer channel, on the other hand, is primarily a message-based service and is available in WCF. The peers in the mesh share data by building synchronous services. Both meshes have built-in security; grouping is implemented via a password and grouped certificates that are managed by the mesh. Peer channel security is also implemented via a password (to join the mesh) and individual certificates that are managed directly by the applications in the mesh. Both types of meshes support PNRP for node discovery; however, only peer channel supports a developer-supplied model such as web service. While a grouping mesh implementation is unmanaged and accessed via the Win32 API library, the peer channel is part of WCF, which is managed code.

The connection types between the peers in a mesh can also be of two topology types: full or partial. In full topology, each peer is connected to every other peer on the mesh. In partial topology, a peer is connected only to a handful of other peers—most likely those with which it exchanges the most data and has affinity. The example in Figure 12-2 shows a partial mesh because every peer is not connected to every other peer on the network. It is rare to come across a full topology mesh because it is not practical to operate in that mode. If there are N nodes in a full topology mesh, then each node is connected to N − 1 nodes at the same time. In other words, if there are 1,000 nodes in a mesh, each of the 1,000 nodes has a connection open to 999 other nodes at the same time. This would lead to a situation where the mesh would soon start running out of resources as more nodes joined the network.

Three types of P2P applications exist: one-to-one, one-to-many, and many-to-many. Figure 12-3 shows the normal flow of a P2P application. When one peer in a mesh wants to communicate with another, the steps are to find the other peer, send an invitation, and create a session between the two.

P2P application flow

Let's examine each of the previous steps in a little more detail:

  1. Find peer: Essentially to "talk" to some other peer, the first task you need to do is find it. You have two ways to go about this. The first is to find other peers on the LAN you are part of. The other is to find peer or peer groups using PNRP. If you are finding other peers on the LAN, you should use the People Near Me feature and integrate that into your application. People Near Me uses WS-Discovery to find users who are signed in. People Near Me is out of the scope of this book, but at a high level it is collaboration with people located nearby. There are many requirements for this to work, such as people discovery, application discovery, metadata discovery, security, invitation, and so on. When using PNPR, on the other hand, it is a serverless name resolution that can be either on the local network or over the Internet.

  2. Send invitation: Invitations are real-time and can go to People Near Me or peers over the Internet, via either a user message or some application data such as mesh name, endpoint, and so on. A listener at the other end detects this incoming invitation request and launches the appropriate application.

  3. Join mesh: The last step to establish a session is to specify the mesh name and credentials (if applicable) that one is intending to join.

Figure 12-4 shows the scenario where you have peers that are part of a mesh and are trying to communicate with each other.

P2P one-to-many application flow

What Is Peer Channel?

The P2P networking stack that we have been discussing so far is unmanaged code, and a developer needs to use C++ to be using it to its full potential. This stack is part of Windows XP and will be improved as part of Windows Vista and Longhorn Server. Microsoft also has a managed code implementation for a subset of the functionality that is exposed by the P2P networking stack, called peer channel, and is released as part of WCF. Since peer channel is a managed stack, you can use any .NET language, which makes implementing P2P applications easier and more productive when compared to unmanaged code.

A typical channel in WCF has two participants, a client and a server, but a peer channel can have any number of participants. A message that is sent by one participant will be received by all other participants on the channel. However, certain mechanisms in peer channel allow you to send a message to only part of the mesh, instead of the whole mesh. To resolve the addresses of a node in a peer channel mesh, you can use either PNRP or a custom resolver. When a node is resolved, that target node can either accept or decline the connection. If the connection is accepted by the target node, it sends it a welcome message that among other things will contain the list of other nodes that are part of the mesh. If the connection is refused, then the existing node sends the prospective node a refusal message containing the reason and a list of the addresses of the other nodes in the mesh.

In WCF, a node that will be part of a mesh is defined via the PeerNode class in your application. The endpoint address for that node is defined via the PeerNodeAddress class (which internally implements the EndpointAddress class). The number of neighbors of each node dictate the overall structure of a peer channel mesh that is actively maintained, resulting in an evenly distributed mesh. For example, a node in the mesh tried to maintain from two to seven connections to its neighbors. Although an ideal state for the node is to have three connections, it will accept up to seven connections. Once a node has reached that threshold, it will start refusing any new connections. If a node loses all its neighbors, it will enter a maintenance cycle where it tries to acquire new neighbors to get to its optimum state of three connections. Also note, you cannot change or configure either the thresholds or the underlying mesh because the peer channel owns and maintains this.

The peer channel also tries to improve efficiency by limiting communication within the mesh by keeping repetitive messages passed to a minimum. When a node sends a message to the mesh, it sends it to the neighbors to which it is connected. These neighbors in turn inspect the message and then forward it to their neighbors, but they do not forward it to the neighbor from whom they got the message to start. In addition, a connection to a neighbor might be terminated if it keeps trying to resend a message that has been processed previously. Internally each node keeps an idempotent local cache of the WS-Addressing message ID and the ID of the neighbor that delivered that message. This allows an optimized mesh network that does not waste resources with repeating data.

A node can send messages to a subset of the mesh by assigning a hop count to the message. A hop count keeps a count of the number of nodes to which a message has been forwarded. This count is expressed as an integer within the message header and is decremented with each hop until it reaches a value of zero, after which it is not forwarded.

NOTE

NetShell is available only when you have the P2P networking option installed in Windows XP. Although NetShell is installed by default on Windows Vista, you need to allow that as an exception in the firewall for it to work.

QuickReturnTraderChat Sample

To get a better understanding of how everything comes together using the peer channel, let us start with a simple application called QuickReturnTraderChat. We have a few traders spread across a stock exchange who need the ability to chat with each other. The exchange, being a secure environment, does not allow any access to IM clients and wants to use the QuickReturnTraderChat to talk to each other. This application allows more than one trader to broadcast a message to the other traders, similar to an IRC channel. You will first look at the nonsecure version of this sample and then later update that to make it secure so no one else can eavesdrop on the conversation.

The application is simple and is implemented as a Windows application containing one form. For clarity, we will not show the Windows form boilerplate code so you can concentrate on the peer channel aspects. You can always get the latest version of the complete source code from this book's website.

Message Interface

A peer channel service contract is just a WCF service contract with one requirement that the OperationContract attribute is set up as one-way, as shown in Listing 12-1. The interface is called IQuickReturnTraderChat and has only one operation called Say, which accepts two parameters: user and message.

Example. IQuickReturnTraderChat Service Contract
[ServiceContract()]
public interface IQuickReturnTraderChat
{
    [OperationContract(IsOneWay = true)]
    void Say(string user, string message);
}

Service Configuration

Listing 12-2 shows the service side of the configuration. This application listens at the net.p2p//QuickReturnTraderChat address. Being a P2P application, the binding is set to netPeerTcpBinding, and the contract for the endpoint is set to QuickReturnTraderChat.IQuickReturnTraderChat, which follows the Namespace.Interface format. The binding configuration is intentionally kept separate (shown later in Listing 12-3).

Example. Service Configuration
<service name="QuickReturnTraderChat.Main">
    <host>
        <baseAddresses>
            <add baseAddress="net.p2p://QuickReturnTraderChat"/>
        </baseAddresses>
    </host>

    <endpoint
        name="QuickTraderChat"
        address=""
        binding="netPeerTcpBinding"
        bindingConfiguration="BindingUnsecure"
        contract="QuickReturnTraderChat.IQuickReturnTraderChat"
      />
</service>

Binding Configuration File

As we stated earlier, a P2P application's binding is set to netPeerTcpBinding and the resolver mode to Pnrp (see Listing 12-3). Since this application is not secure, we have the security mode switched off by setting this to None.

Example. Binding Configuration
<bindings>
    <netPeerTcpBinding>
        <binding  name="BindingUnsecure">
            <security mode="None"/>
            <resolver mode="Pnrp"/>
        </binding>
    </netPeerTcpBinding>
</bindings>

Main Application

The main application, as shown in Figure 12-5, consists of a Windows form that has two textboxes, one for the message being sent (called textBoxMessage) and the other to show the conversation (called textBoxChat). The form also contains one Send button (called buttonSend).

QuickReturnTraderChat application

The class implementing the Windows form is called Main and is implemented as shown in Listing 12-4. This form inherits from the .NET Form class and also implements the IQuickReturnTraderChat interface that was defined earlier. Since this is a WCF service, the class is decorated with the ServiceBehavior attribute and the InstanceContextMode controlling when a new service object should be created. In our case, we want this to behave as a Singleton; as a result, the InstanceContextMode is set to Single.

Example. Service Host Class Definition
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public partial class Main : Form, IQuickReturnTraderChat
{
}

The class Main implements, as shown in Listing 12-5, two methods called StartService and StopService, which start and stop the service host. The class Main also has a few member variables exposing the Channel, ServiceHost, and ChannelFactory.

Example. Service Host Implementation
IQuickReturnTraderChat channel;
ServiceHost host = null;
ChannelFactory<IQuickReturnTraderChat> channelFactory = null;
string userID = "";
private void StartService()
{
    //Instantiate new ServiceHost
    host = new ServiceHost(this);

     //Open ServiceHost
    host.Open();

    //Create a ChannelFactory and load the configuration setting
    channelFactory = new ChannelFactory<IQuickReturnTraderChat>
                                     ("QuickTraderChatEndpoint");
    channel = channelFactory.CreateChannel();

    //Lets others know that someone new has joined
    channel.Say("Admin", "*** New User " + userID + " Joined ****" +
                                                Environment.NewLine);
}
private void StopService()
{
     if (host != null)
     {
          channel.Say("Admin", "*** User " + userID + " Leaving ****" +
                                             Environment.NewLine);

          if (host.State != CommunicationState.Closed)
          {
               channelFactory.Close();
               host.Close();
          }
     }
}

IQuickReturnTraderChat Implementation (the Receiver)

You have both the service side and the receiver side of things in the same class. Listing 12-6 shows the configuration for the receiver, which is quite similar to the sender configuration and uses the same binding.

Example. Receiver Configuration
<client>
    <endpoint
        name="QuickTraderChatEndpoint"
        address="net.p2p://QuickReturnTraderChat"
        binding="netPeerTcpBinding"
        bindingConfiguration="BindingUnsecure"
        contract="QuickReturnTraderChat.IQuickReturnTraderChat"
</client>

The receiver here is fairly simple because all it does is echo out the message to the chat textbox on the Windows form, as shown in Listing 12-7.

Example. Receiver Implementation
void IQuickReturnTraderChat.Say(string user, string message)
{
    textBoxChat.Text += user + " says: " + message;
}

Invoking the Service

The service is invoked in the Click event of the Send button, as shown in Listing 12-8. The second line is where you invoke the service. As you might recall, the channel is of type IQuickReturnTraderChat and is defined in the class Main (shown in Listing 12-4 earlier in this chapter).

Example. Service Invocation
private void buttonSend_Click(object sender, EventArgs e)
{
    string temp = textBoxMessage.Text + Environment.NewLine;

    //Invoke the Service
    channel.Say(userID, temp);

    textBoxMessage.Clear();
}

As you can see, creating a P2P application with WCF is fairly trivial, and you do not need to do anything with the Windows P2P networking stack. Although we have kept the application QuickReturnTraderChat fairly simple to show you how to implement a P2P application, if you need to do some more advanced tasks such as cloud management, detecting and repairing network splits, and so on, then you will need to use the P2P networking stack and C++. At the time of writing this, Microsoft does not have any .NET wrappers for the P2P stack, and you would need to interop to unmanaged code.

P2P Security

Security in a P2P network is an interesting challenge. When securing a P2P network, there are two points of interest from an application point of view. First, only authorized users get on the network. Second, the message you received originated from a known and trusted source, and the message itself has not been tampered with during transmission. The first option is relatively simple to achieve: when a new application or user logs onto the mesh, they are challenged to authenticate before they are allowed to join the mesh. The second aspect is a little more difficult because you are not directly connected to another peer in the mesh. However, with WCF this is relatively straightforward because the PeerSecuritySettings class is exposed via the Security property part of the NetPeerTcpBinding class.

So, how does it all come together with WCF? For OutputChannels, which reside on the sender, each message that is sent is signed using a certificate, and all messages, before being sent to an application, are validated for this credential. The certificate that is needed is provided by using the PeerCredential.Certificate property. The validation stated earlier can be implemented via an instance of the X509CertificateValidator class, which is provided as part of PeerCredential.MessageSenderAuthentication. When the message arrives on the other end, peer channel ensures the validity of the message before forwarding it up the chain to the application.

Peer Channel Security

As mentioned earlier, you specify the security settings for peer channel using the property called Security, which is available on NetPeerTcpBinding. This property operates like any other standard binding in WCF. You can apply four types of security at this level, and they are exposed via the Mode property; the underlying class is in the PeerSecuritySettings class. These four options for security are as follows:


None:

No security is required.


Transport:

No message security is implemented; only neighbor-to-neighbor security is required.


Message:

Only message authentication is required when communicating over an open channel.


TransportWithMessageCredential:

This is essentially a combination of Transport and Message, defined previously. This would require that the message be secure and that authentication is required over secure neighbor-to-neighbor channels.

NOTE

If the security is enabled on the binding and is set to Message or TransportWithMessageCredential, then all messages that pass through both on the outbound and on the inbound need to be secured using X.509Certificate.

Peer channel provides two ways to authenticate two peers, which are configured using the PeerTransportSecurityElement.CredentialType property. This is either Password or Certificate. When this is set to Password, then every peer needs a password to connect. The owner of the mesh is responsible for setting the password initially and communicating the same to peers who you would allow to join the mesh. On the other hand, when this is set to Certificate, then authentication is based on X509Certificate.

When an application initiates a peer channel instance, an instance of the peer channel transport manager is started. The transport manager resolves the endpoint address of the requested peers and the mesh. PNRP acts as the default resolver for this; however, you can choose to implement a custom resolver as well. Once the address is resolved, the transport manager initiates a connection request to each of the peers.

Password-Based Authentication

When using the password-based authentication, the steps to initiate a connection are the same with the transport manager. The main difference is that when a peer initiates a connection request, the link between the two peers is over a SSL connection. Also, as the first step after initiating connection between the two peers, the initiator peer will send a custom handshake message that authenticates the password. If the responder peer is satisfied with this, it accepts the connection and sends a similar response to the originating peer. If the initiator peer is satisfied with this, the connection is established; if not, the connection is abandoned. This aforementioned handshake needs to contain some metadata for it to function. First, the certificate with the secure connection can be established, and second the password for the handshake can be established. The class PeerCredential is exposed as the Peer property on the ChannelFactory.Credentials property. This is demonstrated in the secure version of the chat sample that was discussed earlier in this chapter. This secure version is called QuickReturnSecureTraderChat, and you'll see it a little later in the "QuickReturnSecureTraderChat Sample" section.

Certificate-Based Authentication

When using the certificate-based authentication mode, the application has control of the authentication process as compared to the WCF runtime. There is no custom handshake involved; instead, the transport manager, after receiving the certificate, passes that on to the application to authenticate. To get this functionality, the application needs to provide a couple of certificates. The first certificate establishes the SSL connection and the identity between the peers. And, the second certificate provides a concrete implementation by the application for the X509CertificateValidator abstract class. Note, this is also demonstrated in the secure version of the chat sample a little later in the chapter.

Message Security

If you are interested in securing the message itself to ensure that it has not been tampered with during transmission, then you need to use the Message security option. Effectively, when this is requested, the peer channel on every outbound message includes a signature and, vice versa, on every inbound message validates the signature. The signature is validated against the same certificate (without the specific private keys, of course). The signatures added to the message are compatible with all the peers on the mesh.

NOTE

How can peer channel verify signatures that are application specific? Well, it can verify signatures that are specific to the application because it provides a "hook" that allows you to participate in its signature verification routine. This hook is in a concrete implementation of the abstract X509CertificateValidator class. This allows you to have any criteria for the pass or fail validation.

QuickReturnSecureTraderChat Sample

The QuickReturnSecureTraderChat application essentially is the same as the QuickReturnTraderChat sample discussed earlier in the chapter with the exception that this one uses security. For the sake of simplicity, we implemented this as a separate solution. In the real world, you would probably read the security information via a configuration setting and based on that either enable or disable the security options.

You can set security, as discussed earlier, using either a password or an X.509 certificate. For this sample, we will use a password, but you will see how easy it is to change this to use a certificate.

Service Configuration

Listing 12-9 shows the service side of the configuration, which is similar to the service configuration used in the earlier example. Although the address and the namespace have been updated, the real configuration change is using a different binding depicted by the bindingConfiguration parameter.

Example. Service Configuration
<service name="QuickReturnSecureTraderChat.Main">
    <host>
        <baseAddresses>
            <add baseAddress="net.p2p://QuickReturnSecureTraderChat"/>
        </baseAddresses>
    </host>

    <endpoint
        name="QuickTraderChatSecurePasswordEndPoint"
        address=""
        binding="netPeerTcpBinding"
        bindingConfiguration="BindingSecurePassword"
        contract="QuickReturnSecureTraderChat.IQuickReturnTraderChat"
        />
</service>

Binding Configuration

The updated binding configuration used both by the host and by the client in this example is called BindingSecurePassword. The main difference between this and the previous example is the addition of the security details, as shown in Listing 12-10. As you can see, we have the security mode set to Transport and the type to Password.

Example. Secure Binding Configuration
<binding  name="BindingSecurePassword">
    <security mode="Transport">
        <transport credentialType="Password"/>
    </security>

    <resolver mode="Pnrp"/>
</binding>

Main Application

The main application is the same as shown in Figure 12-5. The only difference between this and the earlier example is the addition of a new member variable to hold the password, which is read from the App.config file.

NOTE

It is not recommended to save the password in App.config in clear text because then anyone can open it and read the password. It is recommended to save the password in an encrypted storage or possibly accept the password from the user at runtime. To hold the password in memory, use the SecureString class, which was introduced in .NET 2.0.

Listing 12-11 shows the updated member variable used by the solution. The channel is of the type IQuickReturnTraderChat, which as you know is the contract implemented by the service. The members host and channelFactory are the service host and the channel factory, respectively. And the two string variables store the user and password that are read from the App.config file using ConfigurationManager.AppSettings in the constructor for the class Main.

Example. Member Variable List
IQuickReturnTraderChat channel;
ServiceHost host = null;
ChannelFactory<IQuickReturnTraderChat> channelFactory = null;
string userID = "";
string password = null;

The StartService method in the class Main has been updated slightly, as shown in Listing 12-12. This now uses a different endpoint configuration file and sets the password for both the host and the channel. The StopService method remains the same as earlier and is not listed again here. As you can see in the listing, the password for both the host and the ChannelFactory is set via the Credentials.Peer.MeshPassword property. The binding configuration has been updated and is read from QuickTraderChatSecurePasswordEndPoint.

Example. Service Host Implementation
private void StartService()
{
    //Instantiate new ServiceHost
    host = new ServiceHost(this);

    //Set the password
    host.Credentials.Peer.MeshPassword = password;

    //Open ServiceHost
    host.Open();

    //Create a ChannelFactory and load the configuration setting
    channelFactory = new ChannelFactory<IQuickReturnTraderChat>
                         ("QuickTraderChatSecurePasswordEndPoint");

    //Set the password for the ChannelFactory
    channelFactory.Credentials.Peer.MeshPassword = password;

    //Create the Channel
    channel = channelFactory.CreateChannel();

    //Lets others know that someone new has joined
    channel.Say("Admin", "*** New User " + userID +
                                " Joined ****" + Environment.NewLine);
}

One interesting behavior with the security is that if you have a set of peers listening on the same endpoint but with different passwords, then they will be isolated from each other. For example, say you have four users called User1, User2, User3, and User4. Say User1 and User2 are chatting and connected to the mesh using "password1." If User3 and User4 start chatting with another password, say "password2," then even though all four users are on the mesh and listening on the same endpoint, the messages between User1 and User2 cannot be seen by Users3 and User4, and vice versa.

To use an X.509 certificate instead of a password to secure a mesh, set the transport credentialType in the binding to Certificate, and set the Credentials.Peer.Certificate property to the certificate on both the host and the client.


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset