(c) BigSpeed Computing Inc. - Secure private networking

Introduction

When someone says "peer-to-peer file sharing," most people think of applications like Gnutella, Kazaa, and Morpheus. You might think of millions of people freely swapping songs, movies, and software across the Internet. But there's another side to P2P file sharing. It can be used to set up a virtual private peer-to-peer network with friends or co-workers. In this article, a simple Peer-to-Peer hub/agent is presented for secure private file sharing. The security is provided through assymetric or symmetric encryption, without need of digital certificates.

Before we start


Download and install the free edition of Peer-to-Peer SDK , which is free for non-commercial use (5 connections).



The hub



Load the sample project P2PHub.sln. The hub component can be accessed via its instance named P2PHub. Let's review its main properties and methods.



Starting the hub

In order to start accepting connections, we have to go through the following steps":

1. Setting the property ListenningPort to the number of the listening port. We take the value from the Settings form.

2. Specifing the encryption mode. There are 3 available: 2.1. Plain mode/ no encryption - Set SecurityMode to 0.
2.2. Symmetric encryption - Set SecurityMode to 1, and SecretKey to the value of the desired crypto key, which must be the same at agents side too.
2.3 Asymmetric encryption - Set SecurityMode to 2, and PublicKey/PrivateKey to the values previously returned by the method GenerateKeypair
. If we don't need encryption, we must set SecurityMode to 0.



3. Invoke the method Start().


  'Set the component properties
  Private Sub SetSettings()

    P2pHub.ListeningPort = Val(FSettings.txtPort.Text)

    If FSettings.radSecret.Checked Then
      P2pHub.SecurityMode = 1
    Else
      If FSettings.radPublic.Checked Then
        P2pHub.SecurityMode = 2
      Else
        P2pHub.SecurityMode = 0
      End If
    End If

    P2pHub.SecretKey = FSettings.txtSecret.Text

    P2pHub.PublicKey = FSettings.txtPublic.Text
    P2pHub.PrivateKey = FSettings.txtPrivate.Text
    P2pHub.Fingerprints = FSettings.txtFingerprints.Text

  End Sub


  'Start the hub
  Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.Click
    If P2pHub.Start Then
      LogMsg(("Hub is started"))
    Else
      Call MsgBox("Cannot start the hub!", "Error")
    End If

    UpdateStatus()
  End Sub



Stopping the hub

