PageRenderTime 56ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/sipsorcery-core/SIPSorcery.Servers.Cores/SIPRegistrationAgent/SIPRegistrationAgentCore.cs

https://github.com/thecc4re/sipsorcery-mono
C# | 825 lines | 663 code | 79 blank | 83 comment | 135 complexity | 17ba88029310e4be9bc617bf69643cef MD5 | raw file
Possible License(s): CC-BY-SA-3.0

Large files files are truncated, but you can click here to view the full file

  1. // ============================================================================
  2. // FileName: SIPRegistrationAgent.cs
  3. //
  4. // Description:
  5. // Registration agent daemon to maintain SIP registrations with multiple SIP
  6. // Registrar servers.
  7. //
  8. // Author(s):
  9. // Aaron Clauson
  10. //
  11. // History:
  12. // 13 Nov 2006 Aaron Clauson Created.
  13. // 19 Oct 2007 Aaron Clauson Incorporated DNS management to stop sipswitch stalling on invalid host names.
  14. // 17 May 2008 Aaron Clauson Refactored UserRegistration class into its own class file.
  15. // 01 Mar 2011 Aaron Clauson Switched to using concurrent collections.
  16. //
  17. // License:
  18. // This software is licensed under the BSD License http://www.opensource.org/licenses/bsd-license.php
  19. //
  20. // Copyright (c) Aaron Clauson (aaron@sipsorcery.com), SIP Sorcery Ltd, Hobart, Australia (www.sipsorcery.com)
  21. // All rights reserved.
  22. //
  23. // Redistribution and use in source and binary forms, with or without modification, are permitted provided that
  24. // the following conditions are met:
  25. //
  26. // Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  27. // Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
  28. // disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of SIP Sorcery Ltd.
  29. // nor the names of its contributors may be used to endorse or promote products derived from this software without specific
  30. // prior written permission.
  31. //
  32. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
  33. // BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  34. // IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
  35. // OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
  36. // OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  37. // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  38. // POSSIBILITY OF SUCH DAMAGE.
  39. // ============================================================================
  40. using System;
  41. using System.Collections.Concurrent;
  42. using System.Collections.Generic;
  43. using System.Data;
  44. using System.Data.SqlClient;
  45. using System.Globalization;
  46. using System.Linq;
  47. using System.Linq.Expressions;
  48. using System.Net;
  49. using System.Runtime.Serialization;
  50. using System.Text;
  51. using System.Text.RegularExpressions;
  52. using System.Threading;
  53. using System.Transactions;
  54. using System.Xml;
  55. using SIPSorcery.Net;
  56. using SIPSorcery.Persistence;
  57. using SIPSorcery.SIP;
  58. using SIPSorcery.SIP.App;
  59. using SIPSorcery.Sys;
  60. using Heijden.DNS;
  61. using log4net;
  62. namespace SIPSorcery.Servers
  63. {
  64. public class SIPRegistrationAgentCore
  65. {
  66. public const int REGISTRATION_RENEWAL_PERIOD = 1000; // Time in milliseconds between the registration agent checking registrations.
  67. public const int REGISTRATION_HEAD_TIME = 5; // Time in seconds to go to next registration to initate.
  68. public const int REGISTER_FAILURERETRY_INTERVAL = 300; // Number of seconds between consecutive register requests in the event of failures or timeouts.
  69. public const int REGISTER_DNSTIMEOUT_RETRY_INTERVAL = 300; // The number of seconds between consecutive register requests in the event of a DNS timeout resolving the registrar server.
  70. public const int REGISTER_EMPTYDNS_RETRY_INTERVAL = 10; // When the DNS manager has not yet had time to do the lookup wait this number of seconds and try again.
  71. public const int REGISTER_CHECKTIME_THRESHOLD = 3; // Time the user registration checks should be taking less than. If exceeded a log message is produced.
  72. public const int REGISTER_EXPIREALL_WAITTIME = 2000; // When stopping the registration agent the time to give after the initial request for all requests to complete.
  73. public const int REGISTER_DELETION_TIMEOUT = 60; // Number of seconds a deletion request will timeout after.
  74. public const int REGISTER_MINIMUM_EXPIRY = 60; // The minimum interval a registration will be accepted for. Anything less than this interval will use this minimum value.
  75. public const int REGISTER_MINIMUM_ATTEMPT = 50; // The minimum interval at which consecutive registration attempts can occur.
  76. //private const int DNS_SYNCHRONOUS_TIMEOUT = 3; // For operations that need to so a synchronous DNS lookup such as binding removals the amount of time for the lookup.
  77. //private const int MAX_DNS_FAILURE_ATTEMPTS = 6;
  78. //private const string DNS_FAILURE_MESSAGE_PREFIX = "DNS Failure:";
  79. public const int DNS_FAILURE_RETRY_WINDOW = 180; // If a provider's DNS lookups fail for this length of time the binding will be disabled.
  80. private const string THREAD_NAME_PREFIX = "regagent-";
  81. private const int NUMBER_BINDINGS_PER_DB_ROUNDTRIP = 20;
  82. private const int MAX_NUMBER_INTRANSIT_BINDINGS = 100000; // The maximum number of in transit REGISTER bindings that will be stored.
  83. private static ILog logger = AppState.GetLogger("sipregagent");
  84. private static readonly string m_userAgentString = SIPConstants.SIP_USERAGENT_STRING;
  85. private static readonly string m_regAgentContactId = SIPProviderBinding.REGAGENT_CONTACT_ID_KEY;
  86. private bool m_disallowPrivateIPRegistrars; // If set to true any SIP providers that resolve to a private or loopback IP address will be disabled.
  87. private bool m_sendRegisters = true; // While true the register agent thread will send out register requests to maintain it's registrations.
  88. private int m_bindingsProcessedCount = 0;
  89. private SIPTransport m_sipTransport;
  90. private SIPEndPoint m_outboundProxy;
  91. private ConcurrentDictionary<Guid, SIPProviderBinding> m_inTransitBindings;
  92. private SIPMonitorLogDelegate StatefulProxyLogEvent_External;
  93. private SIPAssetGetByIdDelegate<SIPProvider> GetSIPProviderById_External;
  94. private SIPAssetUpdateDelegate<SIPProvider> UpdateSIPProvider_External;
  95. private SIPAssetUpdatePropertyDelegate<SIPProvider> UpdateSIPProviderProperty_External;
  96. private SIPAssetPersistor<SIPProviderBinding> m_bindingPersistor;
  97. public SIPRegistrationAgentCore(
  98. SIPMonitorLogDelegate logDelegate,
  99. SIPTransport sipTransport,
  100. SIPEndPoint outboundProxy,
  101. SIPAssetGetByIdDelegate<SIPProvider> getSIPProviderById,
  102. SIPAssetUpdateDelegate<SIPProvider> updateSIPProvider,
  103. SIPAssetUpdatePropertyDelegate<SIPProvider> updateSIPProviderProperty,
  104. SIPAssetPersistor<SIPProviderBinding> bindingPersistor,
  105. bool disAllowPrivateIPRegistrars)
  106. {
  107. StatefulProxyLogEvent_External = logDelegate;
  108. GetSIPProviderById_External = getSIPProviderById;
  109. UpdateSIPProvider_External = updateSIPProvider;
  110. UpdateSIPProviderProperty_External = updateSIPProviderProperty;
  111. m_bindingPersistor = bindingPersistor;
  112. m_sipTransport = sipTransport;
  113. m_outboundProxy = outboundProxy;
  114. m_disallowPrivateIPRegistrars = disAllowPrivateIPRegistrars;
  115. }
  116. public void Start(int threadCount)
  117. {
  118. logger.Debug("SIPRegistrationAgent thread started with " + threadCount + " threads.");
  119. m_inTransitBindings = new ConcurrentDictionary<Guid, SIPProviderBinding>(threadCount, MAX_NUMBER_INTRANSIT_BINDINGS);
  120. for (int index = 1; index <= threadCount; index++)
  121. {
  122. string threadSuffix = index.ToString();
  123. ThreadPool.QueueUserWorkItem(delegate { MonitorRegistrations(THREAD_NAME_PREFIX + threadSuffix); });
  124. }
  125. }
  126. public void Stop()
  127. {
  128. m_sendRegisters = false;
  129. }
  130. /// <summary>
  131. /// Retrieve a list of accounts that the agent will register for from the database and then monitor them and any additional ones inserted.
  132. /// </summary>
  133. private void MonitorRegistrations(string threadName)
  134. {
  135. try
  136. {
  137. Thread.CurrentThread.Name = threadName;
  138. while (m_sendRegisters)
  139. {
  140. try
  141. {
  142. List<SIPProviderBinding> bindingsList = GetNextBindings(NUMBER_BINDINGS_PER_DB_ROUNDTRIP);
  143. while (bindingsList != null && bindingsList.Count > 0)
  144. {
  145. ConcurrentBag<SIPProviderBinding> bindings = new ConcurrentBag<SIPProviderBinding>(bindingsList);
  146. foreach (SIPProviderBinding binding in bindings)
  147. {
  148. DateTime startTime = DateTime.Now;
  149. // Remove any previously in progress bindings from the in transit list. The pevious attempt should be well gone but make sure anyway.
  150. RemoveCachedBinding(binding.Id);
  151. // Get the SIPProvider for the binding.
  152. SIPProvider provider = GetSIPProviderById_External(binding.ProviderId);
  153. //SIPProvider provider = GetSIPProviderByDirectQuery_External(m_selectSIPProvider, new SqlParameter("1", binding.ProviderId));
  154. if (provider == null || !provider.RegisterEnabled || !provider.RegisterAdminEnabled || binding.BindingExpiry == 0)
  155. {
  156. // The SIP Provider entry has been removed or disabled: send a zero expiry register and delete the binding.
  157. // It's CRITICAL that this check is done to prevent bindings being maintained after a user has deleted the
  158. // provider or turned off registrations for it.
  159. if (binding.IsRegistered && provider != null)
  160. {
  161. // Set the binding fields from the provider so the zero expiry register request can be sent.
  162. binding.SetProviderFields(provider);
  163. // If the binding expiry is 0 the agent is removing an existing binding in which case it should use the original settings
  164. // it sent the registration with.
  165. if (binding.RegistrarSIPEndPoint != null)
  166. {
  167. binding.LocalSIPEndPoint = (m_outboundProxy != null) ? m_sipTransport.GetDefaultSIPEndPoint(m_outboundProxy.Protocol) : m_sipTransport.GetDefaultSIPEndPoint(binding.RegistrarSIPEndPoint.Protocol);
  168. // Want to remove this binding, send a register with a 0 expiry.
  169. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterInProgress, "Sending zero expiry register for " + binding.Owner + " and " + binding.ProviderName + " to " + binding.RegistrarSIPEndPoint.ToString() + ".", binding.Owner));
  170. SendInitialRegister(provider, binding, binding.LocalSIPEndPoint, binding.RegistrarSIPEndPoint, 0);
  171. }
  172. }
  173. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRemoval, "Deleting SIP Provider Binding for " + binding.Owner + " and " + binding.ProviderName + " for SIP Provider " + binding.ProviderName + ".", binding.Owner));
  174. m_bindingPersistor.Delete(binding);
  175. }
  176. else if (binding.LastRegisterAttempt != null && binding.LastRegisterAttempt.Value > DateTimeOffset.UtcNow.AddSeconds(REGISTER_MINIMUM_ATTEMPT * -1))
  177. {
  178. // Registration requests too frequent. The attempt will be delayed.
  179. // Set the binding fields from the provider in case any have changed since the binding was last stored.
  180. binding.SetProviderFields(provider);
  181. double lastAttemptSecs = DateTimeOffset.UtcNow.Subtract(binding.LastRegisterAttempt.Value).TotalSeconds;
  182. int delaySeconds = REGISTER_MINIMUM_ATTEMPT - (int)lastAttemptSecs;
  183. binding.RegistrationFailureMessage = "Registration attempts too frequent, delaying " + delaySeconds + "s.";
  184. binding.NextRegistrationTime = DateTimeOffset.UtcNow.AddSeconds(delaySeconds);
  185. m_bindingPersistor.Update(binding);
  186. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRefresh, "SIP Provider registration request for " + binding.ProviderName + " too frequent, delaying by " + delaySeconds + "s to " + binding.NextRegistrationTime.ToString("o") + ".", binding.Owner));
  187. }
  188. else
  189. {
  190. // Set the binding fields from the provider in case any have changed since the binding was last stored.
  191. binding.SetProviderFields(provider);
  192. try
  193. {
  194. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterInProgress, "Checking SIP Provider registration for " + binding.ProviderName + ".", binding.Owner));
  195. SIPDNSLookupResult lookupResult = SIPDNSManager.ResolveSIPService(binding.RegistrarServer, true);
  196. if (lookupResult.LookupError != null)
  197. {
  198. // A DNS error indicates the registrar cannot be resolved, permanently disable it.
  199. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterFailed, "DNS resolution for " + binding.RegistrarServer.ToString() + " failed. " + lookupResult.LookupError + ". DISABLING.", binding.Owner));
  200. DisableSIPProviderRegistration(provider.Id, "Could not resolve registrar " + binding.RegistrarServer.ToString() + ". DNS " + lookupResult.LookupError + ".");
  201. m_bindingPersistor.Delete(binding);
  202. }
  203. else if (lookupResult.ATimedoutAt != null)
  204. {
  205. if (binding.LastRegisterTime == null && DateTimeOffset.UtcNow.Subtract(provider.LastUpdate).TotalMinutes > DNS_FAILURE_RETRY_WINDOW)
  206. {
  207. // The DNS retry window has expired and the binding has never successfully registered so it's highly likely it's an invalid hostname.
  208. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterFailed, "Could not resolve registrar " + binding.RegistrarServer.ToString() + " after trying for " + DNS_FAILURE_RETRY_WINDOW + " minutes. DISABLING.", binding.Owner));
  209. DisableSIPProviderRegistration(provider.Id, "Could not resolve registrar " + binding.RegistrarServer.ToString() + " after trying for " + DNS_FAILURE_RETRY_WINDOW + " minutes.");
  210. m_bindingPersistor.Delete(binding);
  211. }
  212. else
  213. {
  214. // DNS timeouts can be caused by network or server issues. Delay the registration to give the problem a chance to clear up.
  215. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterInProgress, "DNS resolution for " + binding.RegistrarServer.ToString() + " timed out, delaying by " + REGISTER_DNSTIMEOUT_RETRY_INTERVAL + "s.", binding.Owner));
  216. binding.RegistrationFailureMessage = "DNS resolution for " + binding.RegistrarServer.ToString() + " timed out.";
  217. binding.NextRegistrationTime = DateTimeOffset.UtcNow.AddSeconds(REGISTER_DNSTIMEOUT_RETRY_INTERVAL);
  218. m_bindingPersistor.Update(binding);
  219. }
  220. }
  221. else if (lookupResult.Pending)
  222. {
  223. // DNS lookup is pending, delay the registration attempt until the lookup is likely to have been completed.
  224. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterInProgress, "DNS Manager does not currently have an entry for " + binding.RegistrarServer.ToString() + ", delaying " + REGISTER_EMPTYDNS_RETRY_INTERVAL + "s.", binding.Owner));
  225. binding.RegistrationFailureMessage = "DNS resolution for " + binding.RegistrarServer.ToString() + " is pending.";
  226. binding.NextRegistrationTime = DateTimeOffset.UtcNow.AddSeconds(REGISTER_EMPTYDNS_RETRY_INTERVAL);
  227. m_bindingPersistor.Update(binding);
  228. }
  229. else
  230. {
  231. binding.RegistrarSIPEndPoint = lookupResult.GetSIPEndPoint();
  232. string ipAddress = binding.RegistrarSIPEndPoint.Address.ToString();
  233. if (m_disallowPrivateIPRegistrars &&
  234. (IPAddress.IsLoopback(binding.RegistrarSIPEndPoint.Address) ||
  235. IPSocket.IsPrivateAddress(ipAddress) ||
  236. binding.RegistrarSIPEndPoint.Address.ToString() == SIPTransport.BlackholeAddress.ToString()))
  237. {
  238. // The registrar resolved to a private, loopback or 0.0.0.0 address, delete the binding and disable the provider registration.
  239. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterFailed, "DNS resolution for " + binding.RegistrarServer.ToString() + " resolved to a private address of " + ipAddress + ". DISABLING.", binding.Owner));
  240. DisableSIPProviderRegistration(provider.Id, "Registrar resovled to a disallowed private IP address of " + ipAddress + ".");
  241. m_bindingPersistor.Delete(binding);
  242. }
  243. else
  244. {
  245. binding.LastRegisterAttempt = DateTimeOffset.UtcNow;
  246. binding.NextRegistrationTime = DateTimeOffset.UtcNow.AddSeconds(REGISTER_FAILURERETRY_INTERVAL);
  247. binding.LocalSIPEndPoint = (m_outboundProxy != null) ? m_sipTransport.GetDefaultSIPEndPoint(m_outboundProxy.Protocol) : m_sipTransport.GetDefaultSIPEndPoint(binding.RegistrarServer.Protocol);
  248. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterInProgress, "Sending initial register for " + binding.Owner + " and " + binding.ProviderName + " to " + binding.RegistrarSIPEndPoint.ToString() + ".", binding.Owner));
  249. m_bindingPersistor.Update(binding);
  250. SendInitialRegister(provider, binding, binding.LocalSIPEndPoint, binding.RegistrarSIPEndPoint, binding.BindingExpiry);
  251. }
  252. }
  253. }
  254. catch (Exception regExcp)
  255. {
  256. logger.Error("Exception attempting to register provider " + binding.ProviderName + ". " + regExcp.Message + " for " + binding.Owner + ".");
  257. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.Error, "Exception attempting to register provider " + binding.ProviderName + ". " + regExcp.Message, binding.Owner));
  258. try
  259. {
  260. binding.LastRegisterAttempt = DateTimeOffset.UtcNow;
  261. binding.NextRegistrationTime = DateTimeOffset.UtcNow.AddSeconds(REGISTER_FAILURERETRY_INTERVAL);
  262. binding.RegistrationFailureMessage = (regExcp.Message != null && regExcp.Message.Length > 1000) ? "Exception: " + regExcp.Message.Substring(0, 1000) : "Exception: " + regExcp.Message;
  263. m_bindingPersistor.Update(binding);
  264. }
  265. catch (Exception persistExcp)
  266. {
  267. logger.Error("Exception SIPRegistrationAgent persisting after exception. " + persistExcp.Message);
  268. }
  269. }
  270. }
  271. //logger.Debug("Binding entry processing took " + DateTime.Now.Subtract(startTime).TotalMilliseconds.ToString("0") + "ms.");
  272. m_bindingsProcessedCount++;
  273. }
  274. bindingsList = GetNextBindings(NUMBER_BINDINGS_PER_DB_ROUNDTRIP);
  275. }
  276. }
  277. catch (Exception persistExcp)
  278. {
  279. logger.Error("Exception MonitorRegistrations GettingBinding. " + persistExcp.Message);
  280. }
  281. Thread.Sleep(REGISTRATION_RENEWAL_PERIOD);
  282. }
  283. }
  284. catch (Exception excp)
  285. {
  286. logger.Error("Exception MonitorRegistrations. " + excp.Message);
  287. }
  288. }
  289. private List<SIPProviderBinding> GetNextBindings(int count)
  290. {
  291. try
  292. {
  293. // No point having two threads try and use database at the same time.
  294. lock (this)
  295. {
  296. DateTime startTime = DateTime.Now;
  297. List<SIPProviderBinding> bindings = null;
  298. using (var trans = new TransactionScope())
  299. {
  300. bindings = m_bindingPersistor.Get(b => b.NextRegistrationTime <= DateTimeOffset.UtcNow, "nextregistrationtime", 0, count);
  301. if (bindings != null && bindings.Count > 0)
  302. {
  303. foreach (SIPProviderBinding binding in bindings)
  304. {
  305. m_bindingPersistor.UpdateProperty(binding.Id, "NextRegistrationTime", DateTimeOffset.UtcNow.AddSeconds(REGISTER_FAILURERETRY_INTERVAL));
  306. }
  307. }
  308. trans.Complete();
  309. }
  310. //logger.Debug("GetNextBindings took " + DateTime.Now.Subtract(startTime).TotalMilliseconds.ToString("0") + ".");
  311. return bindings;
  312. }
  313. }
  314. catch (Exception excp)
  315. {
  316. logger.Error("Exception GetNextBindings (" + excp.GetType().ToString() + "). " + excp.Message);
  317. return null;
  318. }
  319. }
  320. private void SendInitialRegister(SIPProvider sipProvider, SIPProviderBinding binding, SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, int expirySeconds)
  321. {
  322. try
  323. {
  324. m_inTransitBindings.AddOrUpdate(binding.Id, binding, (s, i) => binding);
  325. binding.CSeq++;
  326. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterInProgress, "Initiating registration for " + binding.Owner + " on " + binding.RegistrarServer.ToString() + ".", binding.Owner));
  327. SIPRequest regRequest = GetRegistrationRequest(sipProvider, binding, localSIPEndPoint, expirySeconds, remoteEndPoint);
  328. SIPNonInviteTransaction regTransaction = m_sipTransport.CreateNonInviteTransaction(regRequest, binding.RegistrarSIPEndPoint, localSIPEndPoint, m_outboundProxy);
  329. regTransaction.NonInviteTransactionFinalResponseReceived += (lep, rep, tn, rsp) => { ThreadPool.QueueUserWorkItem(delegate { ServerResponseReceived(lep, rep, tn, rsp); }); };
  330. regTransaction.NonInviteTransactionTimedOut += (tn) => { ThreadPool.QueueUserWorkItem(delegate { RegistrationTimedOut(tn); }); };
  331. m_sipTransport.SendSIPReliable(regTransaction);
  332. SIPSorceryPerformanceMonitor.IncrementCounter(SIPSorceryPerformanceMonitor.REGISTRATION_AGENT_REGISTRATIONS_PER_SECOND);
  333. }
  334. catch (Exception excp)
  335. {
  336. logger.Error("Exception SendInitialRegister for " + binding.Owner + " and " + binding.RegistrarServer.ToString() + ". " + excp.Message);
  337. RemoveCachedBinding(binding.Id);
  338. }
  339. }
  340. private void RegistrationTimedOut(SIPTransaction sipTransaction)
  341. {
  342. try
  343. {
  344. SIPRequest sipRequest = sipTransaction.TransactionRequest;
  345. Guid callIdGuid = new Guid(sipRequest.Header.CallId);
  346. SIPProviderBinding binding = GetBinding(callIdGuid);
  347. if (binding != null && binding.BindingExpiry != 0)
  348. {
  349. RemoveCachedBinding(binding.Id);
  350. int retryInterval = REGISTER_FAILURERETRY_INTERVAL + Crypto.GetRandomInt(0, REGISTER_FAILURERETRY_INTERVAL);
  351. binding.RegistrationFailureMessage = "Registration to " + binding.RegistrarSIPEndPoint.ToString() + " timed out.";
  352. binding.NextRegistrationTime = DateTimeOffset.UtcNow.AddSeconds(retryInterval);
  353. binding.IsRegistered = false;
  354. m_bindingPersistor.Update(binding);
  355. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterFailed, "Registration timed out for " + binding.Owner + " and provider " + binding.ProviderName + " registering to " + binding.RegistrarSIPEndPoint.ToString() + ", next attempt in " + retryInterval + "s.", binding.Owner));
  356. FireProxyLogEvent(new SIPMonitorMachineEvent(SIPMonitorMachineEventTypesEnum.SIPRegistrationAgentBindingUpdate, binding.Owner, binding.RegistrarSIPEndPoint, binding.ProviderId.ToString()));
  357. }
  358. else
  359. {
  360. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.Warn, "Registration request timed for unmatched call originally to " + sipTransaction.RemoteEndPoint + ".", null));
  361. }
  362. }
  363. catch (Exception excp)
  364. {
  365. logger.Error("Exception RegistrationTimedOut. " + excp.Message);
  366. RemoveCachedBinding(sipTransaction.TransactionRequest.Header.CallId);
  367. }
  368. }
  369. /// <summary>
  370. /// The event handler for responses to the initial register request.
  371. /// </summary>
  372. private void ServerResponseReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPTransaction sipTransaction, SIPResponse sipResponse)
  373. {
  374. try
  375. {
  376. string fromURI = (sipResponse.Header.From != null && sipResponse.Header.From.FromURI != null) ? sipResponse.Header.From.FromURI.ToString() : "NO FROM URI";
  377. string toURI = (sipResponse.Header.To != null && sipResponse.Header.To.ToURI != null) ? sipResponse.Header.To.ToURI.ToString() : "NO TO URI";
  378. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterInProgress, "Server response " + sipResponse.Status + " received for " + fromURI + " and " + toURI + ".", null));
  379. if (sipResponse.Header.CallId.IsNullOrBlank())
  380. {
  381. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.Warn, "An " + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + " from " + remoteEndPoint + " was received for with an empty or missing Call-ID header.", null));
  382. }
  383. else
  384. {
  385. Guid callIdGuid = new Guid(sipResponse.Header.CallId);
  386. SIPProviderBinding binding = GetBinding(callIdGuid);
  387. if (binding != null)
  388. {
  389. if (sipResponse.Status == SIPResponseStatusCodesEnum.ProxyAuthenticationRequired || sipResponse.Status == SIPResponseStatusCodesEnum.Unauthorised)
  390. {
  391. if (sipResponse.Header.AuthenticationHeader != null)
  392. {
  393. SIPRequest authenticatedRequest = GetAuthenticatedRegistrationRequest(binding, sipTransaction.TransactionRequest, sipResponse);
  394. SIPNonInviteTransaction regAuthTransaction = m_sipTransport.CreateNonInviteTransaction(authenticatedRequest, binding.RegistrarSIPEndPoint, localSIPEndPoint, m_outboundProxy);
  395. regAuthTransaction.NonInviteTransactionFinalResponseReceived += (lep, rep, tn, rsp) => { ThreadPool.QueueUserWorkItem(delegate { AuthResponseReceived(lep, rep, tn, rsp); }); };
  396. regAuthTransaction.NonInviteTransactionTimedOut += (tn) => { ThreadPool.QueueUserWorkItem(delegate { RegistrationTimedOut(tn); }); };
  397. m_sipTransport.SendSIPReliable(regAuthTransaction);
  398. }
  399. else if (binding.BindingExpiry != 0)
  400. {
  401. RemoveCachedBinding(binding.Id);
  402. binding.IsRegistered = false;
  403. binding.RegistrationFailureMessage = "Server did not provide auth header, check realm.";
  404. binding.NextRegistrationTime = DateTimeOffset.UtcNow.AddSeconds(REGISTER_FAILURERETRY_INTERVAL);
  405. m_bindingPersistor.Update(binding);
  406. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterFailed, "Registration failed for " + binding.Owner + " on " + binding.RegistrarServer.ToString() + ", the server did not respond with an authentication header, check realm.", binding.Owner));
  407. }
  408. }
  409. else if (binding.BindingExpiry != 0)
  410. {
  411. // Non 401 or 407 responses mean the registration attempt is finished and the call-id will be removed and state updated.
  412. if (sipResponse.Status == SIPResponseStatusCodesEnum.Ok)
  413. {
  414. // Successful registration.
  415. OkResponseReceived(sipTransaction, remoteEndPoint, sipResponse);
  416. }
  417. else if (sipResponse.Status == SIPResponseStatusCodesEnum.Forbidden || sipResponse.Status == SIPResponseStatusCodesEnum.NotFound)
  418. {
  419. // SIP account does not appear to exist. Disable registration attempts until user intervenes to correct.
  420. RemoveCachedBinding(binding.Id);
  421. m_bindingPersistor.Delete(binding);
  422. DisableSIPProviderRegistration(binding.ProviderId, "Authentication failed (" + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + ").");
  423. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterFailed, "Registration failed with " + sipResponse.Status + " for " + binding.Owner + " on " + binding.RegistrarServer.ToString() + ", DISABLING.", binding.Owner));
  424. }
  425. else if (sipResponse.Status == SIPResponseStatusCodesEnum.IntervalTooBrief ||
  426. (sipResponse.Status == SIPResponseStatusCodesEnum.BusyEverywhere && sipResponse.ReasonPhrase == "Too Frequent Requests")) // FWD uses this to indicate it doesn't like the timing.
  427. {
  428. RemoveCachedBinding(binding.Id);
  429. binding.IsRegistered = false;
  430. binding.RegistrationFailureMessage = "Registration failed (" + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + ").";
  431. binding.NextRegistrationTime = DateTimeOffset.UtcNow.AddSeconds(REGISTER_FAILURERETRY_INTERVAL + Crypto.GetRandomInt(0, REGISTER_FAILURERETRY_INTERVAL));
  432. m_bindingPersistor.Update(binding);
  433. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterFailed, "Registration failed for " + binding.Owner + " on " + binding.RegistrarServer.ToString() + " due to " + sipResponse.ReasonPhrase + ".", binding.Owner));
  434. }
  435. else
  436. {
  437. RemoveCachedBinding(binding.Id);
  438. binding.IsRegistered = false;
  439. binding.RegistrationFailureMessage = "Registration failed (" + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + ").";
  440. binding.NextRegistrationTime = DateTimeOffset.UtcNow.AddSeconds(REGISTER_FAILURERETRY_INTERVAL);
  441. m_bindingPersistor.Update(binding);
  442. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterFailed, "Registration failed with " + sipResponse.Status + " for " + binding.Owner + " on " + binding.RegistrarServer.ToString() + ".", binding.Owner));
  443. }
  444. }
  445. }
  446. else
  447. {
  448. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.Warn, "An " + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + " from " + remoteEndPoint + " was received for an unknown registration.", null));
  449. }
  450. }
  451. }
  452. catch (Exception excp)
  453. {
  454. logger.Error("Exception SIPRegistrationAgent ServerResponseReceived (" + remoteEndPoint + "). " + excp.Message);
  455. RemoveCachedBinding(sipResponse.Header.CallId);
  456. }
  457. }
  458. /// <summary>
  459. /// The event handler for responses to the authenticated register request.
  460. /// </summary>
  461. private void AuthResponseReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPTransaction sipTransaction, SIPResponse sipResponse)
  462. {
  463. try
  464. {
  465. string fromURI = (sipResponse.Header.From != null && sipResponse.Header.From.FromURI != null) ? sipResponse.Header.From.FromURI.ToString() : "NO FROM URI";
  466. string toURI = (sipResponse.Header.To != null && sipResponse.Header.To.ToURI != null) ? sipResponse.Header.To.ToURI.ToString() : "NO TO URI";
  467. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterInProgress, "Server auth response " + sipResponse.Status + " received for " + fromURI + " and " + toURI + ".", null));
  468. if (sipResponse.Header.CallId.IsNullOrBlank())
  469. {
  470. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.Warn, "An " + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + " from " + remoteEndPoint + " was received for with an empty or missing Call-ID header.", null));
  471. }
  472. else
  473. {
  474. Guid callIdGuid = new Guid(sipResponse.Header.CallId);
  475. SIPProviderBinding binding = GetBinding(callIdGuid);
  476. if (binding != null && binding.BindingExpiry != 0)
  477. {
  478. if (sipResponse.Status == SIPResponseStatusCodesEnum.Ok)
  479. {
  480. OkResponseReceived(sipTransaction, remoteEndPoint, sipResponse);
  481. }
  482. else if (sipResponse.Status == SIPResponseStatusCodesEnum.IntervalTooBrief)
  483. {
  484. RemoveCachedBinding(binding.Id);
  485. binding.IsRegistered = false;
  486. binding.RegistrationFailureMessage = "Registration failed (" + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + ").";
  487. binding.NextRegistrationTime = DateTimeOffset.UtcNow.AddSeconds(REGISTER_FAILURERETRY_INTERVAL + Crypto.GetRandomInt(0, REGISTER_FAILURERETRY_INTERVAL));
  488. m_bindingPersistor.Update(binding);
  489. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterFailed, "Registration failed for " + binding.Owner + " on " + binding.RegistrarServer.ToString() + " due to " + sipResponse.ReasonPhrase + ".", binding.Owner));
  490. }
  491. else if (sipResponse.Status == SIPResponseStatusCodesEnum.Forbidden || sipResponse.Status == SIPResponseStatusCodesEnum.NotFound)
  492. {
  493. // SIP account does not appear to exist. Disable registration attempts until user intervenes to correct.
  494. RemoveCachedBinding(binding.Id);
  495. m_bindingPersistor.Delete(binding);
  496. DisableSIPProviderRegistration(binding.ProviderId, "Username was rejected by SIP Provider with " + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + ".");
  497. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterFailed, "Registration failed with " + sipResponse.Status + " for " + binding.Owner + " on " + binding.RegistrarServer.ToString() + ", DISABLING.", binding.Owner));
  498. }
  499. else
  500. {
  501. RemoveCachedBinding(binding.Id);
  502. binding.IsRegistered = false;
  503. binding.NextRegistrationTime = DateTimeOffset.UtcNow.AddSeconds(REGISTER_FAILURERETRY_INTERVAL);
  504. binding.RegistrationFailureMessage = "Registration failed (" + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + ").";
  505. m_bindingPersistor.Update(binding);
  506. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterFailed, "Registration failed for " + binding.Owner + " on " + binding.RegistrarServer.ToString() + " with " + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + ".", binding.Owner));
  507. }
  508. FireProxyLogEvent(new SIPMonitorMachineEvent(SIPMonitorMachineEventTypesEnum.SIPRegistrationAgentBindingUpdate, binding.Owner, binding.RegistrarSIPEndPoint, binding.ProviderId.ToString()));
  509. }
  510. }
  511. }
  512. catch (Exception excp)
  513. {
  514. logger.Error("Exception SIPRegistrationAgent AuthResponseReceived. " + excp.Message);
  515. RemoveCachedBinding(sipResponse.Header.CallId);
  516. }
  517. }
  518. private void OkResponseReceived(SIPTransaction sipTransaction, SIPEndPoint remoteEndPoint, SIPResponse sipResponse)
  519. {
  520. try
  521. {
  522. Guid callIdGuid = new Guid(sipResponse.Header.CallId);
  523. SIPProviderBinding binding = GetBinding(callIdGuid);
  524. RemoveCachedBinding(callIdGuid);
  525. if (binding != null)
  526. {
  527. // Updated contacts list.
  528. // Find the contact in the list that matches the one being maintained by this agent in order to determine the expiry value.
  529. int headerExpires = sipResponse.Header.Expires;
  530. int contactExpires = -1;
  531. if (sipResponse.Header.Contact != null && sipResponse.Header.Contact.Count > 0)
  532. {
  533. foreach (SIPContactHeader contactHeader in sipResponse.Header.Contact)
  534. {
  535. if (contactHeader.ContactURI.Parameters.Get(m_regAgentContactId) == binding.BindingSIPURI.Parameters.Get(m_regAgentContactId))
  536. {
  537. contactExpires = contactHeader.Expires;
  538. break;
  539. }
  540. }
  541. }
  542. if (contactExpires != -1)
  543. {
  544. binding.BindingExpiry = contactExpires;
  545. }
  546. else if (headerExpires != -1)
  547. {
  548. binding.BindingExpiry = headerExpires;
  549. }
  550. if (binding.BindingExpiry < REGISTER_MINIMUM_EXPIRY)
  551. {
  552. // Make sure we don't do a 3CX and send registration floods.
  553. binding.BindingExpiry = REGISTER_MINIMUM_EXPIRY;
  554. }
  555. binding.NextRegistrationTime = DateTimeOffset.UtcNow.AddSeconds(binding.BindingExpiry - REGISTRATION_HEAD_TIME);
  556. binding.ContactsList = sipResponse.Header.Contact;
  557. binding.IsRegistered = true;
  558. binding.LastRegisterTime = DateTimeOffset.UtcNow;
  559. binding.RegistrationFailureMessage = null;
  560. m_bindingPersistor.Update(binding);
  561. UpdateSIPProviderOutboundProxy(binding, sipResponse.Header.ProxyReceivedOn);
  562. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegistered, "Contact successfully registered for " + binding.Owner + " on " + binding.RegistrarServer.ToString() + ", expiry " + binding.BindingExpiry + "s.", binding.Owner));
  563. }
  564. }
  565. catch (Exception excp)
  566. {
  567. logger.Error("Exception SIPRegistrationAgent OkResponseReceived. " + excp.Message);
  568. RemoveCachedBinding(sipResponse.Header.CallId);
  569. }
  570. }
  571. private SIPRequest GetRegistrationRequest(SIPProvider sipProvider, SIPProviderBinding binding, SIPEndPoint localSIPEndPoint, int expiry, SIPEndPoint registrarEndPoint)
  572. {
  573. try
  574. {
  575. if (!binding.BindingSIPURI.Parameters.Has(m_regAgentContactId))
  576. {
  577. binding.BindingSIPURI.Parameters.Set(m_regAgentContactId, Crypto.GetRandomString(6));
  578. }
  579. string realm = binding.RegistrarRealm;
  580. SIPURI registerURI = SIPURI.ParseSIPURIRelaxed(realm);
  581. SIPURI regUserURI = SIPURI.ParseSIPURIRelaxed(sipProvider.ProviderUsername + "@" + realm);
  582. SIPFromHeader fromHeader = new SIPFromHeader(null, regUserURI, CallProperties.CreateNewTag());
  583. SIPToHeader toHeader = new SIPToHeader(null, regUserURI, null);
  584. SIPContactHeader contactHeader = new SIPContactHeader(null, binding.BindingSIPURI);
  585. //contactHeader.Expires = binding.BindingExpiry;
  586. string callId = binding.Id.ToString();
  587. int cseq = ++binding.CSeq;
  588. SIPRequest registerRequest = new SIPRequest(SIPMethodsEnum.REGISTER, registerURI);
  589. registerRequest.LocalSIPEndPoint = localSIPEndPoint;
  590. SIPHeader header = new SIPHeader(contactHeader, fromHeader, toHeader, cseq, callId);
  591. header.CSeqMethod = SIPMethodsEnum.REGISTER;
  592. header.UserAgent = m_userAgentString;
  593. header.Expires = binding.BindingExpiry;
  594. SIPViaHeader viaHeader = new SIPViaHeader(localSIPEndPoint, CallProperties.CreateBranchId());
  595. header.Vias.PushViaHeader(viaHeader);
  596. SIPRoute registrarRoute = new SIPRoute(new SIPURI(binding.RegistrarServer.Scheme, registrarEndPoint), true);
  597. header.Routes.PushRoute(registrarRoute);
  598. if (sipProvider != null && !sipProvider.CustomHeaders.IsNullOrBlank())
  599. {
  600. string[] customerHeadersList = sipProvider.CustomHeaders.Split(SIPProvider.CUSTOM_HEADERS_SEPARATOR);
  601. if (customerHeadersList != null && customerHeadersList.Length > 0)
  602. {
  603. foreach (string customHeader in customerHeadersList)
  604. {
  605. if (customHeader.IndexOf(':') == -1)
  606. {
  607. logger.Debug("Skipping custom header due to missing colon, " + customHeader + ".");
  608. continue;
  609. }
  610. else
  611. {
  612. string headerName = customHeader.Substring(0, customHeader.IndexOf(':'));
  613. if (headerName != null && Regex.Match(headerName.Trim(), "(Via|From|To|Contact|CSeq|Call-ID|Max-Forwards|Content)", RegexOptions.IgnoreCase).Success)
  614. {
  615. logger.Debug("Skipping custom header due to an non-permitted string in header name, " + customHeader + ".");
  616. continue;
  617. }
  618. else
  619. {
  620. if (headerName == SIPConstants.SIP_USERAGENT_STRING)
  621. {
  622. header.UserAgent = custom

Large files files are truncated, but you can click here to view the full file