/ src / modules / MouseWithoutBorders / App / Class / SocketStuff.cs
SocketStuff.cs
   1  // Copyright (c) Microsoft Corporation
   2  // The Microsoft Corporation licenses this file to you under the MIT license.
   3  // See the LICENSE file in the project root for more information.
   4  
   5  using System;
   6  using System.Collections.Concurrent;
   7  using System.Collections.Generic;
   8  using System.Diagnostics;
   9  using System.Diagnostics.CodeAnalysis;
  10  using System.Globalization;
  11  using System.IO;
  12  using System.Linq;
  13  using System.Net;
  14  using System.Net.NetworkInformation;
  15  using System.Net.Sockets;
  16  using System.Security.Cryptography;
  17  using System.Threading;
  18  using System.Threading.Tasks;
  19  using System.Windows.Forms;
  20  using MouseWithoutBorders.Core;
  21  
  22  // <summary>
  23  //     Socket code.
  24  // </summary>
  25  // <history>
  26  //     2008 created by Truong Do (ductdo).
  27  //     2009-... modified by Truong Do (TruongDo).
  28  //     2023- Included in PowerToys.
  29  // </history>
  30  using MouseWithoutBorders.Exceptions;
  31  
  32  using Clipboard = MouseWithoutBorders.Core.Clipboard;
  33  using Thread = MouseWithoutBorders.Core.Thread;
  34  
  35  [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#SendData(System.Byte[])", Justification = "Dotnet port with style preservation")]
  36  [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#Close()", Justification = "Dotnet port with style preservation")]
  37  [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#CreateSocket(System.Boolean)", Justification = "Dotnet port with style preservation")]
  38  [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#SendData(System.Byte[],MouseWithoutBorders.IP)", Justification = "Dotnet port with style preservation")]
  39  [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#SendData(System.Object)", Justification = "Dotnet port with style preservation")]
  40  [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#SendFile(System.Net.Sockets.Socket,System.String)", Justification = "Dotnet port with style preservation")]
  41  [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#MainTCPRoutine(System.Net.Sockets.Socket,System.String)", Justification = "Dotnet port with style preservation")]
  42  [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#TCPServerThread(System.Object)", Justification = "Dotnet port with style preservation")]
  43  [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#SendClipboardData(System.Object)", Justification = "Dotnet port with style preservation")]
  44  [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#StartNewTcpClient(MouseWithoutBorders.MachineInf)", Justification = "Dotnet port with style preservation")]
  45  [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#StartNewTcpServer(System.Net.Sockets.Socket,System.String)", Justification = "Dotnet port with style preservation")]
  46  [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#UpdateTCPClients()", Justification = "Dotnet port with style preservation")]
  47  [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#UpdateTcpSockets(System.Net.Sockets.Socket,MouseWithoutBorders.SocketStatus)", Justification = "Dotnet port with style preservation")]
  48  [module: SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#.ctor(System.Int32,System.Boolean)", Justification = "Dotnet port with style preservation")]
  49  [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#SendData(System.Byte[],MouseWithoutBorders.IP,System.Int32)", Justification = "Dotnet port with style preservation")]
  50  [module: SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable", Scope = "type", Target = "MouseWithoutBorders.SocketStuff", Justification = "Dotnet port with style preservation")]
  51  
  52  namespace MouseWithoutBorders.Class
  53  {
  54      internal enum SocketStatus : int
  55      {
  56          NA = 0,
  57          Resolving = 1,
  58          Connecting = 2,
  59          Handshaking = 3,
  60          Error = 4,
  61          ForceClosed = 5,
  62          InvalidKey = 6,
  63          Timeout = 7,
  64          SendError = 8,
  65          Connected = 9,
  66      }
  67  
  68      internal class TcpSk : IDisposable
  69      {
  70          public TcpSk(bool isClient, Socket s, SocketStatus status, string machineName, IPAddress address = null)
  71          {
  72              IsClient = isClient;
  73              BackingSocket = s;
  74              Status = status;
  75              MachineName = machineName;
  76              Address = address;
  77              BirthTime = Common.GetTick();
  78          }
  79  
  80          public bool IsClient { get; set; }
  81  
  82          public Socket BackingSocket { get; set; }
  83  
  84          public SocketStatus Status { get; set; }
  85  
  86          public string MachineName { get; set; }
  87  
  88          public long BirthTime { get; set; }
  89  
  90          public uint MachineId { get; set; }
  91  
  92          public IPAddress Address { get; set; }
  93  
  94          private Stream encryptedStream;
  95          private Stream decryptedStream;
  96          private Stream socketStream;
  97  
  98          public Stream EncryptedStream
  99          {
 100              get
 101              {
 102                  if (encryptedStream == null && BackingSocket.Connected)
 103                  {
 104                      encryptedStream = Encryption.GetEncryptedStream(new NetworkStream(BackingSocket));
 105                      Common.SendOrReceiveARandomDataBlockPerInitialIV(encryptedStream);
 106                  }
 107  
 108                  return encryptedStream;
 109              }
 110          }
 111  
 112          public Stream DecryptedStream
 113          {
 114              get
 115              {
 116                  if (decryptedStream == null && BackingSocket.Connected)
 117                  {
 118                      decryptedStream = Encryption.GetDecryptedStream(new NetworkStream(BackingSocket));
 119                      Common.SendOrReceiveARandomDataBlockPerInitialIV(decryptedStream, false);
 120                  }
 121  
 122                  return decryptedStream;
 123              }
 124          }
 125  
 126          public Stream SocketStream
 127          {
 128              get
 129              {
 130                  if (socketStream == null && BackingSocket.Connected)
 131                  {
 132                      socketStream = new NetworkStream(BackingSocket);
 133                  }
 134  
 135                  return socketStream;
 136              }
 137          }
 138  
 139          public void Dispose()
 140          {
 141              encryptedStream?.Dispose();
 142  
 143              decryptedStream?.Dispose();
 144  
 145              socketStream?.Dispose();
 146          }
 147      }
 148  
 149      internal class SocketStuff
 150      {
 151          private readonly int bASE_PORT;
 152          private TcpServer skClipboardServer;
 153          private TcpServer skMessageServer;
 154          internal object TcpSocketsLock = new();
 155          internal static bool InvalidKeyFound;
 156          internal static bool InvalidKeyFoundOnClientSocket;
 157  
 158          internal const int CONNECT_TIMEOUT = 60000;
 159          private static readonly ConcurrentDictionary<string, int> FailedAttempt = new();
 160  
 161          internal List<TcpSk> TcpSockets
 162          {
 163              get; private set;
 164  
 165              // set { tcpSockets = value; }
 166          }
 167  
 168          internal int TcpPort
 169          {
 170              get;
 171  
 172              // set { tcpPort = value; }
 173          }
 174  
 175          private static bool firstRun;
 176          private readonly long connectTimeout;
 177          private static int restartCount;
 178  
 179          internal SocketStuff(int port, bool byUser)
 180          {
 181              Logger.LogDebug("SocketStuff started.");
 182  
 183              bASE_PORT = port;
 184              Encryption.Ran = new Random();
 185  
 186              Logger.LogDebug("Validating session...");
 187  
 188              if (Common.CurrentProcess.SessionId != NativeMethods.WTSGetActiveConsoleSessionId())
 189              {
 190                  if (Common.DesMachineID != Common.MachineID)
 191                  {
 192                      MachineStuff.SwitchToMultipleMode(false, true);
 193                  }
 194  
 195                  if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop)
 196                  {
 197                      Common.MainForm.SetTrayIconText("Not physical console session.");
 198                      if (byUser)
 199                      {
 200                          Common.ShowToolTip("Not physical console session.", 5000);
 201                      }
 202                  }
 203  
 204                  Common.MMSleep(1);
 205                  Logger.Log("Not physical console session.");
 206  
 207                  throw new NotPhysicalConsoleException("Not physical console session.");
 208              }
 209  
 210              Logger.LogDebug("Creating socket list and mutex...");
 211  
 212              try
 213              {
 214                  lock (TcpSocketsLock)
 215                  {
 216                      TcpSockets = new List<TcpSk>();
 217                  }
 218  
 219                  bool dummy1 = Setting.Values.MatrixOneRow; // Reading from reg to variable
 220                  dummy1 = Setting.Values.MatrixCircle;
 221  
 222                  if (Setting.Values.IsMyKeyRandom)
 223                  {
 224                      Setting.Values.MyKey = Encryption.MyKey;
 225                  }
 226  
 227                  Encryption.MagicNumber = Encryption.Get24BitHash(Encryption.MyKey);
 228                  Package.PackageID = Setting.Values.PackageID;
 229  
 230                  TcpPort = bASE_PORT;
 231  
 232                  if (Common.SocketMutex == null)
 233                  {
 234                      firstRun = true;
 235                      Common.SocketMutex = new Mutex(false, $"Global\\{Application.ProductName}-{FrmAbout.AssemblyVersion}-FF7CDABE-1015-0904-1103-24670FA5D16E");
 236                  }
 237  
 238                  Common.AcquireSocketMutex();
 239              }
 240              catch (AbandonedMutexException e)
 241              {
 242                  Logger.TelemetryLogTrace($"{nameof(SocketStuff)}: {e.Message}", SeverityLevel.Warning);
 243              }
 244  
 245              WinAPI.GetScreenConfig();
 246  
 247              if (firstRun && Common.RunOnScrSaverDesktop)
 248              {
 249                  firstRun = false;
 250              }
 251  
 252              // JUST_GOT_BACK_FROM_SCREEN_SAVER: For bug: monitor does not turn off after logon screen saver exits
 253              else if (!Common.RunOnScrSaverDesktop)
 254              {
 255                  if (Setting.Values.LastX == Common.JUST_GOT_BACK_FROM_SCREEN_SAVER)
 256                  {
 257                      MachineStuff.NewDesMachineID = Common.DesMachineID = Common.MachineID;
 258                  }
 259                  else
 260                  {
 261                      // Common.Log("Getting IP: " + Setting.Values.DesMachineID.ToString(CultureInfo.CurrentCulture));
 262                      Common.LastX = Setting.Values.LastX;
 263                      Common.LastY = Setting.Values.LastY;
 264  
 265                      if (Common.RunOnLogonDesktop && Setting.Values.DesMachineID == (uint)ID.ALL)
 266                      {
 267                          MachineStuff.SwitchToMultipleMode(true, false);
 268                      }
 269                      else
 270                      {
 271                          MachineStuff.SwitchToMultipleMode(false, false);
 272                      }
 273                  }
 274              }
 275  
 276              connectTimeout = Common.GetTick() + (CONNECT_TIMEOUT / 2);
 277              Exception openSocketErr;
 278  
 279              /*
 280               * The machine might be getting a new IP address from its DHCP server
 281               * for ex, when a laptop with a wireless connection just wakes up, might take long time:(
 282               * */
 283  
 284              Common.GetMachineName(); // IPs might have been changed
 285              InitAndCleanup.UpdateMachineTimeAndID();
 286  
 287              Logger.LogDebug("Creating sockets...");
 288  
 289              openSocketErr = CreateSocket();
 290  
 291              int sleepSecs = 0, errCode = 0;
 292  
 293              if (openSocketErr != null)
 294              {
 295                  if (openSocketErr is SocketException)
 296                  {
 297                      errCode = (openSocketErr as SocketException).ErrorCode;
 298  
 299                      switch (errCode)
 300                      {
 301                          case 0: // No error.
 302                              break;
 303  
 304                          case 10048: // WSAEADDRINUSE
 305                              sleepSecs = 10;
 306  
 307                              // It is reasonable to give a try on restarting MwB processes in other sessions.
 308                              if (restartCount++ < 5 && WinAPI.IsMyDesktopActive() && !Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop)
 309                              {
 310                                  Logger.TelemetryLogTrace("Restarting the service dues to WSAEADDRINUSE.", SeverityLevel.Warning);
 311                                  Program.StartService();
 312                                  InitAndCleanup.PleaseReopenSocket = InitAndCleanup.REOPEN_WHEN_WSAECONNRESET;
 313                              }
 314  
 315                              break;
 316  
 317                          case 10049: // WSAEADDRNOTAVAIL
 318                              sleepSecs = 1;
 319                              break;
 320  
 321                          default:
 322                              sleepSecs = 5;
 323                              break;
 324                      }
 325                  }
 326                  else
 327                  {
 328                      sleepSecs = 10;
 329                  }
 330  
 331                  if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop)
 332                  {
 333                      if (byUser)
 334                      {
 335                          Common.ShowToolTip(errCode.ToString(CultureInfo.CurrentCulture) + ": " + openSocketErr.Message, 5000, ToolTipIcon.Warning, Setting.Values.ShowClipNetStatus);
 336                      }
 337                  }
 338  
 339                  Common.MMSleep(sleepSecs);
 340                  Common.ReleaseSocketMutex();
 341                  throw new ExpectedSocketException(openSocketErr.Message);
 342              }
 343              else
 344              {
 345                  restartCount = 0;
 346  
 347                  if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop)
 348                  {
 349                      IpcHelper.CreateIpcServer(false);
 350                  }
 351  
 352                  Common.MainForm.UpdateNotifyIcon();
 353              }
 354          }
 355  
 356          internal void Close(bool sentWait)
 357          {
 358              try
 359              {
 360                  if (!Common.RunOnScrSaverDesktop)
 361                  {
 362                      Setting.Values.LastX = Common.LastX;
 363                      Setting.Values.LastY = Common.LastY;
 364                      Setting.Values.PackageID = Package.PackageID;
 365  
 366                      // Common.Log("Saving IP: " + Setting.Values.DesMachineID.ToString(CultureInfo.CurrentCulture));
 367                      Setting.Values.DesMachineID = (uint)Common.DesMachineID;
 368                  }
 369  
 370                  _ = Common.ExecuteAndTrace(
 371                      "Closing sockets",
 372                      () =>
 373                      {
 374                          Logger.LogDebug($"Closing socket [{skMessageServer?.Name}].");
 375                          skMessageServer?.Close(); // The original ones, not the socket instances produced by the accept() method.
 376                          skMessageServer = null;
 377  
 378                          Logger.LogDebug($"Closing socket [{skClipboardServer?.Name}].");
 379                          skClipboardServer?.Close();
 380                          skClipboardServer = null;
 381                          try
 382                          {
 383                              // If these sockets are failed to be closed then the tool would not function properly, more logs are added for debugging.
 384                              lock (TcpSocketsLock)
 385                              {
 386                                  int c = 0;
 387  
 388                                  if (TcpSockets != null)
 389                                  {
 390                                      Logger.LogDebug("********** Closing Sockets: " + TcpSockets.Count.ToString(CultureInfo.InvariantCulture));
 391  
 392                                      List<TcpSk> notClosedSockets = new();
 393  
 394                                      foreach (TcpSk t in TcpSockets)
 395                                      {
 396                                          if (t != null && t.BackingSocket != null && t.Status != SocketStatus.Resolving)
 397                                          {
 398                                              try
 399                                              {
 400                                                  t.MachineName = "$*NotUsed*$";
 401                                                  t.Status = t.Status >= 0 ? 0 : t.Status - 1;
 402  
 403                                                  if (sentWait)
 404                                                  {
 405                                                      t.BackingSocket.Close(1);
 406                                                  }
 407                                                  else
 408                                                  {
 409                                                      t.BackingSocket.Close();
 410                                                  }
 411  
 412                                                  c++;
 413  
 414                                                  continue;
 415                                              }
 416                                              catch (SocketException e)
 417                                              {
 418                                                  string log = $"Socket.Close error: {e.GetType()}/{e.Message}. This is expected when the socket is already closed by remote host.";
 419                                                  Logger.Log(log);
 420                                              }
 421                                              catch (ObjectDisposedException e)
 422                                              {
 423                                                  string log = $"Socket.Close error: {e.GetType()}/{e.Message}. This is expected when the socket is already disposed.";
 424                                                  Logger.Log(log);
 425                                              }
 426                                              catch (Exception e)
 427                                              {
 428                                                  Logger.Log(e);
 429                                              }
 430  
 431                                              // If there was an error closing the socket:
 432                                              if ((int)t.Status > -5)
 433                                              {
 434                                                  notClosedSockets.Add(t); // Try to give a few times to close the socket later on.
 435                                              }
 436                                          }
 437                                      }
 438  
 439                                      TcpSockets = notClosedSockets;
 440                                  }
 441  
 442                                  Logger.LogDebug("********** Sockets Closed: " + c.ToString(CultureInfo.CurrentCulture));
 443                              }
 444                          }
 445                          catch (Exception e)
 446                          {
 447                              Logger.Log(e);
 448                          }
 449                      },
 450                      TimeSpan.FromSeconds(3),
 451                      true);
 452              }
 453              catch (Exception e)
 454              {
 455                  Logger.Log(e);
 456              }
 457              finally
 458              {
 459                  Common.ReleaseSocketMutex();
 460              }
 461  
 462              if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop)
 463              {
 464                  try
 465                  {
 466                      IpcHelper.CreateIpcServer(true);
 467                  }
 468                  catch (Exception e)
 469                  {
 470                      Logger.Log(e);
 471                  }
 472              }
 473          }
 474  
 475          private Exception CreateSocket()
 476          {
 477              try
 478              {
 479                  skMessageServer = new TcpServer(TcpPort + 1, new ParameterizedThreadStart(TCPServerThread));
 480                  skClipboardServer = new TcpServer(TcpPort, new ParameterizedThreadStart(AcceptConnectionAndSendClipboardData));
 481              }
 482              catch (SocketException e)
 483              {
 484                  Logger.Log(e);
 485                  return e;
 486              }
 487              catch (Exception e)
 488              {
 489                  Logger.Log(e);
 490                  return e;
 491              }
 492  
 493              Logger.LogDebug("==================================================");
 494              return null;
 495          }
 496  
 497          private static int TcpSendData(TcpSk tcp, byte[] bytes)
 498          {
 499              Stream encryptedStream = tcp.EncryptedStream;
 500  
 501              if (tcp.BackingSocket == null || !tcp.BackingSocket.Connected || encryptedStream == null)
 502              {
 503                  string log = $"{nameof(TcpSendData)}: The socket is no longer connected, it could have been closed by the remote host.";
 504                  Logger.Log(log);
 505                  throw new ExpectedSocketException(log);
 506              }
 507  
 508              bytes[3] = (byte)((Encryption.MagicNumber >> 24) & 0xFF);
 509              bytes[2] = (byte)((Encryption.MagicNumber >> 16) & 0xFF);
 510              bytes[1] = 0;
 511              for (int i = 2; i < Package.PACKAGE_SIZE; i++)
 512              {
 513                  bytes[1] = (byte)(bytes[1] + bytes[i]);
 514              }
 515  
 516              try
 517              {
 518                  encryptedStream.Write(bytes, 0, bytes.Length);
 519              }
 520              catch (IOException e)
 521              {
 522                  string log = $"{nameof(TcpSendData)}: Exception writing to the socket: {tcp.MachineName}: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)";
 523                  Logger.Log(log);
 524  
 525                  throw e.InnerException is SocketException se ? new ExpectedSocketException(se) : new ExpectedSocketException(log);
 526              }
 527  
 528              return bytes.Length;
 529          }
 530  
 531          private static void ProcessReceivedDataEx(byte[] buf)
 532          {
 533              int magic;
 534              byte checksum = 0;
 535  
 536              magic = (buf[3] << 24) + (buf[2] << 16);
 537  
 538              if (magic != (Encryption.MagicNumber & 0xFFFF0000))
 539              {
 540                  Logger.Log("Magic number invalid!");
 541                  buf[0] = (byte)PackageType.Invalid;
 542              }
 543  
 544              for (int i = 2; i < Package.PACKAGE_SIZE; i++)
 545              {
 546                  checksum = (byte)(checksum + buf[i]);
 547              }
 548  
 549              if (buf[1] != checksum)
 550              {
 551                  Logger.Log("Checksum invalid!");
 552                  buf[0] = (byte)PackageType.Invalid;
 553              }
 554  
 555              buf[3] = buf[2] = buf[1] = 0;
 556          }
 557  
 558          internal static DATA TcpReceiveData(TcpSk tcp, out int bytesReceived)
 559          {
 560              byte[] buf = new byte[Package.PACKAGE_SIZE_EX];
 561              Stream decryptedStream = tcp.DecryptedStream;
 562  
 563              if (tcp.BackingSocket == null || !tcp.BackingSocket.Connected || decryptedStream == null)
 564              {
 565                  string log = $"{nameof(TcpReceiveData)}: The socket is no longer connected, it could have been closed by the remote host.";
 566                  Logger.Log(log);
 567                  throw new ExpectedSocketException(log);
 568              }
 569  
 570              DATA package;
 571  
 572              try
 573              {
 574                  bytesReceived = decryptedStream.ReadEx(buf, 0, Package.PACKAGE_SIZE);
 575  
 576                  if (bytesReceived != Package.PACKAGE_SIZE)
 577                  {
 578                      buf[0] = bytesReceived == 0 ? (byte)PackageType.Error : (byte)PackageType.Invalid;
 579                  }
 580                  else
 581                  {
 582                      ProcessReceivedDataEx(buf);
 583                  }
 584  
 585                  package = new DATA(buf);
 586  
 587                  if (package.IsBigPackage)
 588                  {
 589                      bytesReceived = decryptedStream.ReadEx(buf, Package.PACKAGE_SIZE, Package.PACKAGE_SIZE);
 590  
 591                      if (bytesReceived != Package.PACKAGE_SIZE)
 592                      {
 593                          buf[0] = bytesReceived == 0 ? (byte)PackageType.Error : (byte)PackageType.Invalid;
 594                      }
 595                      else
 596                      {
 597                          package.Bytes = buf;
 598                      }
 599                  }
 600              }
 601              catch (IOException e)
 602              {
 603                  string log = $"{nameof(TcpReceiveData)}: Exception reading from the socket: {tcp.MachineName}: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)";
 604                  Logger.Log(log);
 605  
 606                  throw e.InnerException is SocketException se ? new ExpectedSocketException(se) : new ExpectedSocketException(log);
 607              }
 608  
 609              return package;
 610          }
 611  
 612          private static void PreProcessData(PackageType type)
 613          {
 614              switch (type)
 615              {
 616                  case PackageType.Keyboard:
 617                      Package.PackageSent.Keyboard++;
 618                      break;
 619  
 620                  case PackageType.Mouse:
 621                      Package.PackageSent.Mouse++;
 622                      break;
 623  
 624                  case PackageType.Heartbeat:
 625                  case PackageType.Heartbeat_ex:
 626                      Package.PackageSent.Heartbeat++;
 627                      break;
 628  
 629                  case PackageType.Hello:
 630                      Package.PackageSent.Hello++;
 631                      break;
 632  
 633                  case PackageType.ByeBye:
 634                      Package.PackageSent.ByeBye++;
 635                      break;
 636  
 637                  case PackageType.Matrix:
 638                      Package.PackageSent.Matrix++;
 639                      break;
 640  
 641                  default:
 642                      byte subtype = (byte)((uint)type & 0x000000FF);
 643                      switch (subtype)
 644                      {
 645                          case (byte)PackageType.ClipboardText:
 646                              Package.PackageSent.ClipboardText++;
 647                              break;
 648  
 649                          case (byte)PackageType.ClipboardImage:
 650                              Package.PackageSent.ClipboardImage++;
 651                              break;
 652  
 653                          default:
 654                              // Common.Log("Send: Other type (1-17)");
 655                              break;
 656                      }
 657  
 658                      break;
 659              }
 660          }
 661  
 662          internal int TcpSend(TcpSk tcp, DATA data)
 663          {
 664              PreProcessData(data.Type);
 665  
 666              if (data.Src == ID.NONE)
 667              {
 668                  data.Src = Common.MachineID;
 669              }
 670  
 671              byte[] dataAsBytes = data.Bytes;
 672              int rv = TcpSendData(tcp, dataAsBytes);
 673              if (rv < dataAsBytes.Length)
 674              {
 675                  Logger.Log("TcpSend error! Length of sent data is unexpected.");
 676                  UpdateTcpSockets(tcp, SocketStatus.SendError);
 677                  throw new SocketException((int)SocketStatus.SendError);
 678              }
 679  
 680              return rv;
 681          }
 682  
 683          private void TCPServerThread(object param)
 684          {
 685              // SuppressFlow fixes an issue on service mode, where the helper process can't get enough permissions to be started again.
 686              // More details can be found on: https://github.com/microsoft/PowerToys/pull/36892
 687              using var asyncFlowControl = ExecutionContext.SuppressFlow();
 688  
 689              try
 690              {
 691                  TcpListener server = param as TcpListener;
 692                  do
 693                  {
 694                      Logger.LogDebug("TCPServerThread: Waiting for request...");
 695                      Socket s = server.AcceptSocket();
 696  
 697                      _ = Task.Run(() =>
 698                      {
 699                          try
 700                          {
 701                              AddSocket(s);
 702                          }
 703                          catch (Exception e)
 704                          {
 705                              Logger.Log(e);
 706                          }
 707                      });
 708                  }
 709                  while (true);
 710              }
 711              catch (InvalidOperationException e)
 712              {
 713                  string log = $"TCPServerThread.AcceptSocket: The server socket could have been closed. {e.Message}";
 714                  Logger.Log(log);
 715              }
 716              catch (SocketException e)
 717              {
 718                  if (e.ErrorCode == (int)SocketError.Interrupted)
 719                  {
 720                      Logger.Log("TCPServerThread.AcceptSocket: A blocking socket call was canceled.");
 721                  }
 722                  else
 723                  {
 724                      Logger.Log(e);
 725                  }
 726              }
 727              catch (Exception e)
 728              {
 729                  Logger.Log(e);
 730              }
 731          }
 732  
 733          private static string GetMachineNameFromSocket(Socket socket)
 734          {
 735              string stringIP = socket.RemoteEndPoint.ToString();
 736              string name = null;
 737  
 738              try
 739              {
 740                  // Remote machine has IP changed, update it.
 741                  name = Dns.GetHostEntry((socket.RemoteEndPoint as IPEndPoint).Address).HostName;
 742              }
 743              catch (SocketException e)
 744              {
 745                  Logger.Log($"{nameof(GetMachineNameFromSocket)}: {e.Message}");
 746                  return stringIP;
 747              }
 748  
 749              // Remove the domain part.
 750              if (!string.IsNullOrEmpty(name))
 751              {
 752                  int dotPos = name.IndexOf('.');
 753  
 754                  if (dotPos > 0)
 755                  {
 756                      Logger.LogDebug("Removing domain part from the full machine name: {0}.", name);
 757                      name = name[..dotPos];
 758                  }
 759              }
 760  
 761              return string.IsNullOrEmpty(name) ? stringIP : name;
 762          }
 763  
 764          private void AddSocket(Socket s)
 765          {
 766              string machineName = GetMachineNameFromSocket(s);
 767              Logger.Log($"New connection from client: [{machineName}].");
 768              TcpSk tcp = AddTcpSocket(false, s, SocketStatus.Connecting, machineName);
 769              StartNewTcpServer(tcp, machineName);
 770          }
 771  
 772          private void StartNewTcpServer(TcpSk tcp, string machineName)
 773          {
 774              void ServerThread()
 775              {
 776                  // SuppressFlow fixes an issue on service mode, where the helper process can't get enough permissions to be started again.
 777                  // More details can be found on: https://github.com/microsoft/PowerToys/pull/36892
 778                  using var asyncFlowControl = ExecutionContext.SuppressFlow();
 779  
 780                  try
 781                  {
 782                      // Receiving packages
 783                      MainTCPRoutine(tcp, machineName, false);
 784                  }
 785                  catch (Exception e)
 786                  {
 787                      Logger.Log(e);
 788                  }
 789              }
 790  
 791              Thread t = new(ServerThread, "TCP Server Thread " + tcp.BackingSocket.LocalEndPoint.ToString() + " : " + machineName);
 792  
 793              t.SetApartmentState(ApartmentState.STA);
 794              t.Start();
 795          }
 796  
 797          internal void UpdateTCPClients()
 798          {
 799              if (InvalidKeyFound)
 800              {
 801                  return;
 802              }
 803  
 804              Logger.LogDebug("!!!!! UpdateTCPClients !!!!!");
 805  
 806              try
 807              {
 808                  if (MachineStuff.MachineMatrix != null)
 809                  {
 810                      Logger.LogDebug("MachineMatrix = " + string.Join(", ", MachineStuff.MachineMatrix));
 811  
 812                      foreach (string st in MachineStuff.MachineMatrix)
 813                      {
 814                          string machineName = st.Trim();
 815                          if (!string.IsNullOrEmpty(machineName) &&
 816                              !machineName.Equals(Common.MachineName.Trim(), StringComparison.OrdinalIgnoreCase))
 817                          {
 818                              bool found = false;
 819  
 820                              found = Common.IsConnectedByAClientSocketTo(machineName);
 821  
 822                              if (found)
 823                              {
 824                                  Logger.LogDebug(machineName + " is already connected! ^^^^^^^^^^^^^^^^^^^^^");
 825                                  continue;
 826                              }
 827  
 828                              StartNewTcpClient(machineName);
 829                          }
 830                      }
 831                  }
 832              }
 833              catch (Exception e)
 834              {
 835                  Logger.Log(e);
 836              }
 837          }
 838  
 839          private static readonly Dictionary<string, List<IPAddress>> BadIPs = new();
 840          private static readonly Lock BadIPsLock = new();
 841  
 842          private static bool IsBadIP(string machineName, IPAddress ip)
 843          {
 844              lock (BadIPsLock)
 845              {
 846                  return BadIPs.ContainsKey(machineName) && BadIPs.TryGetValue(machineName, out List<IPAddress> ips) && ips.Contains(ip);
 847              }
 848          }
 849  
 850          private static void AddBadIP(string machineName, IPAddress ip)
 851          {
 852              if (!IsBadIP(machineName, ip))
 853              {
 854                  lock (BadIPsLock)
 855                  {
 856                      List<IPAddress> ips;
 857  
 858                      if (BadIPs.ContainsKey(machineName))
 859                      {
 860                          _ = BadIPs.TryGetValue(machineName, out ips);
 861                      }
 862                      else
 863                      {
 864                          ips = new List<IPAddress>();
 865                          BadIPs.Add(machineName, ips);
 866                      }
 867  
 868                      ips.Add(ip);
 869                  }
 870              }
 871          }
 872  
 873          internal static void ClearBadIPs()
 874          {
 875              lock (BadIPsLock)
 876              {
 877                  if (BadIPs.Count > 0)
 878                  {
 879                      BadIPs.Clear();
 880                  }
 881              }
 882          }
 883  
 884          internal void StartNewTcpClient(string machineName)
 885          {
 886              void ClientThread(object obj)
 887              {
 888                  // SuppressFlow fixes an issue on service mode, where the helper process can't get enough permissions to be started again.
 889                  // More details can be found on: https://github.com/microsoft/PowerToys/pull/36892
 890                  using var asyncFlowControl = ExecutionContext.SuppressFlow();
 891  
 892                  IPHostEntry host;
 893                  bool useName2IP = false;
 894                  List<IPAddress> validAddresses = new();
 895                  List<IPAddress> validatedAddresses = new();
 896                  string validAddressesSt = string.Empty;
 897  
 898                  // Add a dummy socket to show the status.
 899                  Socket dummySocket = new(AddressFamily.Unspecified, SocketType.Stream, ProtocolType.Tcp);
 900                  TcpSk dummyTcp = AddTcpSocket(true, dummySocket, SocketStatus.Resolving, machineName);
 901  
 902                  Logger.LogDebug("Connecting to: " + machineName);
 903  
 904                  if (!string.IsNullOrEmpty(Setting.Values.Name2IP))
 905                  {
 906                      string combinedName2ipList = Setting.Values.Name2IpPolicyList + Separator + Setting.Values.Name2IP;
 907                      string[] name2ip = combinedName2ipList.Split(Separator, StringSplitOptions.RemoveEmptyEntries);
 908                      string[] nameNip;
 909  
 910                      if (name2ip != null)
 911                      {
 912                          foreach (string st in name2ip)
 913                          {
 914                              nameNip = st.Split(BlankSeparator, StringSplitOptions.RemoveEmptyEntries);
 915  
 916                              if (nameNip != null && nameNip.Length >= 2 && nameNip[0].Trim().Equals(machineName, StringComparison.OrdinalIgnoreCase)
 917                                  && IPAddress.TryParse(nameNip[1].Trim(), out IPAddress ip) && !validAddressesSt.Contains("[" + ip.ToString() + "]")
 918                                  )
 919                              {
 920                                  validatedAddresses.Add(ip);
 921                                  validAddressesSt += "[" + ip.ToString() + "]";
 922                              }
 923                          }
 924                      }
 925  
 926                      if (validatedAddresses.Count > 0)
 927                      {
 928                          useName2IP = true;
 929  
 930                          Logger.LogDebug("Using both user-defined Name-to-IP mappings and DNS result for " + machineName);
 931  
 932                          Common.ShowToolTip("Using both user-defined Name-to-IP mappings and DNS result for " + machineName, 3000, ToolTipIcon.Info, false);
 933  
 934                          if (!CheckForSameSubNet(validatedAddresses, machineName))
 935                          {
 936                              return;
 937                          }
 938  
 939                          foreach (IPAddress vip in validatedAddresses)
 940                          {
 941                              StartNewTcpClientThread(machineName, vip);
 942                          }
 943  
 944                          validatedAddresses.Clear();
 945                      }
 946                  }
 947  
 948                  try
 949                  {
 950                      host = Dns.GetHostEntry(machineName);
 951                  }
 952                  catch (SocketException e)
 953                  {
 954                      host = null;
 955  
 956                      UpdateTcpSockets(dummyTcp, SocketStatus.Timeout);
 957  
 958                      Common.ShowToolTip(e.Message + ": " + machineName, 10000, ToolTipIcon.Warning, Setting.Values.ShowClipNetStatus);
 959  
 960                      Logger.Log($"{nameof(StartNewTcpClient)}.{nameof(Dns.GetHostEntry)}: {e.Message}");
 961                  }
 962  
 963                  UpdateTcpSockets(dummyTcp, SocketStatus.NA);
 964  
 965                  if (!MachineStuff.InMachineMatrix(machineName))
 966                  {
 967                      // While Resolving from name to IP, user may have changed the machine name and clicked on Apply.
 968                      return;
 969                  }
 970  
 971                  if (host != null)
 972                  {
 973                      string ipLog = string.Empty;
 974  
 975                      foreach (IPAddress ip in host.AddressList)
 976                      {
 977                          ipLog += "<" + ip.ToString() + ">";
 978  
 979                          if ((ip.AddressFamily == AddressFamily.InterNetwork || ip.AddressFamily == AddressFamily.InterNetworkV6) && !validAddressesSt.Contains("[" + ip.ToString() + "]"))
 980                          {
 981                              validAddresses.Add(ip);
 982                              validAddressesSt += "[" + ip.ToString() + "]";
 983                          }
 984                      }
 985  
 986                      Logger.LogDebug(machineName + ipLog);
 987                  }
 988  
 989                  if (validAddresses.Count > 0)
 990                  {
 991                      if (!Setting.Values.ReverseLookup)
 992                      {
 993                          validatedAddresses = validAddresses;
 994                          ClearBadIPs();
 995                      }
 996                      else
 997                      {
 998                          foreach (IPAddress ip in validAddresses)
 999                          {
1000                              if (IsBadIP(machineName, ip))
1001                              {
1002                                  Logger.Log($"Skip bad IP address: {ip}");
1003                                  continue;
1004                              }
1005  
1006                              try
1007                              {
1008                                  // Reverse lookup to validate the IP Address.
1009                                  string hn = Dns.GetHostEntry(ip).HostName;
1010  
1011                                  if (hn.StartsWith(machineName, StringComparison.CurrentCultureIgnoreCase) || hn.Equals(ip.ToString(), StringComparison.OrdinalIgnoreCase))
1012                                  {
1013                                      validatedAddresses.Add(ip);
1014                                  }
1015                                  else
1016                                  {
1017                                      Logger.Log($"DNS information of machine not matched: {machineName} => {ip} => {hn}.");
1018                                      AddBadIP(machineName, ip);
1019                                  }
1020                              }
1021                              catch (SocketException se)
1022                              {
1023                                  Logger.Log($"{nameof(StartNewTcpClient)}: DNS information of machine not matched: {machineName} => {ip} => {se.Message}.");
1024                                  AddBadIP(machineName, ip);
1025                              }
1026                              catch (ArgumentException ae)
1027                              {
1028                                  Logger.Log($"{nameof(StartNewTcpClient)}: DNS information of machine not matched: {machineName} => {ip} => {ae.Message}.");
1029                                  AddBadIP(machineName, ip);
1030                              }
1031                          }
1032                      }
1033                  }
1034  
1035                  if (validatedAddresses.Count > 0)
1036                  {
1037                      if (!CheckForSameSubNet(validatedAddresses, machineName))
1038                      {
1039                          return;
1040                      }
1041  
1042                      foreach (IPAddress ip in validatedAddresses)
1043                      {
1044                          StartNewTcpClientThread(machineName, ip);
1045                      }
1046                  }
1047                  else
1048                  {
1049                      Logger.Log("Cannot resolve IPv4 Addresses of machine: " + machineName);
1050  
1051                      if (!useName2IP)
1052                      {
1053                          Common.ShowToolTip($"Cannot resolve IP Address of the remote machine: {machineName}.\r\nPlease fix your DNS or use the Mapping option in the Settings form.", 10000, ToolTipIcon.Warning, Setting.Values.ShowClipNetStatus);
1054                      }
1055                  }
1056              }
1057  
1058              Thread t = new(
1059                  ClientThread, "StartNewTcpClient." + machineName);
1060  
1061              t.SetApartmentState(ApartmentState.STA);
1062              t.Start();
1063          }
1064  
1065          private bool CheckForSameSubNet(List<IPAddress> validatedAddresses, string machineName)
1066          {
1067              if (!Setting.Values.SameSubNetOnly)
1068              {
1069                  return true;
1070              }
1071  
1072              // Only support if IPv4 addresses found in both.
1073              IEnumerable<IPAddress> remoteIPv4Addresses = validatedAddresses.Where(addr => addr?.AddressFamily == AddressFamily.InterNetwork);
1074  
1075              if (!remoteIPv4Addresses.Any())
1076              {
1077                  Logger.Log($"No IPv4 resolved from the remote machine: {machineName}.");
1078                  return true;
1079              }
1080  
1081              List<IPAddress> localIPv4Addresses = GetMyIPv4Addresses().ToList();
1082  
1083              if (localIPv4Addresses.Count == 0)
1084              {
1085                  Logger.Log($"No IPv4 resolved from the local machine: {Common.MachineName}");
1086                  return true;
1087              }
1088  
1089              foreach (IPAddress remote in remoteIPv4Addresses)
1090              {
1091                  foreach (IPAddress local in localIPv4Addresses)
1092                  {
1093                      byte[] myIPAddressBytes = local.GetAddressBytes();
1094                      byte[] yourIPAddressBytes = remote.GetAddressBytes();
1095  
1096                      // Same WAN?
1097                      if (myIPAddressBytes[0] == yourIPAddressBytes[0] && myIPAddressBytes[1] == yourIPAddressBytes[1])
1098                      {
1099                          return true;
1100                      }
1101                  }
1102              }
1103  
1104              Logger.Log($"Skip machine not in the same network: {machineName}.");
1105  
1106              return false;
1107          }
1108  
1109          private IEnumerable<IPAddress> GetMyIPv4Addresses()
1110          {
1111              try
1112              {
1113                  IEnumerable<IPAddress> ip4addresses = NetworkInterface.GetAllNetworkInterfaces()?
1114                      .Where(networkInterface =>
1115                          (networkInterface.NetworkInterfaceType == NetworkInterfaceType.Ethernet || networkInterface.NetworkInterfaceType == NetworkInterfaceType.Wireless80211)
1116                          && networkInterface.OperationalStatus == OperationalStatus.Up)
1117                          .SelectMany(ni => ni?.GetIPProperties()?.UnicastAddresses.Select(uni => uni?.Address))
1118                          .Where(addr => addr?.AddressFamily == AddressFamily.InterNetwork);
1119  
1120                  return ip4addresses;
1121              }
1122              catch (Exception e)
1123              {
1124                  Logger.Log(e);
1125                  return Enumerable.Empty<IPAddress>();
1126              }
1127          }
1128  
1129          private void StartNewTcpClientThread(string machineName, IPAddress ip)
1130          {
1131              void NewTcpClient()
1132              {
1133                  // SuppressFlow fixes an issue on service mode, where the helper process can't get enough permissions to be started again.
1134                  // More details can be found on: https://github.com/microsoft/PowerToys/pull/36892
1135                  using var asyncFlowControl = ExecutionContext.SuppressFlow();
1136  
1137                  TcpClient tcpClient = null;
1138  
1139                  try
1140                  {
1141                      tcpClient = new TcpClient(AddressFamily.InterNetworkV6);
1142                      tcpClient.Client.DualMode = true;
1143  
1144                      if (Common.IsConnectedByAClientSocketTo(machineName))
1145                      {
1146                          Logger.LogDebug(machineName + " is already connected by another client socket.");
1147                          return;
1148                      }
1149  
1150                      if (Common.IsConnectingByAClientSocketTo(machineName, ip))
1151                      {
1152                          Logger.LogDebug($"{machineName}:{ip} is already being connected by another client socket.");
1153                          return;
1154                      }
1155  
1156                      TcpSk tcp = AddTcpSocket(true, tcpClient.Client, SocketStatus.Connecting, machineName, ip);
1157  
1158                      // Update the other server socket's machine name based on this corresponding client socket.
1159                      UpdateTcpSockets(tcp, SocketStatus.Connecting);
1160  
1161                      Logger.LogDebug(string.Format(CultureInfo.CurrentCulture, "=====> Connecting to: {0}:{1}", machineName, ip.ToString()));
1162  
1163                      long timeoutLeft;
1164  
1165                      do
1166                      {
1167                          try
1168                          {
1169                              tcpClient.Connect(ip, TcpPort + 1);
1170                          }
1171                          catch (ObjectDisposedException)
1172                          {
1173                              // When user reconnects.
1174                              Logger.LogDebug($"tcpClient.Connect: The socket has already been disposed: {machineName}:{ip}");
1175                              return;
1176                          }
1177                          catch (SocketException e)
1178                          {
1179                              timeoutLeft = connectTimeout - Common.GetTick();
1180  
1181                              if (timeoutLeft > 0)
1182                              {
1183                                  Logger.LogDebug($"tcpClient.Connect: {timeoutLeft}: {e.Message}");
1184                                  Thread.Sleep(1000);
1185                                  continue;
1186                              }
1187                              else
1188                              {
1189                                  Logger.Log($"tcpClient.Connect: Unable to connect after a timeout: {machineName}:{ip} : {e.Message}");
1190  
1191                                  string message = $"Connection timed out: {machineName}:{ip}";
1192  
1193                                  Common.ShowToolTip(message, 5000, ToolTipIcon.Warning, Setting.Values.ShowClipNetStatus);
1194  
1195                                  UpdateTcpSockets(tcp, SocketStatus.Timeout);
1196                                  return;
1197                              }
1198                          }
1199  
1200                          break;
1201                      }
1202                      while (true);
1203  
1204                      Logger.LogDebug($"=====> Connected: {tcpClient.Client.LocalEndPoint} => {machineName}: {ip}");
1205  
1206                      // Sending/Receiving packages
1207                      MainTCPRoutine(tcp, machineName, true);
1208                  }
1209                  catch (ObjectDisposedException e)
1210                  {
1211                      Logger.Log($"{nameof(StartNewTcpClientThread)}: The socket could have been closed/disposed due to machine switch: {e.Message}");
1212                  }
1213                  catch (SocketException e)
1214                  {
1215                      // DHCP error, etc.
1216                      string localIP = tcpClient?.Client?.LocalEndPoint?.ToString();
1217  
1218                      if (localIP != null && (localIP.StartsWith("169.254", StringComparison.InvariantCulture) || localIP.ToString().StartsWith("0.0", StringComparison.InvariantCulture)))
1219                      {
1220                          Common.ShowToolTip($"Error: The machine has limited connectivity on [{localIP}].", 5000, ToolTipIcon.Warning, Setting.Values.ShowClipNetStatus);
1221                      }
1222                      else
1223                      {
1224                          Logger.TelemetryLogTrace($"{nameof(StartNewTcpClientThread)}: Error: {e.Message} on the IP Address: {localIP}", SeverityLevel.Error);
1225                      }
1226                  }
1227                  catch (Exception e)
1228                  {
1229                      Logger.Log(e);
1230                  }
1231              }
1232  
1233              Thread t = new(NewTcpClient, "TCP Client Thread " + machineName + " " + ip.ToString());
1234  
1235              t.SetApartmentState(ApartmentState.STA);
1236              t.Start();
1237          }
1238  
1239          private void FlagReopenSocketIfNeeded(Exception e)
1240          {
1241              /* SCENARIO: MachineA has MM blocked by firewall but MachineA can connect to MachineB so the tool would work normally (MM is not blocked by firewall in MachineB).
1242              * 1. a connection from A to B is working. Mouse/Keyboard is connected to A.
1243              * 2. User moves Mouse to B and lock B by Ctrl+Alt+L.
1244              * 3. B closes all sockets before switches to logon desktop. The client socket in A gets reset by B (the only connection between A and B).
1245              * 4. B is now on the logon desktop and tries to connect to A, connection fails since it is block by firewall in A.
1246              * 5. When the client socket gets reset in A, it should retry to connect to B => this is the fix implemented by few lines of code below.
1247              * */
1248  
1249              // WSAECONNRESET
1250              if (e is ExpectedSocketException se && se.ShouldReconnect)
1251              {
1252                  InitAndCleanup.PleaseReopenSocket = InitAndCleanup.REOPEN_WHEN_WSAECONNRESET;
1253                  Logger.Log($"MainTCPRoutine: {nameof(FlagReopenSocketIfNeeded)}");
1254              }
1255          }
1256  
1257          private long lastRemoteMachineID;
1258          internal static readonly string[] Separator = new string[] { "\r\n" };
1259          internal static readonly char[] BlankSeparator = new char[] { ' ' };
1260  
1261          private void MainTCPRoutine(TcpSk tcp, string machineName, bool isClient)
1262          {
1263              int packageCount = 0;
1264              DATA d;
1265              string remoteMachine = string.Empty;
1266              string strIP = string.Empty;
1267              ID remoteID = ID.NONE;
1268  
1269              byte[] buf = RandomNumberGenerator.GetBytes(Package.PACKAGE_SIZE_EX);
1270              d = new DATA(buf);
1271  
1272              TcpSk currentTcp = tcp;
1273              Socket currentSocket = currentTcp.BackingSocket;
1274  
1275              if (currentSocket == null)
1276              {
1277                  Logger.LogDebug($"{nameof(MainTCPRoutine)}: The socket could have been closed/disposed by other threads.");
1278                  return;
1279              }
1280  
1281              try
1282              {
1283                  currentSocket.SendBufferSize = Package.PACKAGE_SIZE * 10000;
1284                  currentSocket.ReceiveBufferSize = Package.PACKAGE_SIZE * 10000;
1285                  currentSocket.NoDelay = true; // This is very interesting to know:(
1286                  currentSocket.SendTimeout = 500;
1287                  d.MachineName = Common.MachineName;
1288  
1289                  d.Type = PackageType.Handshake;
1290  
1291                  for (int i = 0; i < 10; i++)
1292                  {
1293                      _ = TcpSend(currentTcp, d);
1294                  }
1295  
1296                  d.Machine1 = ~d.Machine1;
1297                  d.Machine2 = ~d.Machine2;
1298                  d.Machine3 = ~d.Machine3;
1299                  d.Machine4 = ~d.Machine4;
1300  
1301                  UpdateTcpSockets(currentTcp, SocketStatus.Handshaking);
1302  
1303                  strIP = Common.GetRemoteStringIP(currentSocket, true);
1304                  remoteMachine = string.IsNullOrEmpty(machineName) ? GetMachineNameFromSocket(currentSocket) : machineName;
1305  
1306                  Logger.LogDebug($"MainTCPRoutine: Remote machineName/IP = {remoteMachine}/{strIP}");
1307              }
1308              catch (ObjectDisposedException e)
1309              {
1310                  InitAndCleanup.PleaseReopenSocket = InitAndCleanup.REOPEN_WHEN_WSAECONNRESET;
1311                  UpdateTcpSockets(currentTcp, SocketStatus.ForceClosed);
1312                  currentSocket.Close();
1313                  Logger.Log($"{nameof(MainTCPRoutine)}: The socket could have been closed/disposed by other threads: {e.Message}");
1314              }
1315              catch (Exception e)
1316              {
1317                  UpdateTcpSockets(currentTcp, SocketStatus.ForceClosed);
1318                  FlagReopenSocketIfNeeded(e);
1319                  currentSocket.Close();
1320                  Logger.Log(e);
1321              }
1322  
1323              int errCount = 0;
1324  
1325              while (true)
1326              {
1327                  try
1328                  {
1329                      DATA package = TcpReceiveData(currentTcp, out int receivedCount);
1330                      remoteID = package.Src;
1331  
1332                      if (package.Type == PackageType.Error)
1333                      {
1334                          errCount++;
1335  
1336                          string log = $"{nameof(MainTCPRoutine)}.TcpReceive error, invalid package from {remoteMachine}: {receivedCount}";
1337                          Logger.Log(log);
1338  
1339                          if (receivedCount > 0)
1340                          {
1341                              Common.ShowToolTip($"Invalid package from {remoteMachine}. Ensure the security keys are the same in both machines.", 5000, ToolTipIcon.Warning, false);
1342                          }
1343  
1344                          if (errCount > 5)
1345                          {
1346                              Common.MMSleep(1);
1347  
1348                              UpdateTcpSockets(currentTcp, SocketStatus.Error);
1349                              currentSocket.Close();
1350  
1351                              /*
1352                               * Sometimes when the peer machine closes the connection, we do not actually get an exception.
1353                               * Socket status is still connected and a read on the socket stream returns 0 byte.
1354                               * In this case, we should give ONE try to reconnect.
1355                               */
1356  
1357                              if (InitAndCleanup.ReopenSocketDueToReadError)
1358                              {
1359                                  InitAndCleanup.PleaseReopenSocket = InitAndCleanup.REOPEN_WHEN_WSAECONNRESET;
1360                                  InitAndCleanup.ReopenSocketDueToReadError = false;
1361                              }
1362  
1363                              break;
1364                          }
1365                      }
1366                      else
1367                      {
1368                          errCount = 0;
1369                      }
1370  
1371                      if (package.Type == PackageType.Handshake)
1372                      {
1373                          // Common.Log("Got a Handshake signal!");
1374                          package.Type = PackageType.HandshakeAck;
1375                          package.Src = ID.NONE;
1376                          package.MachineName = Common.MachineName;
1377  
1378                          package.Machine1 = ~package.Machine1;
1379                          package.Machine2 = ~package.Machine2;
1380                          package.Machine3 = ~package.Machine3;
1381                          package.Machine4 = ~package.Machine4;
1382  
1383                          _ = TcpSend(currentTcp, package);
1384                      }
1385                      else
1386                      {
1387                          if (packageCount >= 0)
1388                          {
1389                              if (++packageCount >= 10)
1390                              {
1391                                  // Common.ShowToolTip("Invalid Security Key from " + remoteMachine, 5000);
1392                                  Logger.Log("More than 10 invalid packages received!");
1393  
1394                                  package.Type = PackageType.Invalid;
1395  
1396                                  for (int i = 0; i < 10; i++)
1397                                  {
1398                                      _ = TcpSend(currentTcp, package);
1399                                  }
1400  
1401                                  Common.MMSleep(2);
1402  
1403                                  UpdateTcpSockets(currentTcp, SocketStatus.InvalidKey);
1404                                  currentSocket.Close();
1405                                  break;
1406                              }
1407                              else if (package.Type == PackageType.HandshakeAck)
1408                              {
1409                                  if (package.Machine1 == d.Machine1 && package.Machine2 == d.Machine2 &&
1410                                     package.Machine3 == d.Machine3 && package.Machine4 == d.Machine4)
1411                                  {
1412                                      string claimedMachineName = package.MachineName;
1413  
1414                                      if (!remoteMachine.Equals(claimedMachineName, StringComparison.Ordinal))
1415                                      {
1416                                          Logger.LogDebug($"DNS.RemoteMachineName({remoteMachine}) <> Claimed.MachineName({claimedMachineName}), using the claimed machine name.");
1417                                          remoteMachine = claimedMachineName;
1418                                          currentTcp.MachineName = remoteMachine;
1419                                      }
1420  
1421                                      // Double check to avoid a redundant client socket.
1422                                      if (isClient && Common.IsConnectedByAClientSocketTo(remoteMachine))
1423                                      {
1424                                          Logger.LogDebug("=====> Duplicate connected client socket for: " + remoteMachine + ":" + strIP + " is being removed.");
1425                                          UpdateTcpSockets(currentTcp, SocketStatus.ForceClosed);
1426                                          currentSocket.Close();
1427                                          return;
1428                                      }
1429  
1430                                      if (remoteMachine.Equals(Common.MachineName, StringComparison.OrdinalIgnoreCase))
1431                                      {
1432                                          Logger.LogDebug("Connected to/from local socket: " + strIP + (isClient ? "-Client" : "-Server"));
1433                                          UpdateTcpSockets(currentTcp, SocketStatus.NA);
1434                                          Common.MMSleep(1);
1435                                          currentSocket.Close();
1436                                          return;
1437                                      }
1438  
1439                                      packageCount = -1; // Trusted
1440                                      InvalidKeyFound = false;
1441                                      currentTcp.MachineId = (uint)remoteID;
1442                                      currentTcp.Status = SocketStatus.Connected;
1443                                      UpdateTcpSockets(currentTcp, SocketStatus.Connected);
1444                                      Logger.LogDebug("))))))))))))))) Machine got trusted: " + remoteMachine + ":" + strIP + ", Is client: " + isClient);
1445  
1446                                      if (Math.Abs(Common.GetTick() - Common.LastReconnectByHotKeyTime) < 5000)
1447                                      {
1448                                          Common.ShowToolTip("Connected to " + remoteMachine, 1000, ToolTipIcon.Info, Setting.Values.ShowClipNetStatus);
1449                                      }
1450  
1451                                      Common.SendHeartBeat(initial: true);
1452  
1453                                      if (MachineStuff.MachinePool.TryFindMachineByName(remoteMachine, out MachineInf machineInfo))
1454                                      {
1455                                          Logger.LogDebug("Machine updated: " + remoteMachine + "/" + remoteID.ToString());
1456  
1457                                          if (machineInfo.Name.Equals(MachineStuff.DesMachineName, StringComparison.OrdinalIgnoreCase))
1458                                          {
1459                                              Logger.LogDebug("Des ID updated: " + Common.DesMachineID.ToString() +
1460                                                  "/" + remoteID.ToString());
1461                                              MachineStuff.NewDesMachineID = Common.DesMachineID = remoteID;
1462                                          }
1463  
1464                                          _ = MachineStuff.MachinePool.TryUpdateMachineID(remoteMachine, remoteID, true);
1465                                          MachineStuff.UpdateMachinePoolStringSetting();
1466                                      }
1467                                      else
1468                                      {
1469                                          Logger.LogDebug("New machine connected: {0}.", remoteMachine);
1470  
1471                                          if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop)
1472                                          {
1473                                              Common.ShowToolTip("Connected to new machine " + remoteMachine, 1000, ToolTipIcon.Info, Setting.Values.ShowClipNetStatus);
1474                                          }
1475                                      }
1476  
1477                                      if (!isClient)
1478                                      {
1479                                          MachineStuff.UpdateClientSockets("MainTCPRoutine");
1480                                      }
1481                                  }
1482                                  else
1483                                  {
1484                                      Logger.LogDebug("Invalid ACK from " + remoteMachine);
1485                                      UpdateTcpSockets(currentTcp, SocketStatus.InvalidKey);
1486  
1487                                      string remoteEP = currentSocket.RemoteEndPoint.ToString();
1488  
1489                                      if (FailedAttempt.AddOrUpdate(remoteEP, 1, (key, value) => value + 1) > 10)
1490                                      {
1491                                          _ = FailedAttempt.AddOrUpdate(remoteEP, 0, (key, value) => 0);
1492  
1493                                          _ = MessageBox.Show($"Too many connection attempts from [{remoteEP}]!\r\nRestart the app to retry.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
1494                                          Common.MainForm.Quit(true, false);
1495                                      }
1496  
1497                                      currentSocket.Close();
1498                                      break;
1499                                  }
1500                              }
1501                              else if (package.Type == PackageType.Mouse)
1502                              {
1503                                  if (packageCount > 5)
1504                                  {
1505                                      packageCount--;
1506                                  }
1507                              }
1508                              else if (package.Type is PackageType.Heartbeat or PackageType.Heartbeat_ex)
1509                              {
1510                                  if (packageCount > 5)
1511                                  {
1512                                      packageCount--;
1513                                  }
1514                              }
1515                              else
1516                              {
1517                                  if (packageCount > 5)
1518                                  {
1519                                      UpdateTcpSockets(currentTcp, SocketStatus.InvalidKey);
1520                                  }
1521                                  else
1522                                  {
1523                                      Logger.Log(string.Format(
1524                                          CultureInfo.CurrentCulture,
1525                                          "Unexpected package, size = {0}, type = {1}",
1526                                          receivedCount,
1527                                          package.Type));
1528                                  }
1529                              }
1530                          }
1531                          else if (receivedCount > 0)
1532                          {
1533                              // Add some log when remote machine switches.
1534                              if (lastRemoteMachineID != (long)remoteID)
1535                              {
1536                                  _ = Interlocked.Exchange(ref lastRemoteMachineID, (long)remoteID);
1537                                  Logger.LogDebug($"MainTCPRoutine: Remote machine = {strIP}/{lastRemoteMachineID}");
1538                              }
1539  
1540                              if (package.Type == PackageType.HandshakeAck)
1541                              {
1542                                  Logger.LogDebug("Skipping the rest of the Handshake packages.");
1543                              }
1544                              else
1545                              {
1546                                  Receiver.ProcessPackage(package, currentTcp);
1547                              }
1548                          }
1549                      }
1550                  }
1551                  catch (Exception e)
1552                  {
1553                      UpdateTcpSockets(currentTcp, SocketStatus.Error);
1554                      FlagReopenSocketIfNeeded(e);
1555                      currentSocket.Close();
1556                      Logger.Log(e);
1557                      break;
1558                  }
1559              }
1560  
1561              if (remoteID != ID.NONE)
1562              {
1563                  _ = MachineStuff.RemoveDeadMachines(remoteID);
1564              }
1565          }
1566  
1567          private static void AcceptConnectionAndSendClipboardData(object param)
1568          {
1569              // SuppressFlow fixes an issue on service mode, where the helper process can't get enough permissions to be started again.
1570              // More details can be found on: https://github.com/microsoft/PowerToys/pull/36892
1571              using var asyncFlowControl = ExecutionContext.SuppressFlow();
1572  
1573              TcpListener server = param as TcpListener;
1574  
1575              do
1576              {
1577                  Logger.LogDebug("SendClipboardData: Waiting for request...");
1578                  Socket s = null;
1579  
1580                  try
1581                  {
1582                      s = server.AcceptSocket();
1583                  }
1584                  catch (InvalidOperationException e)
1585                  {
1586                      Logger.Log($"The clipboard socket could have been closed. {e.Message}");
1587                      break;
1588                  }
1589                  catch (SocketException e)
1590                  {
1591                      if (e.ErrorCode == (int)SocketError.Interrupted)
1592                      {
1593                          Logger.Log("server.AcceptSocket: A blocking socket call was canceled.");
1594                          continue;
1595                      }
1596                      else
1597                      {
1598                          Logger.Log(e);
1599                          break;
1600                      }
1601                  }
1602                  catch (Exception e)
1603                  {
1604                      Logger.Log(e);
1605                      break;
1606                  }
1607  
1608                  if (s != null)
1609                  {
1610                      try
1611                      {
1612                          new Task(() =>
1613                          {
1614                              // SuppressFlow fixes an issue on service mode, where the helper process can't get enough permissions to be started again.
1615                              // More details can be found on: https://github.com/microsoft/PowerToys/pull/36892
1616                              using var asyncFlowControl = ExecutionContext.SuppressFlow();
1617  
1618                              System.Threading.Thread thread = Thread.CurrentThread;
1619                              thread.Name = $"{nameof(SendOrReceiveClipboardData)}.{thread.ManagedThreadId}";
1620                              Thread.UpdateThreads(thread);
1621                              SendOrReceiveClipboardData(s);
1622                          }).Start();
1623                      }
1624                      catch (Exception e)
1625                      {
1626                          Logger.Log(e);
1627                      }
1628                  }
1629              }
1630              while (true);
1631          }
1632  
1633          internal static void SendOrReceiveClipboardData(Socket s)
1634          {
1635              try
1636              {
1637                  string remoteEndPoint = s.RemoteEndPoint.ToString();
1638                  Logger.LogDebug("SendClipboardData: Request accepted: " + s.LocalEndPoint.ToString() + "/" + remoteEndPoint);
1639                  DragDrop.IsDropping = false;
1640                  DragDrop.IsDragging = false;
1641                  DragDrop.DragMachine = (ID)1;
1642  
1643                  bool clientPushData = true;
1644                  ClipboardPostAction postAction = ClipboardPostAction.Other;
1645                  bool handShaken = Clipboard.ShakeHand(ref remoteEndPoint, s, out Stream enStream, out Stream deStream, ref clientPushData, ref postAction);
1646  
1647                  if (!handShaken)
1648                  {
1649                      s.Close();
1650                      return;
1651                  }
1652                  else
1653                  {
1654                      Logger.LogDebug($"{nameof(SendOrReceiveClipboardData)}: Clipboard connection accepted: " + remoteEndPoint);
1655                      Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] { Common.ICON_SMALL_CLIPBOARD, -1, -1, -1 });
1656                  }
1657  
1658                  if (clientPushData)
1659                  {
1660                      Clipboard.ReceiveAndProcessClipboardData(remoteEndPoint, s, enStream, deStream, $"{postAction}");
1661                  }
1662                  else
1663                  {
1664                      SendClipboardData(s, enStream);
1665                  }
1666              }
1667              catch (Exception e)
1668              {
1669                  Logger.Log(e);
1670              }
1671          }
1672  
1673          internal static void SendClipboardData(Socket s, Stream ecStream)
1674          {
1675              if (Common.RunWithNoAdminRight && Setting.Values.OneWayClipboardMode)
1676              {
1677                  s?.Close();
1678                  return;
1679              }
1680  
1681              const int CLOSE_TIMEOUT = 10;
1682              byte[] header = new byte[1024];
1683              string headerString = string.Empty;
1684              if (Clipboard.LastDragDropFile != null)
1685              {
1686                  string fileName = null;
1687  
1688                  if (!Launch.ImpersonateLoggedOnUserAndDoSomething(() =>
1689                  {
1690                      if (!File.Exists(Clipboard.LastDragDropFile))
1691                      {
1692                          headerString = Directory.Exists(Clipboard.LastDragDropFile)
1693                              ? $"{0}*{Clipboard.LastDragDropFile} - Folder is not supported, zip it first!"
1694                              : Clipboard.LastDragDropFile.Contains("- File too big")
1695                                  ? $"{0}*{Clipboard.LastDragDropFile}"
1696                                  : $"{0}*{Clipboard.LastDragDropFile} not found!";
1697                      }
1698                      else
1699                      {
1700                          fileName = Clipboard.LastDragDropFile;
1701                          headerString = $"{new FileInfo(fileName).Length}*{fileName}";
1702                      }
1703                  }))
1704                  {
1705                      s?.Close();
1706                      return;
1707                  }
1708  
1709                  Common.GetBytesU(headerString).CopyTo(header, 0);
1710  
1711                  try
1712                  {
1713                      ecStream.Write(header, 0, header.Length);
1714  
1715                      if (!string.IsNullOrEmpty(fileName))
1716                      {
1717                          if (SendFile(s, ecStream, fileName))
1718                          {
1719                              s.Close(CLOSE_TIMEOUT);
1720                          }
1721                      }
1722                      else
1723                      {
1724                          s.Close(CLOSE_TIMEOUT);
1725                      }
1726                  }
1727                  catch (IOException e)
1728                  {
1729                      string log = $"{nameof(SendClipboardData)}: Exception accessing the socket: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)";
1730                      Logger.Log(log);
1731                  }
1732                  catch (SocketException e)
1733                  {
1734                      string log = $"{nameof(SendClipboardData)}: {e.GetType()}/{e.Message}. This is expected when the connection is closed by the remote host.";
1735                      Logger.Log(log);
1736                  }
1737                  catch (ObjectDisposedException e)
1738                  {
1739                      string log = $"{nameof(SendClipboardData)}: {e.GetType()}/{e.Message}. This is expected when the socket is disposed by a machine switch for ex..";
1740                      Logger.Log(log);
1741                  }
1742              }
1743              else if (!Clipboard.IsClipboardDataImage && Clipboard.LastClipboardData != null)
1744              {
1745                  try
1746                  {
1747                      byte[] data = Clipboard.LastClipboardData;
1748  
1749                      headerString = $"{data.Length}*{"text"}";
1750                      Common.GetBytesU(headerString).CopyTo(header, 0);
1751  
1752                      if (data != null)
1753                      {
1754                          ecStream.Write(header, 0, header.Length);
1755                          _ = SendData(s, ecStream, data);
1756                          Logger.LogDebug("Text sent: " + data.Length.ToString(CultureInfo.CurrentCulture));
1757                      }
1758  
1759                      s.Close(CLOSE_TIMEOUT);
1760                  }
1761                  catch (IOException e)
1762                  {
1763                      string log = $"{nameof(SendClipboardData)}: Exception accessing the socket: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)";
1764                      Logger.Log(log);
1765                  }
1766                  catch (SocketException e)
1767                  {
1768                      string log = $"{nameof(SendClipboardData)}: {e.GetType()}/{e.Message}. This is expected when the connection is closed by the remote host.";
1769                      Logger.Log(log);
1770                  }
1771                  catch (ObjectDisposedException e)
1772                  {
1773                      string log = $"{nameof(SendClipboardData)}: {e.GetType()}/{e.Message}. This is expected when the socket is disposed by a machine switch for ex..";
1774                      Logger.Log(log);
1775                  }
1776              }
1777              else if (Clipboard.LastClipboardData != null && Clipboard.LastClipboardData.Length > 0)
1778              {
1779                  byte[] data = Clipboard.LastClipboardData;
1780  
1781                  headerString = $"{data.Length}*{"image"}";
1782                  Common.GetBytesU(headerString).CopyTo(header, 0);
1783                  try
1784                  {
1785                      ecStream.Write(header, 0, header.Length);
1786                      _ = SendData(s, ecStream, data);
1787                      Logger.LogDebug("Image sent: " + data.Length.ToString(CultureInfo.CurrentCulture));
1788                      s.Close(CLOSE_TIMEOUT);
1789                  }
1790                  catch (IOException e)
1791                  {
1792                      string log = $"{nameof(SendClipboardData)}: Exception accessing the socket: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)";
1793                      Logger.Log(log);
1794                  }
1795                  catch (SocketException e)
1796                  {
1797                      string log = $"{nameof(SendClipboardData)}: {e.GetType()}/{e.Message}. This is expected when the connection is closed by the remote host.";
1798                      Logger.Log(log);
1799                  }
1800                  catch (ObjectDisposedException e)
1801                  {
1802                      string log = $"{nameof(SendClipboardData)}: {e.GetType()}/{e.Message}. This is expected when the socket is disposed by a machine switch for ex..";
1803                      Logger.Log(log);
1804                  }
1805              }
1806              else
1807              {
1808                  Logger.Log("No data available in clipboard or LastDragDropFile!");
1809                  s.Close();
1810              }
1811          }
1812  
1813          private static bool SendFileEx(Socket s, Stream ecStream, string fileName)
1814          {
1815              try
1816              {
1817                  using (FileStream f = File.OpenRead(fileName))
1818                  {
1819                      byte[] buf = new byte[Common.NETWORK_STREAM_BUF_SIZE];
1820                      int rv, sentCount = 0;
1821  
1822                      do
1823                      {
1824                          if ((rv = f.Read(buf, 0, Common.NETWORK_STREAM_BUF_SIZE)) > 0)
1825                          {
1826                              ecStream.Write(buf, 0, rv);
1827                              sentCount += rv;
1828                          }
1829                      }
1830                      while (rv > 0);
1831  
1832                      if ((rv = Package.PACKAGE_SIZE - (sentCount % Package.PACKAGE_SIZE)) > 0)
1833                      {
1834                          Array.Clear(buf, 0, buf.Length);
1835                          ecStream.Write(buf, 0, rv);
1836                      }
1837  
1838                      ecStream.Flush();
1839  
1840                      Logger.LogDebug("File sent: " + fileName);
1841                  }
1842  
1843                  return true;
1844              }
1845              catch (Exception e)
1846              {
1847                  if (e is IOException)
1848                  {
1849                      string log = $"{nameof(SendFileEx)}: Exception accessing the socket: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)";
1850                      Logger.Log(log);
1851                  }
1852                  else
1853                  {
1854                      Logger.Log(e);
1855                  }
1856  
1857                  Common.ShowToolTip(e.Message, 1000, ToolTipIcon.Warning, Setting.Values.ShowClipNetStatus);
1858                  s.Close();
1859              }
1860  
1861              return false;
1862          }
1863  
1864          private static bool SendFile(Socket s, Stream ecStream, string fileName)
1865          {
1866              bool r = false;
1867  
1868              if (Common.RunOnLogonDesktop || Common.RunOnScrSaverDesktop)
1869              {
1870                  if (fileName.StartsWith(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) + @"\" + Common.BinaryName + @"\ScreenCaptures\", StringComparison.CurrentCultureIgnoreCase))
1871                  {
1872                      r = SendFileEx(s, ecStream, fileName);
1873                  }
1874              }
1875              else
1876              {
1877                  _ = Launch.ImpersonateLoggedOnUserAndDoSomething(() => { r = SendFileEx(s, ecStream, fileName); });
1878              }
1879  
1880              return r;
1881          }
1882  
1883          private static bool SendData(Socket s, Stream ecStream, byte[] data)
1884          {
1885              bool r = false;
1886  
1887              try
1888              {
1889                  using MemoryStream f = new(data);
1890                  byte[] buf = new byte[Common.NETWORK_STREAM_BUF_SIZE];
1891                  int rv, sentCount = 0;
1892  
1893                  do
1894                  {
1895                      if ((rv = f.Read(buf, 0, Common.NETWORK_STREAM_BUF_SIZE)) > 0)
1896                      {
1897                          ecStream.Write(buf, 0, rv);
1898                          sentCount += rv;
1899                      }
1900                  }
1901                  while (rv > 0);
1902  
1903                  if ((rv = sentCount % Package.PACKAGE_SIZE) > 0)
1904                  {
1905                      Array.Clear(buf, 0, buf.Length);
1906                      ecStream.Write(buf, 0, rv);
1907                  }
1908  
1909                  ecStream.Flush();
1910                  Logger.LogDebug("Data sent: " + data.Length.ToString(CultureInfo.InvariantCulture));
1911                  r = true;
1912              }
1913              catch (Exception e)
1914              {
1915                  if (e is IOException)
1916                  {
1917                      string log = $"{nameof(SendData)}: Exception accessing the socket: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)";
1918                      Logger.Log(log);
1919                  }
1920                  else
1921                  {
1922                      Logger.Log(e);
1923                  }
1924  
1925                  Common.ShowToolTip(e.Message, 1000, ToolTipIcon.Warning, Setting.Values.ShowClipNetStatus);
1926                  ecStream.Close();
1927                  s.Close();
1928              }
1929  
1930              return r;
1931          }
1932  
1933          private TcpSk AddTcpSocket(bool isClient, Socket s, SocketStatus status, string machineName)
1934          {
1935              Common.CloseAnUnusedSocket();
1936              TcpSk tcp = new(isClient, s, status, machineName);
1937  
1938              lock (TcpSocketsLock)
1939              {
1940  #if ENABLE_LEGACY_DOS_PROTECTION
1941                  PreventDoS(TcpSockets);
1942  #endif
1943                  TcpSockets.Add(tcp);
1944              }
1945  
1946              return tcp;
1947          }
1948  
1949          private TcpSk AddTcpSocket(bool isClient, Socket s, SocketStatus status, string machineName, IPAddress ip)
1950          {
1951              Common.CloseAnUnusedSocket();
1952              TcpSk tcp = new(isClient, s, status, machineName, ip);
1953  
1954              lock (TcpSocketsLock)
1955              {
1956  #if ENABLE_LEGACY_DOS_PROTECTION
1957                  PreventDoS(TcpSockets);
1958  #endif
1959                  TcpSockets.Add(tcp);
1960              }
1961  
1962              return tcp;
1963          }
1964  
1965          private void UpdateTcpSockets(TcpSk tcp, SocketStatus status)
1966          {
1967              if (status == SocketStatus.InvalidKey)
1968              {
1969                  InvalidKeyFound = true;
1970              }
1971  
1972              InvalidKeyFoundOnClientSocket = tcp.IsClient && InvalidKeyFound;
1973  
1974              try
1975              {
1976                  lock (TcpSocketsLock)
1977                  {
1978                      if (TcpSockets != null)
1979                      {
1980                          if (status == SocketStatus.Connected && tcp.IsClient)
1981                          {
1982                              List<TcpSk> tobeRemovedSockets = TcpSockets;
1983  
1984                              if (tcp.MachineId == Setting.Values.MachineId)
1985                              {
1986                                  tcp = null;
1987                                  Setting.Values.MachineId = Encryption.Ran.Next();
1988                                  InitAndCleanup.UpdateMachineTimeAndID();
1989                                  InitAndCleanup.PleaseReopenSocket = InitAndCleanup.REOPEN_WHEN_HOTKEY;
1990  
1991                                  Logger.TelemetryLogTrace("MachineID conflict.", SeverityLevel.Information);
1992                              }
1993                              else
1994                              {
1995                                  // Keep the first connected one.
1996                                  tobeRemovedSockets = TcpSockets.Where(item => item.IsClient && !ReferenceEquals(item, tcp) && item.MachineName.Equals(tcp.MachineName, StringComparison.OrdinalIgnoreCase)).ToList();
1997                              }
1998  
1999                              foreach (TcpSk t in tobeRemovedSockets)
2000                              {
2001                                  t.Status = SocketStatus.ForceClosed;
2002                                  Logger.LogDebug($"Closing duplicated socket {t.MachineName}: {t.Address}");
2003                              }
2004                          }
2005  
2006                          List<TcpSk> toBeRemoved = new();
2007  
2008                          foreach (TcpSk t in TcpSockets)
2009                          {
2010                              if (t.Status is SocketStatus.Error or
2011                                  SocketStatus.ForceClosed or
2012  
2013                                  // SocketStatus.InvalidKey or
2014                                  SocketStatus.NA or
2015                                  SocketStatus.Timeout or
2016                                  SocketStatus.SendError)
2017                              {
2018                                  try
2019                                  {
2020                                      if (t.BackingSocket != null)
2021                                      {
2022                                          t.MachineName = "$*NotUsed*$";
2023                                          t.Status = t.Status >= 0 ? 0 : t.Status - 1; // If error closing, the socket will be closed again at SocketStuff.Close().
2024                                          t.BackingSocket.Close();
2025                                      }
2026  
2027                                      toBeRemoved.Add(t);
2028                                  }
2029                                  catch (SocketException e)
2030                                  {
2031                                      string log = $"{nameof(UpdateTcpSockets)}: {e.GetType()}/{e.Message}. This is expected when the connection is closed by the remote host.";
2032                                      Logger.Log(log);
2033                                  }
2034                                  catch (ObjectDisposedException e)
2035                                  {
2036                                      string log = $"{nameof(UpdateTcpSockets)}: {e.GetType()}/{e.Message}. This is expected when the socket is disposed by a machine switch for ex..";
2037                                      Logger.Log(log);
2038                                  }
2039                              }
2040                          }
2041  
2042                          if (tcp != null)
2043                          {
2044                              tcp.Status = status;
2045  
2046                              if (status == SocketStatus.Connected)
2047                              {
2048                                  // Update the socket's machine name based on its corresponding client/server socket.
2049                                  foreach (TcpSk t in TcpSockets)
2050                                  {
2051                                      if (t.MachineId == tcp.MachineId && t.IsClient != tcp.IsClient)
2052                                      {
2053                                          if ((string.IsNullOrEmpty(tcp.MachineName) || tcp.MachineName.Contains('.') || tcp.MachineName.Contains(':'))
2054                                              && !(string.IsNullOrEmpty(t.MachineName) || t.MachineName.Contains('.') || t.MachineName.Contains(':')))
2055                                          {
2056                                              tcp.MachineName = t.MachineName;
2057                                          }
2058                                          else if ((string.IsNullOrEmpty(t.MachineName) || t.MachineName.Contains('.') || t.MachineName.Contains(':'))
2059                                              && !(string.IsNullOrEmpty(tcp.MachineName) || tcp.MachineName.Contains('.') || tcp.MachineName.Contains(':')))
2060                                          {
2061                                              t.MachineName = tcp.MachineName;
2062                                          }
2063                                      }
2064                                  }
2065  
2066                                  if (string.IsNullOrEmpty(tcp.MachineName) || tcp.MachineName.Contains('.') || tcp.MachineName.Contains(':'))
2067                                  {
2068                                      tcp.MachineName = MachineStuff.NameFromID((ID)tcp.MachineId);
2069                                  }
2070  
2071                                  if (string.IsNullOrEmpty(tcp.MachineName) || tcp.MachineName.Contains('.') || tcp.MachineName.Contains(':'))
2072                                  {
2073                                      tcp.MachineName = Common.GetRemoteStringIP(tcp.BackingSocket);
2074                                  }
2075                              }
2076                          }
2077                          else
2078                          {
2079                              Logger.Log("UpdateTcpSockets.Exception: Socket not found!");
2080                          }
2081  
2082                          foreach (TcpSk t in toBeRemoved)
2083                          {
2084                              _ = TcpSockets.Remove(t);
2085                          }
2086                      }
2087                  }
2088              }
2089              catch (Exception e)
2090              {
2091                  Logger.Log(e);
2092              }
2093          }
2094  
2095          private void PreventDoS(List<TcpSk> sockets)
2096          {
2097              if (sockets.Count > 100)
2098              {
2099                  TcpSk tcp;
2100  
2101                  try
2102                  {
2103                      string msg = Application.ProductName + " has been terminated, too many connections.";
2104  
2105                      for (int i = 0; i < 10; i++)
2106                      {
2107                          tcp = sockets[i * 10];
2108  
2109                          if (tcp != null)
2110                          {
2111                              msg += $"\r\n{Common.MachineName}{(tcp.IsClient ? "=>" : "<=")}{tcp.MachineName}:{tcp.Status}";
2112                          }
2113                      }
2114  
2115                      _ = Launch.CreateLowIntegrityProcess(
2116                          "\"" + Path.GetDirectoryName(Application.ExecutablePath) + "\\MouseWithoutBordersHelper.exe\"",
2117                          "InternalError" + " \"" + msg + "\"",
2118                          0,
2119                          false,
2120                          0,
2121                          (short)ProcessWindowStyle.Hidden);
2122                  }
2123                  finally
2124                  {
2125                      Common.MainForm.Quit(true, false);
2126                  }
2127              }
2128          }
2129      }
2130  }