In order to stop accepting connections, we have to call the method Stop(). If we have some peers currently connected, we should remove them with the method DisconnectPeer().
  'Stop the hub
  Private Sub btnStop_Click(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles btnStop.Click
    Dim i As Integer

    P2pHub.Stop()

    For i = lvPeers.Items.Count - 1 To 0 Step -1
      P2pHub.DisconnectPeer(lvPeers.Items(i).Tag)
    Next i

    UpdateStatus()
    LogMsg("Hub stopped")
  End Sub


Adding a new connection

When a new peer is connected, the event OnPeerConnected is fired. At this point we should the client information (address/port) and store it's handle somewhere for later on use.
  'A new peer is just connected
  Private Sub P2pHub_OnPeerConnected(ByVal sender As Object, ByVal e As AxbsP2pHub.IbsP2pHubXEvents_OnPeerConnectedEvent) Handles P2pHub.OnPeerConnected
    Dim LI As ListViewItem

    LI = lvPeers.Items.Add("Not signed in")
    LI.Tag = e.aHandle
    LI.SubItems.Add(P2pHub.GetPeerAddress(e.aHandle))
    LI.SubItems.Add(Str(P2pHub.GetPeerPort(e.aHandle)))
    LI.SubItems.Add(Now.ToLongTimeString)
    LI.SubItems.Add("Connected")

    UpdateStatus()
    LogMsg(("New peer at " + LI.SubItems(1).Text + ":" + LI.SubItems(2).Text))
  End Sub




Handling the disconnected peers

When a connected peer is disconnected, the event OnPeerDisconnected is fired. At this point we should release all data associated with this peer.
  'A connection is broken
  Private Sub P2pHub_OnPeerDisconnected(ByVal sender As Object, ByVal e As AxbsP2pHub.IbsP2pHubXEvents_OnPeerDisconnectedEvent) Handles P2pHub.OnPeerDisconnected
    Dim LI As ListViewItem
    LI = ItemFromHandle(e.aHandle)
    If LI Is Nothing Then Exit Sub
    LogMsg(("Disconnected " & LI.Text & " " & P2pHub.GetPeerAddress(e.aHandle) & ":" & Str(P2pHub.GetPeerPort(e.aHandle))))
    lvPeers.Items.Remove(LI)
  End Sub




Providing the user password

When a connection is just established, the peer sends immediatelly a sign-in request. In our simple implementatipn, we accept only the default users (Guest). That's why in the handler of the event OnConnectionRequest the asked username should be always empty, and we always pass an empty string for the password value. Setting the parameter aAction to 1 enables the new connection.
  'A request from a new peer
  Private Sub P2pHub_OnConnectionRequest(ByVal sender As System.Object, ByVal e As AxbsP2pHub.IbsP2pHubXEvents_OnConnectionRequestEvent) Handles P2pHub.OnConnectionRequest
    If e.aUsername = "" Then
      'this is a Guest user, go ahead
      e.aAction = 1
    Else
      'Unknown user, we accept only guests
      e.aAction = 0
    End If
  End Sub






The agent



Load the sample project P2pAgent.sln. The client component can be accessed via its instance P2pAgent. Let's review its main properties and methods.



Connecting to the hub

In order to establish a new connection to the hub, we have to go through the following steps":

1. If we want encrypted connections, we have to set the property SecurityMode to 1 or 2 by the same way as in the hub.

2. Invoking the method OpenSession(Addr, Port, Username, Password) , passing as parameters the IP address/domain name and the listening port of the hub, and the username and the password as well.
'Connecting to the hub
Private Sub btnConnect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnConnect.Click
    If FConnect.ShowDialog <> DialogResult.OK Then Exit Sub

    If Not P2pAgent.OpenSession(FConnect.txtHubAddr.Text, Val(FConnect.txtHubPort.Text), FConnect.txtUsername.Text, FConnect.txtPassword.Text) Then
      MsgBox("Cannot initiate a new session: " + Str(P2pAgent.LastError))
    End If

    UpdateStatus()
End Sub
A successful connection will be signaled with the event OnSessionOpen.






Disconnecting from the hub

In order to end a connection to the hub, we have to call the method CloseSession().
'Disconnect from the hub
Private Sub btnDisconnect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDisconnect.Click
    P2pAgent.CloseSession()
End Sub







***** Agent - Client module





Listing folder contents

The method ListFiles() sends a requets for folder contents.
'Request the folder content
Private Sub ListFolder()
  If NowList Then Exit Sub
  If foMain.P2PAgent.ListFiles(Peer.Handle, txtFolder.Text) Then
    NowList = True
    lvFiles.Items.Clear()
    btnList.Enabled = False
    If txtFolder.Text > "\" Then
      Call HaveFileItem("..", True, 0, 0, 0, 0)
    End If
  Else
    CheckError(foMain.P2PAgent.LastError)
  End If
End Sub


The event OnHaveFileItem is fired for each available file item.
  'A file item is received
  Private Sub P2PAgent_OnHaveFileItem(ByVal sender As Object, ByVal e As AxbsP2pAgent.IbsP2pAgentXEvents_OnHaveFileItemEvent) Handles P2pAgent.OnHaveFileItem
    If Not PickPeer(e.aHandle) Then Exit Sub
    ThePeer.FFiles.HaveFileItem(e.aName, e.aFolder, e.aSizeLo, e.aSizeHi, e.aTimeLo, e.aTimeHi)
  End Sub


The end of the list is signaled with the event OnListFolderDone().
'End of the list
Public Sub ListFolderDone(ByVal aCode As Integer)
  NowList = False
  'CheckError(aCode)
  UpdateButtons()
End Sub




Creating a new folder

The method CreateFolder() sends a request to create a new folder.
'Request a new folder
Private Sub btnNew_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnNew.Click
  Dim Nm As String
  Nm = InputBox("Folder name:", "Create folder")
  If Nm = "" Then Exit Sub
  If foMain.P2PAgent.CreateFolder(Peer.Handle, AddSlash(txtFolder.Text) + Nm) Then
    NowCreate = True
  Else
    CheckError(foMain.P2PAgent.LastError)
  End If
  UpdateButtons()
End Sub


The event OnCreateFolderDone() is fired when the folder is created.
  'A new folder is created
  Public Sub CreateFolderDone(ByVal aCode As Integer)
    NowCreate = False
    CheckError((aCode))
    UpdateButtons()
    If aCode > 0 Then Exit Sub
    Call ListFolder()
  End Sub




Renaming a file

The method RenameFile() send a request to rename a file on the server.
'Send rename request
Private Sub btnRename_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnRename.Click
  Dim Nm As String

  If lvFiles.SelectedItems.Count = 0 Then Exit Sub
  If lvFiles.SelectedItems(0).ImageIndex = FolderImgIdx Then
    'it is a folder
    Nm = InputBox("New name:", "Rename folder")
    If Nm = "" Then Exit Sub
    If foMain.P2PAgent.RenameFolder(Peer.Handle, AddSlash(txtFolder.Text) + lvFiles.SelectedItems(0).Text, AddSlash(txtFolder.Text) + Nm) Then
      NowRenameFolder = True
    Else
      CheckError(foMain.P2PAgent.LastError)
    End If
  Else
    'it is a file
    Nm = InputBox("New name:", "Rename file")
    If Nm = "" Then Exit Sub
    If foMain.P2PAgent.RenameFile(Peer.Handle, AddSlash(txtFolder.Text) + lvFiles.SelectedItems(0).Text, AddSlash(txtFolder.Text) + Nm) Then
      NowRenameFile = True
    Else
      CheckError(foMain.P2PAgent.LastError)
    End If
  End If
  Call UpdateButtons()
End Sub


The event OnRenameFileDone() is fired when the confirmation is received.
  'A file is renamed
  Public Sub RenameFileDone(ByVal aCode As Integer)
    NowRenameFile = False
    CheckError((aCode))
    Call UpdateButtons()
    If aCode > 0 Then Exit Sub
    Call ListFolder()
  End Sub




Deleting a file

The method DeleteFile() sends a requests to delete a file.
'Send delete request
Private Sub btnDelete_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDelete.Click
  If lvFiles.SelectedItems.Count = 0 Then Exit Sub
  If lvFiles.SelectedItems(0).ImageIndex = FolderImgIdx Then
    'it is a folder
    If foMain.P2PAgent.DeleteFolder(Peer.Handle, AddSlash(txtFolder.Text) + lvFiles.SelectedItems(0).Text) Then
      NowDeleteFolder = True
    Else
      CheckError(foMain.P2PAgent.LastError)
    End If
  Else
    'it is a file
    If foMain.P2PAgent.DeleteFile(Peer.Handle, AddSlash(txtFolder.Text) + lvFiles.SelectedItems(0).Text) Then
      NowDeleteFile = True
    Else
      CheckError(foMain.P2PAgent.LastError)
    End If
  End If
  Call UpdateButtons()
End Sub


The event OnDeleteFileDone() is fired when the confirmation is received.
  'A file is deleted
  Public Sub DeleteFileDone(ByVal aCode As Integer)
    NowDeleteFile = False
    CheckError((aCode))
    Call UpdateButtons()
    If aCode > 0 Then Exit Sub
    Call ListFolder()
  End Sub




Downloading a file

The method Download() sends a request to download a file.
'Send a download request
Private Sub GoDownload()
  If lvFiles.SelectedItems.Count = 0 Then Exit Sub
  If lvFiles.SelectedItems(0).ImageIndex = FolderImgIdx Then Exit Sub

  dlgSave.FileName = lvFiles.SelectedItems(0).Text
  If dlgSave.ShowDialog() <> DialogResult.OK Then Exit Sub
  DnldFile = lvFiles.SelectedItems(0).Text

  If foMain.P2PAgent.Download(Peer.Handle, AddSlash(txtFolder.Text) + lvFiles.SelectedItems(0).Text, ExtractFilePath(dlgSave.FileName)) Then
    NowDownload = True
    txtDownload.Text = DnldFile + ": handshaking"
  Else
    CheckError(foMain.P2PAgent.LastError)
  End If
  UpdateStatus()
End Sub


The event OnDownloadProgress() informs about the download progress.
'New progress information is available for the download operation (client module)
  Private Sub P2PAgent_OnDownloadProgress(ByVal sender As Object, ByVal e As AxbsP2pAgent.IbsP2pAgentXEvents_OnDownloadProgressEvent) Handles P2pAgent.OnDownloadProgress
    If Not PickPeer(e.aHandle) Then Exit Sub
    ThePeer.FFiles.DownloadProgress(e.aCountLo, e.aCountHi, e.aSizeLo, e.aSizeHi)
  End Sub


The event OnDownloadDone() is fired when the download is completed.
'The download operation is completed (client module)
  Private Sub P2PAgent_OnClientDownloadDone(ByVal sender As Object, ByVal e As AxbsP2pAgent.IbsP2pAgentXEvents_OnClientDownloadDoneEvent) Handles P2pAgent.OnClientDownloadDone
    If Not PickPeer(e.aHandle) Then Exit Sub
    ThePeer.FFiles.DownloadDone(e.aCode)
  End Sub




Uploading a file

The method Upload() sends a requets to upload a file.
'Send an upload request
Private Sub btnUpload_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnUpload.Click
  On Error GoTo IsCanceled

  dlgOpen.FileName = ""
  If dlgOpen.ShowDialog() <> DialogResult.OK Then Exit Sub

  UpldFile = dlgOpen.FileName
  If foMain.P2PAgent.Upload(Peer.Handle, UpldFile, txtFolder.Text) Then
    NowUpload = True
    UpldFol = txtFolder.Text
    txtUpload.Text = ExtractFileName(UpldFile) + ": handshaking"
  Else
    CheckError(foMain.P2PAgent.LastError)
  End If
  UpdateStatus()

IsCanceled:
End Sub


The event OnUploadProgress() provides information about the upload progress.
'New progress information is available for the upload operation (client module)
  Private Sub P2PAgent_OnUploadProgress1(ByVal sender As Object, ByVal e As AxbsP2pAgent.IbsP2pAgentXEvents_OnUploadProgressEvent) Handles P2pAgent.OnUploadProgress
    If Not PickPeer(e.aHandle) Then Exit Sub
    ThePeer.FFiles.UploadProgress(e.aCountLo, e.aCountHi, e.aSizeLo, e.aSizeHi)
  End Sub
The event OnUploadDone() is fired when the upload operation is completed.
'The upload operation is completed (client module)
  Private Sub P2PAgent_OnClientUploadDone(ByVal sender As Object, ByVal e As AxbsP2pAgent.IbsP2pAgentXEvents_OnClientUploadDoneEvent) Handles P2pAgent.OnClientUploadDone
    If Not PickPeer(e.aHandle) Then Exit Sub
    ThePeer.FFiles.UploadDone(e.aCode)
  End Sub






***** Agent - Server module



Listing the folder contents

The request for folder content is received through the event OnNeedListFolder. The handler is reponsible to grant or refuse a permission for the operation. There are two input and two output parameters:

aHandle - Handle of the peer object firing the event.
aPath - Relative pathname of the folder of interest.
aOkay - Set this variable to true in order to permit the operation.
aRoot - If the operation is permitted, write here the root folder assigned to this user.


 'A request to list the folder is received
  Private Sub P2PAgent_OnNeedListFolder(ByVal sender As Object, ByVal e As AxbsP2pAgent.IbsP2pAgentXEvents_OnNeedListFolderEvent) Handles P2pAgent.OnNeedListFolder
    If Not PickPeer(e.aHandle) Then Exit Sub
    e.aOkay = True
    e.aRoot = AppPath()
    LogMsg(ThePeer.Username + ": list folder " + PreSlash(e.aPath))
  End Sub




Handling of "create folder" requests

When a "create folder" request is received from a peer, the event OnNeedCreateFolder is fired. The handler must grant or refuse a permission for the operation. The parameters are:

aHandle - Handle of the peer object firing the event.
aPath - Relative pathname of the file to be zipped.
aOkay - Set this variable to true in order to permit the operation.
aRoot - If the operation is permitted, write here the root folder assigned to this user.


'A request to create a folder is received (server module)
  Private Sub P2PAgent_OnNeedCreateFolder(ByVal sender As Object, ByVal e As AxbsP2pAgent.IbsP2pAgentXEvents_OnNeedCreateFolderEvent) Handles P2pAgent.OnNeedCreateFolder
    If Not PickPeer(e.aHandle) Then Exit Sub
    e.aOkay = True
    e.aRoot = AppPath()
    LogMsg(ThePeer.Username + ": create folder " + PreSlash(e.aPath))
  End Sub




Handling of "rename file" requests

When a "rename file" request is received from a peer, event OnNeedRenameFile is fired. The handler must grant or refuse a permission for the operation. The parameters are:

aHandle - Handle of the peer object firing the event.
aPath - Relative pathname of the file to be renamed.
aOkay - Set this variable to true in order to permit the operation.
aRoot - If the operation is permitted, write here the root folder assigned to this user.


'A request to rename a folder is received
  Private Sub P2PAgent_OnNeedRenameFolder(ByVal sender As Object, ByVal e As AxbsP2pAgent.IbsP2pAgentXEvents_OnNeedRenameFolderEvent) Handles P2pAgent.OnNeedRenameFolder
    If Not PickPeer(e.aHandle) Then Exit Sub
    e.aOkay = True
    e.aRoot = AppPath()
    LogMsg(ThePeer.Username + ": rename folder " + PreSlash(e.aPath))
  End Sub




Handling of "delete file" requests

When a "delete file" request is received from a peer, event OnNeedDeleteFile is fired. The handler must grant or refuse a permission for the operation. The parameters are:

aHandle - Handle of the peer object firing the event.
aPath - Relative pathname of the file to be deleted
aOkay - Set this variable to true in order to permit the operation.
aRoot - If the operation is permitted, write here the root folder assigned to this user.


'A request to delete a file is received (server module)
  Private Sub P2PAgent_OnNeedDeleteFile1(ByVal sender As Object, ByVal e As AxbsP2pAgent.IbsP2pAgentXEvents_OnNeedDeleteFileEvent) Handles P2pAgent.OnNeedDeleteFile
    If Not PickPeer(e.aHandle) Then Exit Sub
    e.aOkay = True
    e.aRoot = AppPath()
    LogMsg(ThePeer.Username + ": delete file " + PreSlash(e.aPath))
  End Sub




Handling of download requests

When a download request is received from a peer, event OnNeedDownload is fired. The handler must grant or refuse a permission for the operation. The parameters are:

aHandle - Handle of the peer object firing the event.
aPath - Relative pathname of the file to be downloaded.
aOkay - Set this variable to true in order to permit the operation.
aRoot - If the operation is permitted, write here the root folder assigned to this user.


'A request to download is received (server module)
  Private Sub P2PAgent_OnNeedDownload(ByVal sender As Object, ByVal e As AxbsP2pAgent.IbsP2pAgentXEvents_OnNeedDownloadEvent) Handles P2pAgent.OnNeedDownload
    If Not PickPeer(e.aHandle) Then Exit Sub
    e.aOkay = True
    e.aRoot = AppPath()
    LogMsg(ThePeer.Username + ": start downloading " + PreSlash(e.aPath))
  End Sub


The event OnServerDownloadDone informs about the end of the operation.

'The download operation is completed (server module)
Private Sub P2PAgent_OnServerDownloadDone(ByVal sender As Object, ByVal e As AxbsP2pAgent.IbsP2pAgentXEvents_OnServerDownloadDoneEvent) Handles P2pAgent.OnServerDownloadDone
  If Not PickPeer(e.aHandle) Then Exit Sub
  LogMsg(ThePeer.Username + ": finish downloading " + ErrorText(e.aCode))
End Sub




Handling of upload requests

When a upload request is received from a peer, event OnNeedUpload is fired. The handler must grant or refuse a permission for the operation. The parameters are:

aHandle - Handle of the peer object firing the event.
aPath - Relative pathname of the fer of interest.
aOkay - Set this variable to true in order to permit the operation.
aRoot - If the operation is permitted, write here the root folder assigned to this user.


'A request to upload a file is received (server module)
  Private Sub P2PAgent_OnNeedUpload(ByVal sender As Object, ByVal e As AxbsP2pAgent.IbsP2pAgentXEvents_OnNeedUploadEvent) Handles P2pAgent.OnNeedUpload
    If Not PickPeer(e.aHandle) Then Exit Sub
    e.aOkay = True
    e.aRoot = AppPath()
    LogMsg(ThePeer.Username + ": start uploading " + PreSlash(e.aPath))
  End Sub


The event OnUploadDone informs about the end of the operation.

'The upload operation is completed (server module)
  Private Sub P2PAgent_OnServerUploadDone(ByVal sender As Object, ByVal e As AxbsP2pAgent.IbsP2pAgentXEvents_OnServerUploadDoneEvent) Handles P2pAgent.OnServerUploadDone
    If Not PickPeer(e.aHandle) Then Exit Sub
    LogMsg(ThePeer.Username + ": finish uploading " + ErrorText(e.aCode))
  End Sub






The bottom line

Transferring large files over the Internet can be a frustrating task. In this article, we have presented one simple and affordable solution for private file sharing.