source: trunk/CrypP2P/Internal/P2PBase.cs @ 2540

Last change on this file since 2540 was 2540, checked in by kopal, 11 years ago

added "Remember my password" button. So the user can decide if he wants to save his password on the machine or not

File size: 23.8 KB
Line 
1/*
2   Copyright 2010 Paul Lelgemann and Christian Arnold,
3                  University of Duisburg-Essen
4
5   Licensed under the Apache License, Version 2.0 (the "License");
6   you may not use this file except in compliance with the License.
7   You may obtain a copy of the License at
8
9       http://www.apache.org/licenses/LICENSE-2.0
10
11   Unless required by applicable law or agreed to in writing, software
12   distributed under the License is distributed on an "AS IS" BASIS,
13   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   See the License for the specific language governing permissions and
15   limitations under the License.
16*/
17
18using System;
19using System.Linq;
20using System.Text;
21using System.Threading;
22using Cryptool.PluginBase;
23using Cryptool.Plugins.PeerToPeer.Internal;
24using Gears4Net;
25using PeersAtPlay;
26using PeersAtPlay.Monitoring;
27using PeersAtPlay.P2PLink;
28using PeersAtPlay.P2PLink.SnalNG;
29using PeersAtPlay.P2POverlay;
30using PeersAtPlay.P2POverlay.Bootstrapper;
31using PeersAtPlay.P2POverlay.Bootstrapper.DnsBootstrapper;
32using PeersAtPlay.P2POverlay.Bootstrapper.IrcBootstrapperV2;
33using PeersAtPlay.P2POverlay.Bootstrapper.LocalMachineBootstrapper;
34using PeersAtPlay.P2POverlay.FullMeshOverlay;
35using PeersAtPlay.P2PStorage.DHT;
36using PeersAtPlay.P2PStorage.FullMeshDHT;
37using PeersAtPlay.PapsClient;
38using PeersAtPlay.Util.Logging;
39using PeersAtPlay.P2POverlay.Chord;
40using PeersAtPlay.P2PStorage.WebDHT;
41using System.Security.Cryptography;
42
43/* TODO:
44 * - Delete UseNatTraversal-Flag and insert CertificateCheck and CertificateSetup
45 * - Testing asynchronous methods incl. EventHandlers
46 */
47
48namespace Cryptool.P2P.Internal
49{
50    /// <summary>
51    ///   Wrapper class to integrate peer@play environment into CrypTool.
52    ///   This class synchronizes asynchronous methods for easier usage in CT2.
53    /// </summary>
54    public class P2PBase
55    {
56        #region Variables
57
58        private AutoResetEvent systemJoined;
59        private readonly AutoResetEvent systemLeft;
60        private IBootstrapper bootstrapper;
61        private IP2PLinkManager linkmanager;
62        private P2POverlay overlay;
63        internal IDHT Dht;
64        internal IVersionedDHT VersionedDht;
65
66        /// <summary>
67        ///   True if system was successfully joined, false if system is COMPLETELY left
68        /// </summary>
69        public bool IsConnected { get; private set; }
70
71        /// <summary>
72        ///   True if the underlying peer to peer system has been fully initialized
73        /// </summary>
74        public bool IsInitialized { get; private set; }
75
76        #endregion
77
78        #region Delegates
79
80        public event P2PMessageReceived OnP2PMessageReceived;
81        public delegate void P2PMessageReceived(PeerId sourceAddr, byte[] data);
82
83        public event SystemJoined OnSystemJoined;
84        public delegate void SystemJoined();
85
86        public event SystemLeft OnSystemLeft;
87        public delegate void SystemLeft();
88
89        #endregion
90
91        public P2PBase()
92        {
93            IsConnected = false;
94            IsInitialized = false;
95
96            systemJoined = new AutoResetEvent(false);
97            systemLeft = new AutoResetEvent(false);
98        }
99
100        public static string Password { get; set; }
101
102        #region Basic P2P Methods (Init, Start, Stop)
103
104        /// <summary>
105        ///   Initializes the underlying peer-to-peer system with settings configured in P2PSettings. This step is required in order to be able to establish a connection.
106        /// </summary>
107        public void Initialize()
108        {
109            Scheduler scheduler = new STAScheduler("pap_snal");
110            Scheduler scheduler_2 = new STAScheduler("pap_mysql");
111
112            switch (P2PSettings.Default.LinkManager)
113            {
114                case P2PLinkManagerType.Snal:
115                    LogToMonitor("Init LinkMgr: Using NAT Traversal stuff");
116
117                    // NAT-Traversal stuff needs a different Snal-Version
118                    linkmanager = new Snal(scheduler);
119
120                    var settings = new PeersAtPlay.P2PLink.SnalNG.Settings();
121                    settings.LoadDefaults();
122                    settings.ConnectInternal = true;
123                    settings.LocalReceivingPort = P2PSettings.Default.LocalReceivingPort;
124                    settings.UseLocalAddressDetection = P2PSettings.Default.UseLocalAddressDetection;
125                    settings.NoDelay = false;
126                    settings.ReuseAddress = false;
127                    settings.UseNetworkMonitorServer = true;
128                    settings.CloseConnectionAfterPingTimeout = true;
129
130                    settings.FragmentMessages = true;
131                    settings.FragmentMessageSize = 10*1024;
132
133                    linkmanager.Settings = settings;
134                    linkmanager.ApplicationType = ApplicationType.CrypTool;
135
136                    break;
137                default:
138                    throw new NotImplementedException();
139            }
140
141            switch (P2PSettings.Default.Bootstrapper)
142            {
143                case P2PBootstrapperType.LocalMachineBootstrapper:
144                    // LocalMachineBootstrapper = only local connection (runs only on one machine)
145                    bootstrapper = new LocalMachineBootstrapper();
146                    break;
147                case P2PBootstrapperType.IrcBootstrapper:
148                    PeersAtPlay.P2POverlay.Bootstrapper.IrcBootstrapperV2.Settings.DelaySymmetricResponse = true;
149                    PeersAtPlay.P2POverlay.Bootstrapper.IrcBootstrapperV2.Settings.IncludeSymmetricResponse = false;
150                    PeersAtPlay.P2POverlay.Bootstrapper.IrcBootstrapperV2.Settings.UsePeerCache = false;
151
152                    bootstrapper = new IrcBootstrapper(scheduler);
153                    break;
154                case P2PBootstrapperType.DnsBootstrapper:
155                    bootstrapper = new DnsBootstrapper();
156                    break;
157                default:
158                    throw new NotImplementedException();
159            }
160
161            try
162            {
163                switch (P2PSettings.Default.Architecture)
164                {
165                    case P2PArchitecture.FullMesh:
166                        overlay = new FullMeshOverlay(scheduler);
167                        Dht = new FullMeshDHT(scheduler);
168                        break;
169                    case P2PArchitecture.Chord:
170                        overlay = new ChordNGCore(scheduler);
171                        Dht = (IDHT) overlay;
172                        break;
173                    case P2PArchitecture.Server:
174                        PeersAtPlay.PapsClient.Properties.Settings.Default.ServerHost = P2PSettings.Default.ServerHost;
175                        PeersAtPlay.PapsClient.Properties.Settings.Default.ServerPort = P2PSettings.Default.ServerPort;
176                        bootstrapper = new LocalMachineBootstrapper();
177                        overlay = new PapsClientOverlay();
178                        Dht = new PapsClientDht(scheduler_2);
179                        break;
180                    case P2PArchitecture.WebDHT:
181                        Dht = new WebDHT(scheduler_2);
182                        break;
183                    default:
184                        throw new NotImplementedException();
185                }
186            }
187            catch(Exception e)
188            {
189                P2PManager.GuiLogMessage("Error initializing P2P network: " + e.Message, NotificationLevel.Error);
190                return;
191            }
192
193            if (overlay != null)
194            {
195                overlay.MessageReceived += OverlayMessageReceived;
196            }
197            Dht.SystemJoined += OnDhtSystemJoined;
198            Dht.SystemLeft += OnDhtSystemLeft;
199
200            VersionedDht = (IVersionedDHT) Dht;
201
202            P2PManager.GuiLogMessage("Initializing DHT with world name " + P2PSettings.Default.WorldName,
203                                        NotificationLevel.Info);
204            IsInitialized = true;
205           
206            string password = null;
207            if(P2PSettings.Default.RememberPassword){
208                password = DecryptString(P2PSettings.Default.Password);
209            }else{
210                password = DecryptString(P2PBase.Password);               
211            }           
212
213            Dht.Initialize(P2PSettings.Default.PeerName, password, P2PSettings.Default.WorldName, overlay,
214                           bootstrapper, linkmanager, null);
215        }
216
217        /// <summary>
218        ///   Starts the P2P System. When the given P2P world doesn't exist yet,
219        ///   inclusive creating the and bootstrapping to the P2P network.
220        ///   In either case joining the P2P world.
221        ///   This synchronized method returns true not before the peer has
222        ///   successfully joined the network (this may take one or two minutes).
223        /// </summary>
224        /// <exception cref = "InvalidOperationException">When the peer-to-peer system has not been initialized.
225        /// After validating the settings, this can be done by calling Initialize().</exception>
226        /// <returns>True, if the peer has completely joined the p2p network</returns>
227        public bool SynchStart()
228        {
229            if (!IsInitialized)
230            {
231                throw new InvalidOperationException("Peer-to-peer is not initialized.");
232            }
233
234            if (IsConnected)
235            {
236                return true;
237            }
238
239            try
240            {
241                Dht.BeginStart(BeginStartEventHandler);
242
243                // Wait for event SystemJoined. When it's invoked, the peer completely joined the P2P system
244                systemJoined.WaitOne();
245                P2PManager.GuiLogMessage("System join process ended.", NotificationLevel.Debug);
246            }
247            catch (Exception e)
248            {
249                e.GetBaseException();
250            }
251
252            return true;
253        }
254
255        private void BeginStartEventHandler(DHTEventArgs eventArgs)
256        {
257            P2PManager.GuiLogMessage("Received DHTEventArgs: " + eventArgs + ", state: " + eventArgs.State, NotificationLevel.Debug);
258        }
259
260        /// <summary>
261        ///   Disconnects from the peer-to-peer system.
262        /// </summary>
263        /// <returns>True, if the peer has completely left the p2p network</returns>
264        public bool SynchStop()
265        {
266            if (Dht == null) return false;
267
268            Dht.BeginStop(null);
269
270            if (IsConnected)
271                systemLeft.WaitOne();
272
273            systemJoined.Reset();
274            systemLeft.Reset();
275
276            return true;
277        }
278
279        #endregion
280
281        #region Peer related method (GetPeerId, Send message to peer)
282
283        /// <summary>
284        ///   Get PeerName of the actual peer
285        /// </summary>
286        /// <param name = "sPeerName">out: additional peer information UserName on LinkManager</param>
287        /// <returns>PeerID as a String</returns>
288        public PeerId GetPeerId(out string sPeerName)
289        {
290            sPeerName = linkmanager.UserName;
291            return new PeerId(overlay.LocalAddress);
292        }
293
294        /// <summary>
295        ///   Construct PeerId object for a specific byte[] id
296        /// </summary>
297        /// <param name = "byteId">overlay address as byte array</param>
298        /// <returns>corresponding PeerId for given byte[] id</returns>
299        public PeerId GetPeerId(byte[] byteId)
300        {
301            LogToMonitor("GetPeerID: Converting byte[] to PeerId-Object");
302            return new PeerId(overlay.GetAddress(byteId));
303        }
304
305        // overlay.LocalAddress = Overlay-Peer-Address/Names
306        public void SendToPeer(byte[] data, byte[] destinationPeer)
307        {
308            // get stack size of the pap use-data and add own use data (for optimizing Stack size)
309            var realStackSize = overlay.GetHeaderSize() + data.Length;
310
311            var stackData = new ByteStack(realStackSize);
312            stackData.Push(data);
313
314            var destinationAddr = overlay.GetAddress(destinationPeer);
315            var overlayMsg = new OverlayMessage(MessageReceiverType.P2PBase, 
316                                                overlay.LocalAddress, destinationAddr, stackData);
317
318            overlay.Send(overlayMsg);
319        }
320
321        private void OverlayMessageReceived(object sender, OverlayMessageEventArgs e)
322        {
323            if (OnP2PMessageReceived == null) return;
324
325            var pid = new PeerId(e.Message.Source);
326            /* You have to fire this event asynchronous, because the main
327                 * thread will be stopped in this wrapper class for synchronizing
328                 * the asynchronous stuff (AutoResetEvent) --> so this could run
329                 * into a deadlock, when you fire this event synchronous (normal Invoke)
330                 * ATTENTION: This could change the invocation order!!! In my case
331                              no problem, but maybe in future cases... */
332
333            // TODO: not safe: The delegate must have only one target
334            // OnP2PMessageReceived.BeginInvoke(pid, e.Message.Data.PopBytes(e.Message.Data.CurrentStackSize), null, null);
335
336            foreach (var del in OnP2PMessageReceived.GetInvocationList())
337            {
338                var data = e.Message.Data.ToArray();
339                if (e.Message.Data.CurrentStackSize != 0)
340                    data = e.Message.Data.PopBytes(e.Message.Data.CurrentStackSize);
341
342                del.DynamicInvoke(pid, data);
343            }
344        }
345
346        #endregion
347
348        #region Event Handling (System Joined, Left and Message Received)
349
350        private void OnDhtSystemJoined(object sender, EventArgs e)
351        {
352            IsConnected = true;
353
354            if (OnSystemJoined != null)
355                OnSystemJoined();
356
357            systemJoined.Set();
358        }
359
360        private void OnDhtSystemLeft(object sender, SystemLeftEventArgs e)
361        {
362            IsConnected = false;
363            IsInitialized = false;
364            Dht.BeginStop(null);
365
366            // Allow new connection to start and check for waiting / blocked tasks
367            // TODO reset running ConnectionWorkers?
368            systemLeft.Set();
369            systemJoined.Set();
370
371            LogToMonitor("CrypP2P left the system.");
372
373            if (OnSystemLeft != null)
374                OnSystemLeft();
375        }
376
377        #endregion
378
379        #region Synchronous Methods + their Callbacks
380
381        /// <summary>
382        ///   Stores a value in the DHT at the given key
383        /// </summary>
384        /// <param name = "key">Key of DHT Entry</param>
385        /// <param name = "value">Value of DHT Entry</param>
386        /// <returns>True, when storing is completed!</returns>
387        public RequestResult SynchStore(string key, string value)
388        {
389            return SynchStore(key, Encoding.UTF8.GetBytes(value));
390        }
391
392        /// <summary>
393        ///   Stores a value in the DHT at the given key
394        /// </summary>
395        /// <param name = "key">Key of DHT Entry</param>
396        /// <param name = "data">Value of DHT Entry</param>
397        /// <returns>True, when storing is completed!</returns>
398        public RequestResult SynchStore(string key, byte[] data)
399        {
400            var autoResetEvent = new AutoResetEvent(false);
401
402            // LogToMonitor("testcrash" + Encoding.UTF8.GetString(new byte[5000]));
403            LogToMonitor("Begin: SynchStore. Key: " + key + ", " + (data != null ? data.Length : 0) + " bytes");
404
405            var requestResult = new RequestResult {WaitHandle = autoResetEvent, Key = key, Data = data};
406            VersionedDht.Store(OnSynchStoreCompleted, key, data, requestResult);
407
408            if (P2PSettings.Default.UseTimeout)
409            {
410                // blocking till response
411                bool success = autoResetEvent.WaitOne(1000*60*2);
412                if (!success)
413                {
414                    P2PManager.GuiLogMessage("Timeout in DHT Operation. Just a quick hack!", NotificationLevel.Warning);
415                    if (!IsConnected)
416                        throw new NotConnectedException();
417                    else
418                        throw new InvalidOperationException("SynchStore failed for some reason!");
419                }
420            }
421            else
422            {
423                autoResetEvent.WaitOne();
424            }
425
426            LogToMonitor("End: SynchStore. Key: " + key + ". Status: " + requestResult.Status);
427
428            return requestResult;
429        }
430
431        /// <summary>
432        ///   Callback for a the synchronized store method
433        /// </summary>
434        /// <param name = "storeResult">retrieved data container</param>
435        private static void OnSynchStoreCompleted(StoreResult storeResult)
436        {
437            var requestResult = storeResult.AsyncState as RequestResult;
438            if (requestResult == null)
439            {
440                LogToMonitor("Received OnSynchStoreCompleted, but RequestResult object is missing. Discarding.");
441                return;
442            }
443
444            requestResult.Parse(storeResult);
445
446            // unblock WaitHandle in the synchronous method
447            requestResult.WaitHandle.Set();
448        }
449
450        /// <summary>
451        ///   Get the value of the given DHT Key or null, if it doesn't exist.
452        /// </summary>
453        /// <param name = "key">Key of DHT Entry</param>
454        /// <returns>Value of DHT Entry</returns>
455        public RequestResult SynchRetrieve(string key)
456        {
457            LogToMonitor("Begin: SynchRetrieve. Key: " + key);
458
459            var autoResetEvent = new AutoResetEvent(false);
460            var requestResult = new RequestResult {WaitHandle = autoResetEvent, Key = key};
461
462            Dht.Retrieve(OnSynchRetrieveCompleted, key, requestResult);
463
464            if (P2PSettings.Default.UseTimeout)
465            {
466                // blocking till response
467                bool success = autoResetEvent.WaitOne(1000*60*2);
468                if (!success)
469                {
470                    P2PManager.GuiLogMessage("Timeout in DHT Operation. Just a quick hack!", NotificationLevel.Warning);
471                    if (!IsConnected)
472                        throw new NotConnectedException();
473                    else
474                        throw new InvalidOperationException("SynchRetrieve failed for some reason!");
475                }
476            }
477            else
478            {
479                autoResetEvent.WaitOne();
480            }
481
482            LogToMonitor("End: SynchRetrieve. Key: " + key + ". Status: " + requestResult.Status);
483
484            return requestResult;
485        }
486
487        /// <summary>
488        ///   Callback for a the synchronized retrieval method
489        /// </summary>
490        /// <param name = "retrieveResult"></param>
491        private static void OnSynchRetrieveCompleted(RetrieveResult retrieveResult)
492        {
493            var requestResult = retrieveResult.AsyncState as RequestResult;
494            if (requestResult == null)
495            {
496                LogToMonitor("Received OnSynchRetrieveCompleted, but RequestResult object is missing. Discarding.");
497                return;
498            }
499
500            requestResult.Parse(retrieveResult);
501
502            // unblock WaitHandle in the synchronous method
503            requestResult.WaitHandle.Set();
504        }
505
506        /// <summary>
507        ///   Removes a key/value pair out of the DHT
508        /// </summary>
509        /// <param name = "key">Key of the DHT Entry</param>
510        /// <returns>True, when removing is completed!</returns>
511        public RequestResult SynchRemove(string key)
512        {
513            LogToMonitor("Begin SynchRemove. Key: " + key);
514
515            var autoResetEvent = new AutoResetEvent(false);
516            var requestResult = new RequestResult { WaitHandle = autoResetEvent, Key = key };
517            VersionedDht.Remove(OnSynchRemoveCompleted, key, requestResult);
518
519            if (P2PSettings.Default.UseTimeout)
520            {
521                // blocking till response
522                bool success = autoResetEvent.WaitOne(1000*60*2);
523                if (!success)
524                {
525                    P2PManager.GuiLogMessage("Timeout in DHT Operation. Just a quick hack!", NotificationLevel.Warning);
526                    if (!IsConnected)
527                        throw new NotConnectedException();
528                    else
529                        throw new InvalidOperationException("SynchRemove failed for some reason!");
530                }
531            }
532            else
533            {
534                autoResetEvent.WaitOne();
535            }
536
537            LogToMonitor("End: SynchRemove. Key: " + key + ". Status: " + requestResult.Status);
538
539            return requestResult;
540        }
541
542        /// <summary>
543        ///   Callback for a the synchronized remove method
544        /// </summary>
545        /// <param name = "removeResult"></param>
546        private static void OnSynchRemoveCompleted(RemoveResult removeResult)
547        {
548            var requestResult = removeResult.AsyncState as RequestResult;
549            if (requestResult == null)
550            {
551                LogToMonitor("Received OnSynchRemoveCompleted, but RequestResult object is missing. Discarding.");
552                return;
553            }
554
555            requestResult.Parse(removeResult);
556
557            // unblock WaitHandle in the synchronous method
558            requestResult.WaitHandle.Set();
559        }
560
561        #endregion
562
563        #region Statistic Methods
564
565        public long TotalBytesSentOnAllLinks()
566        {
567            return (long) linkmanager.GetAllLinkInformation().Sum(linkInformation => linkInformation.TotalBytesSent);
568        }
569
570        public long TotalBytesReceivedOnAllLinks()
571        {
572            return (long) linkmanager.GetAllLinkInformation().Sum(linkInformation => linkInformation.TotalBytesReceived);
573        }
574
575        #endregion
576
577        #region Log facility
578
579        /// <summary>
580        ///   To log the internal state in the Monitoring Software of P@play
581        /// </summary>
582        public void LogInternalState()
583        {
584            if (Dht != null)
585            {
586                Dht.LogInternalState();
587            }
588        }
589
590        private static void LogToMonitor(string sTextToLog)
591        {
592            if (P2PSettings.Default.Log2Monitor)
593                Log.Debug(sTextToLog);
594        }
595
596        /// <summary>
597        /// Encrypts the given string using the current windows user password and converts
598        /// this to a base64 string
599        /// </summary>
600        /// <param name="s"></param>
601        /// <returns>encrypted base64 string</returns>
602        public static string EncryptString(string s)
603        {
604            byte[] bytes = Encoding.Unicode.GetBytes(s);
605            byte[] encBytes = ProtectedData.Protect(bytes, null, DataProtectionScope.CurrentUser);
606            return Convert.ToBase64String(encBytes);
607        }
608
609        /// <summary>
610        /// Decrypts the given base64 string using the current windows user password
611        /// </summary>
612        /// <param name="s"></param>
613        /// <returns>decrypted string</returns>
614        public static string DecryptString(string s)
615        {
616            if (string.IsNullOrEmpty(s))
617            {
618                return "";
619            }
620            try
621            {
622                byte[] encBytes = Convert.FromBase64String(s);
623                byte[] bytes = ProtectedData.Unprotect(encBytes, null, DataProtectionScope.CurrentUser);
624                return Encoding.Unicode.GetString(bytes);
625            }
626            catch (Exception)
627            {
628                return "";
629            }
630        }
631
632
633        #endregion
634    }
635}
Note: See TracBrowser for help on using the repository browser.