MXPPacketServer.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSimulator Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. /* This file borrows heavily from MXPServer.cs - the reference MXPServer
  28. * See http://www.bubblecloud.org for a copy of the original file and
  29. * implementation details. */
  30. using System;
  31. using System.Collections.Generic;
  32. using System.Reflection;
  33. using System.Threading;
  34. using log4net;
  35. using MXP;
  36. using MXP.Messages;
  37. using OpenMetaverse;
  38. using OpenSim.Client.MXP.ClientStack;
  39. using OpenSim.Framework;
  40. using OpenSim.Region.Framework.Scenes;
  41. using OpenSim.Framework.Communications;
  42. using OpenSim.Services.Interfaces;
  43. using System.Security.Cryptography;
  44. namespace OpenSim.Client.MXP.PacketHandler
  45. {
  46. public class MXPPacketServer
  47. {
  48. internal static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  49. #region Fields
  50. private readonly List<MXPClientView> m_clients = new List<MXPClientView>();
  51. private readonly Dictionary<UUID, Scene> m_scenes;
  52. private readonly Transmitter m_transmitter;
  53. // private readonly Thread m_clientThread;
  54. private readonly IList<Session> m_sessions = new List<Session>();
  55. private readonly IList<Session> m_sessionsToClient = new List<Session>();
  56. private readonly IList<MXPClientView> m_sessionsToRemove = new List<MXPClientView>();
  57. private readonly int m_port;
  58. // private readonly bool m_accountsAuthenticate;
  59. private readonly String m_programName;
  60. private readonly byte m_programMajorVersion;
  61. private readonly byte m_programMinorVersion;
  62. #endregion
  63. #region Constructors
  64. public MXPPacketServer(int port, Dictionary<UUID, Scene> scenes, bool accountsAuthenticate)
  65. {
  66. m_port = port;
  67. // m_accountsAuthenticate = accountsAuthenticate;
  68. m_scenes = scenes;
  69. m_programMinorVersion = 63;
  70. m_programMajorVersion = 0;
  71. m_programName = "OpenSimulator";
  72. m_transmitter = new Transmitter(port);
  73. StartListener();
  74. }
  75. public void StartListener()
  76. {
  77. m_log.Info("[MXP ClientStack] Transmitter starting on UDP server port: " + m_port);
  78. m_transmitter.Startup();
  79. m_log.Info("[MXP ClientStack] Transmitter started. MXP version: "+MxpConstants.ProtocolMajorVersion+"."+MxpConstants.ProtocolMinorVersion+" Source Revision: "+MxpConstants.ProtocolSourceRevision);
  80. }
  81. #endregion
  82. #region Properties
  83. /// <summary>
  84. /// Number of sessions pending. (Process() accepts pending sessions).
  85. /// </summary>
  86. public int PendingSessionCount
  87. {
  88. get
  89. {
  90. return m_transmitter.PendingSessionCount;
  91. }
  92. }
  93. /// <summary>
  94. /// Number of connected sessions.
  95. /// </summary>
  96. public int SessionCount
  97. {
  98. get
  99. {
  100. return m_sessions.Count;
  101. }
  102. }
  103. /// <summary>
  104. /// Property reflecting whether client transmitter threads are alive.
  105. /// </summary>
  106. public bool IsTransmitterAlive
  107. {
  108. get
  109. {
  110. return m_transmitter != null && m_transmitter.IsAlive;
  111. }
  112. }
  113. /// <summary>
  114. /// Number of packets sent.
  115. /// </summary>
  116. public ulong PacketsSent
  117. {
  118. get
  119. {
  120. return m_transmitter != null ? m_transmitter.PacketsSent : 0;
  121. }
  122. }
  123. /// <summary>
  124. /// Number of packets received.
  125. /// </summary>
  126. public ulong PacketsReceived
  127. {
  128. get
  129. {
  130. return m_transmitter != null ? m_transmitter.PacketsReceived : 0;
  131. }
  132. }
  133. /// <summary>
  134. /// Bytes client has received so far.
  135. /// </summary>
  136. public ulong BytesReceived
  137. {
  138. get
  139. {
  140. return m_transmitter != null ? m_transmitter.BytesReceived : 0;
  141. }
  142. }
  143. /// <summary>
  144. /// Bytes client has sent so far.
  145. /// </summary>
  146. public ulong BytesSent
  147. {
  148. get
  149. {
  150. return m_transmitter != null ? m_transmitter.BytesSent : 0;
  151. }
  152. }
  153. /// <summary>
  154. /// Number of bytes received (bytes per second) during past second.
  155. /// </summary>
  156. public double ReceiveRate
  157. {
  158. get
  159. {
  160. return m_transmitter != null ? m_transmitter.ReceiveRate : 0;
  161. }
  162. }
  163. /// <summary>
  164. /// Number of bytes sent (bytes per second) during past second.
  165. /// </summary>
  166. public double SendRate
  167. {
  168. get
  169. {
  170. return m_transmitter != null ? m_transmitter.SendRate : 0;
  171. }
  172. }
  173. #endregion
  174. #region Session Management
  175. public void Disconnect(Session session)
  176. {
  177. if (session.IsConnected)
  178. {
  179. Message message = MessageFactory.Current.ReserveMessage(typeof(LeaveRequestMessage));
  180. session.Send(message);
  181. MessageFactory.Current.ReleaseMessage(message);
  182. }
  183. else
  184. {
  185. throw new Exception("Not connected.");
  186. }
  187. }
  188. #endregion
  189. #region Processing
  190. public void Process()
  191. {
  192. ProcessMessages();
  193. Clean();
  194. }
  195. public void Clean()
  196. {
  197. foreach (MXPClientView clientView in m_clients)
  198. {
  199. if (clientView.Session.SessionState == SessionState.Disconnected)
  200. {
  201. m_sessionsToRemove.Add(clientView);
  202. }
  203. }
  204. foreach (MXPClientView clientView in m_sessionsToRemove)
  205. {
  206. clientView.Scene.RemoveClient(clientView.AgentId);
  207. clientView.OnClean();
  208. m_clients.Remove(clientView);
  209. m_sessions.Remove(clientView.Session);
  210. }
  211. m_sessionsToRemove.Clear();
  212. }
  213. public void ProcessMessages()
  214. {
  215. if (m_transmitter.PendingSessionCount > 0)
  216. {
  217. Session tmp = m_transmitter.AcceptPendingSession();
  218. m_sessions.Add(tmp);
  219. m_sessionsToClient.Add(tmp);
  220. }
  221. List<Session> tmpRemove = new List<Session>();
  222. foreach (Session session in m_sessionsToClient)
  223. {
  224. while (session.AvailableMessages > 0)
  225. {
  226. Message message = session.Receive();
  227. if (message.GetType() == typeof (JoinRequestMessage))
  228. {
  229. JoinRequestMessage joinRequestMessage = (JoinRequestMessage) message;
  230. m_log.Info("[MXP ClientStack]: Session join request: " + session.SessionId + " (" +
  231. (session.IsIncoming ? "from" : "to") + " " + session.RemoteEndPoint.Address + ":" +
  232. session.RemoteEndPoint.Port + ")");
  233. try
  234. {
  235. if (joinRequestMessage.BubbleId == Guid.Empty)
  236. {
  237. foreach (Scene scene in m_scenes.Values)
  238. {
  239. if (scene.RegionInfo.RegionName == joinRequestMessage.BubbleName)
  240. {
  241. m_log.Info("[MXP ClientStack]: Resolved region by name: " + joinRequestMessage.BubbleName + " (" + scene.RegionInfo.RegionID + ")");
  242. joinRequestMessage.BubbleId = scene.RegionInfo.RegionID.Guid;
  243. }
  244. }
  245. }
  246. if (joinRequestMessage.BubbleId == Guid.Empty)
  247. {
  248. m_log.Warn("[MXP ClientStack]: Failed to resolve region by name: " + joinRequestMessage.BubbleName);
  249. }
  250. UUID sceneId = new UUID(joinRequestMessage.BubbleId);
  251. bool regionExists = true;
  252. if (!m_scenes.ContainsKey(sceneId))
  253. {
  254. m_log.Info("[MXP ClientStack]: No such region: " + sceneId);
  255. regionExists = false;
  256. }
  257. UUID userId = UUID.Zero;
  258. UserAccount account = null;
  259. bool authorized = regionExists ? AuthoriseUser(joinRequestMessage.ParticipantName,
  260. joinRequestMessage.ParticipantPassphrase,
  261. new UUID(joinRequestMessage.BubbleId), out account)
  262. : false;
  263. if (authorized)
  264. {
  265. Scene scene = m_scenes[sceneId];
  266. UUID mxpSessionID = UUID.Random();
  267. string reason;
  268. m_log.Debug("[MXP ClientStack]: Session join request success: " + session.SessionId + " (" +
  269. (session.IsIncoming ? "from" : "to") + " " + session.RemoteEndPoint.Address + ":" +
  270. session.RemoteEndPoint.Port + ")");
  271. m_log.Debug("[MXP ClientStack]: Attaching UserAgent to UserProfile...");
  272. UUID secureSession = UUID.Zero;
  273. AttachUserAgentToUserProfile(account, session, mxpSessionID, sceneId, out secureSession);
  274. m_log.Debug("[MXP ClientStack]: Attached UserAgent to UserProfile.");
  275. m_log.Debug("[MXP ClientStack]: Preparing Scene to Connection...");
  276. if (!PrepareSceneForConnection(mxpSessionID, secureSession, sceneId, account, out reason))
  277. {
  278. m_log.DebugFormat("[MXP ClientStack]: Scene refused connection: {0}", reason);
  279. DeclineConnection(session, joinRequestMessage);
  280. tmpRemove.Add(session);
  281. continue;
  282. }
  283. m_log.Debug("[MXP ClientStack]: Prepared Scene to Connection.");
  284. m_log.Debug("[MXP ClientStack]: Accepting connection...");
  285. AcceptConnection(session, joinRequestMessage, mxpSessionID, userId);
  286. m_log.Info("[MXP ClientStack]: Accepted connection.");
  287. m_log.Debug("[MXP ClientStack]: Creating ClientView....");
  288. MXPClientView client = new MXPClientView(session, mxpSessionID, userId, scene, account.FirstName, account.LastName);
  289. m_clients.Add(client);
  290. m_log.Debug("[MXP ClientStack]: Created ClientView.");
  291. client.MXPSendSynchronizationBegin(m_scenes[new UUID(joinRequestMessage.BubbleId)].SceneContents.GetTotalObjectsCount());
  292. m_log.Debug("[MXP ClientStack]: Starting ClientView...");
  293. try
  294. {
  295. client.Start();
  296. m_log.Debug("[MXP ClientStack]: Started ClientView.");
  297. }
  298. catch (Exception e)
  299. {
  300. m_log.Error(e);
  301. }
  302. m_log.Debug("[MXP ClientStack]: Connected");
  303. }
  304. else
  305. {
  306. m_log.Info("[MXP ClientStack]: Session join request failure: " + session.SessionId + " (" +
  307. (session.IsIncoming ? "from" : "to") + " " + session.RemoteEndPoint.Address + ":" +
  308. session.RemoteEndPoint.Port + ")");
  309. DeclineConnection(session, joinRequestMessage);
  310. }
  311. }
  312. catch (Exception e)
  313. {
  314. m_log.Error("[MXP ClientStack]: Session join request failure: " + session.SessionId + " (" +
  315. (session.IsIncoming ? "from" : "to") + " " + session.RemoteEndPoint.Address + ":" +
  316. session.RemoteEndPoint.Port + "): "+e.ToString()+" :"+e.StackTrace.ToString());
  317. }
  318. tmpRemove.Add(session);
  319. }
  320. }
  321. }
  322. foreach (Session session in tmpRemove)
  323. {
  324. m_sessionsToClient.Remove(session);
  325. }
  326. foreach (MXPClientView clientView in m_clients)
  327. {
  328. int messagesProcessedCount = 0;
  329. Session session = clientView.Session;
  330. while (session.AvailableMessages > 0)
  331. {
  332. Message message = session.Receive();
  333. if (message.GetType() == typeof(LeaveRequestMessage))
  334. {
  335. LeaveResponseMessage leaveResponseMessage = (LeaveResponseMessage)MessageFactory.Current.ReserveMessage(
  336. typeof(LeaveResponseMessage));
  337. m_log.Debug("[MXP ClientStack]: Session leave request: " + session.SessionId + " (" + (session.IsIncoming ? "from" : "to") + " " + session.RemoteEndPoint.Address + ":" + session.RemoteEndPoint.Port + ")");
  338. leaveResponseMessage.RequestMessageId = message.MessageId;
  339. leaveResponseMessage.FailureCode = 0;
  340. session.Send(leaveResponseMessage);
  341. if (session.SessionState != SessionState.Disconnected)
  342. {
  343. session.SetStateDisconnected();
  344. }
  345. m_log.Debug("[MXP ClientStack]: Removing Client from Scene");
  346. //clientView.Scene.RemoveClient(clientView.AgentId);
  347. }
  348. if (message.GetType() == typeof(LeaveResponseMessage))
  349. {
  350. LeaveResponseMessage leaveResponseMessage = (LeaveResponseMessage)message;
  351. m_log.Debug("[MXP ClientStack]: Session leave response: " + session.SessionId + " (" + (session.IsIncoming ? "from" : "to") + " " + session.RemoteEndPoint.Address + ":" + session.RemoteEndPoint.Port + ")");
  352. if (leaveResponseMessage.FailureCode == 0)
  353. {
  354. session.SetStateDisconnected();
  355. }
  356. m_log.Debug("[MXP ClientStack]: Removing Client from Scene");
  357. //clientView.Scene.RemoveClient(clientView.AgentId);
  358. }
  359. else
  360. {
  361. clientView.MXPPRocessMessage(message);
  362. }
  363. MessageFactory.Current.ReleaseMessage(message);
  364. messagesProcessedCount++;
  365. if (messagesProcessedCount > 1000)
  366. {
  367. break;
  368. }
  369. }
  370. }
  371. }
  372. private void AcceptConnection(Session session, JoinRequestMessage joinRequestMessage, UUID mxpSessionID, UUID userId)
  373. {
  374. JoinResponseMessage joinResponseMessage = (JoinResponseMessage)MessageFactory.Current.ReserveMessage(
  375. typeof(JoinResponseMessage));
  376. joinResponseMessage.RequestMessageId = joinRequestMessage.MessageId;
  377. joinResponseMessage.FailureCode = MxpResponseCodes.SUCCESS;
  378. joinResponseMessage.BubbleId = joinRequestMessage.BubbleId;
  379. joinResponseMessage.ParticipantId = userId.Guid;
  380. joinResponseMessage.AvatarId = userId.Guid;
  381. joinResponseMessage.BubbleAssetCacheUrl = "http://" +
  382. NetworkUtil.GetHostFor(session.RemoteEndPoint.Address,
  383. m_scenes[
  384. new UUID(joinRequestMessage.BubbleId)].
  385. RegionInfo.
  386. ExternalHostName) + ":" +
  387. m_scenes[new UUID(joinRequestMessage.BubbleId)].RegionInfo.
  388. HttpPort + "/assets/";
  389. joinResponseMessage.BubbleName = m_scenes[new UUID(joinRequestMessage.BubbleId)].RegionInfo.RegionName;
  390. joinResponseMessage.BubbleRange = 128;
  391. joinResponseMessage.BubblePerceptionRange = 128 + 256;
  392. joinResponseMessage.BubbleRealTime = 0;
  393. joinResponseMessage.ProgramName = m_programName;
  394. joinResponseMessage.ProgramMajorVersion = m_programMajorVersion;
  395. joinResponseMessage.ProgramMinorVersion = m_programMinorVersion;
  396. joinResponseMessage.ProtocolMajorVersion = MxpConstants.ProtocolMajorVersion;
  397. joinResponseMessage.ProtocolMinorVersion = MxpConstants.ProtocolMinorVersion;
  398. joinResponseMessage.ProtocolSourceRevision = MxpConstants.ProtocolSourceRevision;
  399. session.Send(joinResponseMessage);
  400. session.SetStateConnected();
  401. }
  402. private void DeclineConnection(Session session, Message joinRequestMessage)
  403. {
  404. JoinResponseMessage joinResponseMessage = (JoinResponseMessage)MessageFactory.Current.ReserveMessage(typeof(JoinResponseMessage));
  405. joinResponseMessage.RequestMessageId = joinRequestMessage.MessageId;
  406. joinResponseMessage.FailureCode = MxpResponseCodes.UNAUTHORIZED_OPERATION;
  407. joinResponseMessage.ProgramName = m_programName;
  408. joinResponseMessage.ProgramMajorVersion = m_programMajorVersion;
  409. joinResponseMessage.ProgramMinorVersion = m_programMinorVersion;
  410. joinResponseMessage.ProtocolMajorVersion = MxpConstants.ProtocolMajorVersion;
  411. joinResponseMessage.ProtocolMinorVersion = MxpConstants.ProtocolMinorVersion;
  412. joinResponseMessage.ProtocolSourceRevision = MxpConstants.ProtocolSourceRevision;
  413. session.Send(joinResponseMessage);
  414. session.SetStateDisconnected();
  415. }
  416. public bool AuthoriseUser(string participantName, string password, UUID sceneId, out UserAccount account)
  417. {
  418. string firstName = "";
  419. string lastName = "";
  420. account = null;
  421. string[] nameParts = participantName.Split(' ');
  422. if (nameParts.Length != 2)
  423. {
  424. m_log.Error("[MXP ClientStack]: Login failed as user name is not formed of first and last name separated by space: " + participantName);
  425. return false;
  426. }
  427. firstName = nameParts[0];
  428. lastName = nameParts[1];
  429. account = m_scenes[sceneId].UserAccountService.GetUserAccount(m_scenes[sceneId].RegionInfo.ScopeID, firstName, lastName);
  430. if (account != null)
  431. return (m_scenes[sceneId].AuthenticationService.Authenticate(account.PrincipalID, password, 1) != string.Empty);
  432. return false;
  433. }
  434. private void AttachUserAgentToUserProfile(UserAccount account, Session session, UUID sessionId, UUID sceneId, out UUID secureSessionId)
  435. {
  436. secureSessionId = UUID.Random();
  437. Scene scene = m_scenes[sceneId];
  438. scene.PresenceService.LoginAgent(account.PrincipalID.ToString(), sessionId, secureSessionId);
  439. }
  440. private bool PrepareSceneForConnection(UUID sessionId, UUID secureSessionId, UUID sceneId, UserAccount account, out string reason)
  441. {
  442. Scene scene = m_scenes[sceneId];
  443. AgentCircuitData agent = new AgentCircuitData();
  444. agent.AgentID = account.PrincipalID;
  445. agent.firstname = account.FirstName;
  446. agent.lastname = account.LastName;
  447. agent.SessionID = sessionId;
  448. agent.SecureSessionID = secureSessionId;
  449. agent.circuitcode = sessionId.CRC();
  450. agent.BaseFolder = UUID.Zero;
  451. agent.InventoryFolder = UUID.Zero;
  452. agent.startpos = new Vector3(0, 0, 0); // TODO Fill in region start position
  453. agent.CapsPath = "http://localhost/";
  454. AvatarData avatar = scene.AvatarService.GetAvatar(account.PrincipalID);
  455. if (avatar != null)
  456. agent.Appearance = avatar.ToAvatarAppearance(account.PrincipalID); //userService.GetUserAppearance(userProfile.ID);
  457. if (agent.Appearance == null)
  458. {
  459. m_log.WarnFormat("[INTER]: Appearance not found for {0} {1}. Creating default.", agent.firstname, agent.lastname);
  460. agent.Appearance = new AvatarAppearance();
  461. }
  462. return scene.NewUserConnection(agent, 0, out reason);
  463. }
  464. public void PrintDebugInformation()
  465. {
  466. m_log.Info("[MXP ClientStack]: Statistics report");
  467. m_log.Info("Pending Sessions: " + PendingSessionCount);
  468. m_log.Info("Sessions: " + SessionCount + " (Clients: " + m_clients.Count + " )");
  469. m_log.Info("Transmitter Alive?: " + IsTransmitterAlive);
  470. m_log.Info("Packets Sent/Received: " + PacketsSent + " / " + PacketsReceived);
  471. m_log.Info("Bytes Sent/Received: " + BytesSent + " / " + BytesReceived);
  472. m_log.Info("Send/Receive Rate (bps): " + SendRate + " / " + ReceiveRate);
  473. }
  474. #endregion
  475. }
  476. }