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

Language C# Lines 826
MD5 Hash 17ba88029310e4be9bc617bf69643cef
Repository https://github.com/thecc4re/sipsorcery-mono.git View Raw File
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
// ============================================================================
// FileName: SIPRegistrationAgent.cs
//
// Description:
// Registration agent daemon to maintain SIP registrations with multiple SIP
// Registrar servers.
//
// Author(s):
// Aaron Clauson
//
// History:
// 13 Nov 2006	Aaron Clauson	Created.
// 19 Oct 2007  Aaron Clauson   Incorporated DNS management to stop sipswitch stalling on invalid host names.
// 17 May 2008  Aaron Clauson   Refactored UserRegistration class into its own class file.
// 01 Mar 2011  Aaron Clauson   Switched to using concurrent collections.
//
// License: 
// This software is licensed under the BSD License http://www.opensource.org/licenses/bsd-license.php
//
// Copyright (c) Aaron Clauson (aaron@sipsorcery.com), SIP Sorcery Ltd, Hobart, Australia (www.sipsorcery.com)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that 
// the following conditions are met:
//
// Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 
// Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 
// disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of SIP Sorcery Ltd. 
// nor the names of its contributors may be used to endorse or promote products derived from this software without specific 
// prior written permission. 
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
// BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
// POSSIBILITY OF SUCH DAMAGE.
// ============================================================================

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Net;
using System.Runtime.Serialization;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Transactions;
using System.Xml;
using SIPSorcery.Net;
using SIPSorcery.Persistence;
using SIPSorcery.SIP;
using SIPSorcery.SIP.App;
using SIPSorcery.Sys;
using Heijden.DNS;
using log4net;

namespace SIPSorcery.Servers
{
    public class SIPRegistrationAgentCore
    {
        public const int REGISTRATION_RENEWAL_PERIOD = 1000;        // Time in milliseconds between the registration agent checking registrations.
        public const int REGISTRATION_HEAD_TIME = 5;                // Time in seconds to go to next registration to initate.
        public const int REGISTER_FAILURERETRY_INTERVAL = 300;      // Number of seconds between consecutive register requests in the event of failures or timeouts.
        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.
        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.
        public const int REGISTER_CHECKTIME_THRESHOLD = 3;          // Time the user registration checks should be taking less than. If exceeded a log message is produced.
        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.
        public const int REGISTER_DELETION_TIMEOUT = 60;            // Number of seconds a deletion request will timeout after.
        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.
        public const int REGISTER_MINIMUM_ATTEMPT = 50;             // The minimum interval at which consecutive registration attempts can occur.
        //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.
        //private const int MAX_DNS_FAILURE_ATTEMPTS = 6;
        //private const string DNS_FAILURE_MESSAGE_PREFIX = "DNS Failure:";
        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.
        private const string THREAD_NAME_PREFIX = "regagent-";
        private const int NUMBER_BINDINGS_PER_DB_ROUNDTRIP = 20;
        private const int MAX_NUMBER_INTRANSIT_BINDINGS = 100000;    // The maximum number of in transit REGISTER bindings that will be stored.

        private static ILog logger = AppState.GetLogger("sipregagent");
        private static readonly string m_userAgentString = SIPConstants.SIP_USERAGENT_STRING;
        private static readonly string m_regAgentContactId = SIPProviderBinding.REGAGENT_CONTACT_ID_KEY;

        private bool m_disallowPrivateIPRegistrars;     // If set to true any SIP providers that resolve to a private or loopback IP address will be disabled.
        private bool m_sendRegisters = true;             // While true the register agent thread will send out register requests to maintain it's registrations.
        private int m_bindingsProcessedCount = 0;

        private SIPTransport m_sipTransport;
        private SIPEndPoint m_outboundProxy;

        private ConcurrentDictionary<Guid, SIPProviderBinding> m_inTransitBindings;

        private SIPMonitorLogDelegate StatefulProxyLogEvent_External;
        private SIPAssetGetByIdDelegate<SIPProvider> GetSIPProviderById_External;
        private SIPAssetUpdateDelegate<SIPProvider> UpdateSIPProvider_External;
        private SIPAssetUpdatePropertyDelegate<SIPProvider> UpdateSIPProviderProperty_External;
        private SIPAssetPersistor<SIPProviderBinding> m_bindingPersistor;

        public SIPRegistrationAgentCore(
            SIPMonitorLogDelegate logDelegate,
            SIPTransport sipTransport,
            SIPEndPoint outboundProxy,
            SIPAssetGetByIdDelegate<SIPProvider> getSIPProviderById,
            SIPAssetUpdateDelegate<SIPProvider> updateSIPProvider,
            SIPAssetUpdatePropertyDelegate<SIPProvider> updateSIPProviderProperty,
            SIPAssetPersistor<SIPProviderBinding> bindingPersistor,
            bool disAllowPrivateIPRegistrars)
        {
            StatefulProxyLogEvent_External = logDelegate;
            GetSIPProviderById_External = getSIPProviderById;
            UpdateSIPProvider_External = updateSIPProvider;
            UpdateSIPProviderProperty_External = updateSIPProviderProperty;
            m_bindingPersistor = bindingPersistor;
            m_sipTransport = sipTransport;
            m_outboundProxy = outboundProxy;
            m_disallowPrivateIPRegistrars = disAllowPrivateIPRegistrars;
        }

        public void Start(int threadCount)
        {
            logger.Debug("SIPRegistrationAgent thread started with " + threadCount + " threads.");

            m_inTransitBindings = new ConcurrentDictionary<Guid, SIPProviderBinding>(threadCount, MAX_NUMBER_INTRANSIT_BINDINGS);

            for (int index = 1; index <= threadCount; index++)
            {
                string threadSuffix = index.ToString();
                ThreadPool.QueueUserWorkItem(delegate { MonitorRegistrations(THREAD_NAME_PREFIX + threadSuffix); });
            }
        }

        public void Stop()
        {
            m_sendRegisters = false;
        }

        /// <summary>
        /// Retrieve a list of accounts that the agent will register for from the database and then monitor them and any additional ones inserted.
        /// </summary>
        private void MonitorRegistrations(string threadName)
        {
            try
            {
                Thread.CurrentThread.Name = threadName;

                while (m_sendRegisters)
                {
                    try
                    {
                        List<SIPProviderBinding> bindingsList = GetNextBindings(NUMBER_BINDINGS_PER_DB_ROUNDTRIP);

                        while (bindingsList != null && bindingsList.Count > 0)
                        {
                            ConcurrentBag<SIPProviderBinding> bindings = new ConcurrentBag<SIPProviderBinding>(bindingsList);

                            foreach (SIPProviderBinding binding in bindings)
                            {
                                DateTime startTime = DateTime.Now;

                                // Remove any previously in progress bindings from the in transit list. The pevious attempt should be well gone but make sure anyway.
                                RemoveCachedBinding(binding.Id);

                                // Get the SIPProvider for the binding.
                                SIPProvider provider = GetSIPProviderById_External(binding.ProviderId);
                                //SIPProvider provider = GetSIPProviderByDirectQuery_External(m_selectSIPProvider, new SqlParameter("1", binding.ProviderId));

                                if (provider == null || !provider.RegisterEnabled || !provider.RegisterAdminEnabled || binding.BindingExpiry == 0)
                                {
                                    // The SIP Provider entry has been removed or disabled: send a zero expiry register and delete the binding.
                                    // It's CRITICAL that this check is done to prevent bindings being maintained after a user has deleted the 
                                    // provider or turned off registrations for it.
                                    if (binding.IsRegistered && provider != null)
                                    {
                                        // Set the binding fields from the provider so the zero expiry register request can be sent.
                                        binding.SetProviderFields(provider);

                                        // If the binding expiry is 0 the agent is removing an existing binding in which case it should use the original settings
                                        // it sent the registration with.
                                        if (binding.RegistrarSIPEndPoint != null)
                                        {
                                            binding.LocalSIPEndPoint = (m_outboundProxy != null) ? m_sipTransport.GetDefaultSIPEndPoint(m_outboundProxy.Protocol) : m_sipTransport.GetDefaultSIPEndPoint(binding.RegistrarSIPEndPoint.Protocol);

                                            // Want to remove this binding, send a register with a 0 expiry.
                                            FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterInProgress, "Sending zero expiry register for " + binding.Owner + " and " + binding.ProviderName + " to " + binding.RegistrarSIPEndPoint.ToString() + ".", binding.Owner));
                                            SendInitialRegister(provider, binding, binding.LocalSIPEndPoint, binding.RegistrarSIPEndPoint, 0);
                                        }
                                    }

                                    FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRemoval, "Deleting SIP Provider Binding for " + binding.Owner + " and " + binding.ProviderName + " for SIP Provider " + binding.ProviderName + ".", binding.Owner));
                                    m_bindingPersistor.Delete(binding);
                                }
                                else if (binding.LastRegisterAttempt != null && binding.LastRegisterAttempt.Value > DateTimeOffset.UtcNow.AddSeconds(REGISTER_MINIMUM_ATTEMPT * -1))
                                {
                                    // Registration requests too frequent. The attempt will be delayed.
                                    // Set the binding fields from the provider in case any have changed since the binding was last stored.
                                    binding.SetProviderFields(provider);
                                    double lastAttemptSecs = DateTimeOffset.UtcNow.Subtract(binding.LastRegisterAttempt.Value).TotalSeconds;
                                    int delaySeconds = REGISTER_MINIMUM_ATTEMPT - (int)lastAttemptSecs;
                                    binding.RegistrationFailureMessage = "Registration attempts too frequent, delaying " + delaySeconds + "s.";
                                    binding.NextRegistrationTime = DateTimeOffset.UtcNow.AddSeconds(delaySeconds);
                                    m_bindingPersistor.Update(binding);
                                    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));
                                }
                                else
                                {
                                    // Set the binding fields from the provider in case any have changed since the binding was last stored.
                                    binding.SetProviderFields(provider);

                                    try
                                    {
                                        FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterInProgress, "Checking SIP Provider registration for " + binding.ProviderName + ".", binding.Owner));
                                        SIPDNSLookupResult lookupResult = SIPDNSManager.ResolveSIPService(binding.RegistrarServer, true);
                                        if (lookupResult.LookupError != null)
                                        {
                                            // A DNS error indicates the registrar cannot be resolved, permanently disable it.
                                            FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterFailed, "DNS resolution for " + binding.RegistrarServer.ToString() + " failed. " + lookupResult.LookupError + ". DISABLING.", binding.Owner));
                                            DisableSIPProviderRegistration(provider.Id, "Could not resolve registrar " + binding.RegistrarServer.ToString() + ". DNS " + lookupResult.LookupError + ".");
                                            m_bindingPersistor.Delete(binding);
                                        }
                                        else if (lookupResult.ATimedoutAt != null)
                                        {
                                            if (binding.LastRegisterTime == null && DateTimeOffset.UtcNow.Subtract(provider.LastUpdate).TotalMinutes > DNS_FAILURE_RETRY_WINDOW)
                                            {
                                                // The DNS retry window has expired and the binding has never successfully registered so it's highly likely it's an invalid hostname.
                                                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));
                                                DisableSIPProviderRegistration(provider.Id, "Could not resolve registrar " + binding.RegistrarServer.ToString() + " after trying for " + DNS_FAILURE_RETRY_WINDOW + " minutes.");
                                                m_bindingPersistor.Delete(binding);
                                            }
                                            else
                                            {
                                                // DNS timeouts can be caused by network or server issues. Delay the registration to give the problem a chance to clear up.
                                                FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterInProgress, "DNS resolution for " + binding.RegistrarServer.ToString() + " timed out, delaying by " + REGISTER_DNSTIMEOUT_RETRY_INTERVAL + "s.", binding.Owner));
                                                binding.RegistrationFailureMessage = "DNS resolution for " + binding.RegistrarServer.ToString() + " timed out.";
                                                binding.NextRegistrationTime = DateTimeOffset.UtcNow.AddSeconds(REGISTER_DNSTIMEOUT_RETRY_INTERVAL);
                                                m_bindingPersistor.Update(binding);
                                            }
                                        }
                                        else if (lookupResult.Pending)
                                        {
                                            // DNS lookup is pending, delay the registration attempt until the lookup is likely to have been completed.
                                            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));
                                            binding.RegistrationFailureMessage = "DNS resolution for " + binding.RegistrarServer.ToString() + " is pending.";
                                            binding.NextRegistrationTime = DateTimeOffset.UtcNow.AddSeconds(REGISTER_EMPTYDNS_RETRY_INTERVAL);
                                            m_bindingPersistor.Update(binding);
                                        }
                                        else
                                        {
                                            binding.RegistrarSIPEndPoint = lookupResult.GetSIPEndPoint();
                                            string ipAddress = binding.RegistrarSIPEndPoint.Address.ToString();

                                            if (m_disallowPrivateIPRegistrars &&
                                                (IPAddress.IsLoopback(binding.RegistrarSIPEndPoint.Address) ||
                                                IPSocket.IsPrivateAddress(ipAddress) ||
                                                binding.RegistrarSIPEndPoint.Address.ToString() == SIPTransport.BlackholeAddress.ToString()))
                                            {
                                                // The registrar resolved to a private, loopback or 0.0.0.0 address, delete the binding and disable the provider registration.
                                                FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterFailed, "DNS resolution for " + binding.RegistrarServer.ToString() + " resolved to a private address of " + ipAddress + ". DISABLING.", binding.Owner));
                                                DisableSIPProviderRegistration(provider.Id, "Registrar resovled to a disallowed private IP address of " + ipAddress + ".");
                                                m_bindingPersistor.Delete(binding);
                                            }
                                            else
                                            {
                                                binding.LastRegisterAttempt = DateTimeOffset.UtcNow;
                                                binding.NextRegistrationTime = DateTimeOffset.UtcNow.AddSeconds(REGISTER_FAILURERETRY_INTERVAL);
                                                binding.LocalSIPEndPoint = (m_outboundProxy != null) ? m_sipTransport.GetDefaultSIPEndPoint(m_outboundProxy.Protocol) : m_sipTransport.GetDefaultSIPEndPoint(binding.RegistrarServer.Protocol);
                                                FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterInProgress, "Sending initial register for " + binding.Owner + " and " + binding.ProviderName + " to " + binding.RegistrarSIPEndPoint.ToString() + ".", binding.Owner));
                                                m_bindingPersistor.Update(binding);

                                                SendInitialRegister(provider, binding, binding.LocalSIPEndPoint, binding.RegistrarSIPEndPoint, binding.BindingExpiry);
                                            }
                                        }
                                    }
                                    catch (Exception regExcp)
                                    {
                                        logger.Error("Exception attempting to register provider " + binding.ProviderName + ". " + regExcp.Message + " for " + binding.Owner + ".");
                                        FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.Error, "Exception attempting to register provider " + binding.ProviderName + ". " + regExcp.Message, binding.Owner));

                                        try
                                        {
                                            binding.LastRegisterAttempt = DateTimeOffset.UtcNow;
                                            binding.NextRegistrationTime = DateTimeOffset.UtcNow.AddSeconds(REGISTER_FAILURERETRY_INTERVAL);
                                            binding.RegistrationFailureMessage = (regExcp.Message != null && regExcp.Message.Length > 1000) ? "Exception: " + regExcp.Message.Substring(0, 1000) : "Exception: " + regExcp.Message;
                                            m_bindingPersistor.Update(binding);
                                        }
                                        catch (Exception persistExcp)
                                        {
                                            logger.Error("Exception SIPRegistrationAgent persisting after exception. " + persistExcp.Message);
                                        }
                                    }
                                }

                                //logger.Debug("Binding entry processing took " + DateTime.Now.Subtract(startTime).TotalMilliseconds.ToString("0") + "ms.");

                                m_bindingsProcessedCount++;
                            }

                            bindingsList = GetNextBindings(NUMBER_BINDINGS_PER_DB_ROUNDTRIP);
                        }
                    }
                    catch (Exception persistExcp)
                    {
                        logger.Error("Exception MonitorRegistrations GettingBinding. " + persistExcp.Message);
                    }

                    Thread.Sleep(REGISTRATION_RENEWAL_PERIOD);
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception MonitorRegistrations. " + excp.Message);
            }
        }

        private List<SIPProviderBinding> GetNextBindings(int count)
        {
            try
            {
                // No point having two threads try and use database at the same time.
                lock (this)
                {
                    DateTime startTime = DateTime.Now;

                    List<SIPProviderBinding> bindings = null;

                    using (var trans = new TransactionScope())
                    {
                        bindings = m_bindingPersistor.Get(b => b.NextRegistrationTime <= DateTimeOffset.UtcNow, "nextregistrationtime", 0, count);
                        if (bindings != null && bindings.Count > 0)
                        {
                            foreach (SIPProviderBinding binding in bindings)
                            {
                                m_bindingPersistor.UpdateProperty(binding.Id, "NextRegistrationTime", DateTimeOffset.UtcNow.AddSeconds(REGISTER_FAILURERETRY_INTERVAL));
                            }
                        }

                        trans.Complete();
                    }

                    //logger.Debug("GetNextBindings took " + DateTime.Now.Subtract(startTime).TotalMilliseconds.ToString("0") + ".");

                    return bindings;
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception GetNextBindings (" + excp.GetType().ToString() + "). " + excp.Message);
                return null;
            }
        }

        private void SendInitialRegister(SIPProvider sipProvider, SIPProviderBinding binding, SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, int expirySeconds)
        {
            try
            {
                m_inTransitBindings.AddOrUpdate(binding.Id, binding, (s, i) => binding);

                binding.CSeq++;

                FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterInProgress, "Initiating registration for " + binding.Owner + " on " + binding.RegistrarServer.ToString() + ".", binding.Owner));
                SIPRequest regRequest = GetRegistrationRequest(sipProvider, binding, localSIPEndPoint, expirySeconds, remoteEndPoint);

                SIPNonInviteTransaction regTransaction = m_sipTransport.CreateNonInviteTransaction(regRequest, binding.RegistrarSIPEndPoint, localSIPEndPoint, m_outboundProxy);
                regTransaction.NonInviteTransactionFinalResponseReceived += (lep, rep, tn, rsp) => { ThreadPool.QueueUserWorkItem(delegate { ServerResponseReceived(lep, rep, tn, rsp); }); };
                regTransaction.NonInviteTransactionTimedOut += (tn) => { ThreadPool.QueueUserWorkItem(delegate { RegistrationTimedOut(tn); }); };

                m_sipTransport.SendSIPReliable(regTransaction);

                SIPSorceryPerformanceMonitor.IncrementCounter(SIPSorceryPerformanceMonitor.REGISTRATION_AGENT_REGISTRATIONS_PER_SECOND);
            }
            catch (Exception excp)
            {
                logger.Error("Exception SendInitialRegister for " + binding.Owner + " and " + binding.RegistrarServer.ToString() + ". " + excp.Message);
                RemoveCachedBinding(binding.Id);
            }
        }

        private void RegistrationTimedOut(SIPTransaction sipTransaction)
        {
            try
            {
                SIPRequest sipRequest = sipTransaction.TransactionRequest;
                Guid callIdGuid = new Guid(sipRequest.Header.CallId);
                SIPProviderBinding binding = GetBinding(callIdGuid);

                if (binding != null && binding.BindingExpiry != 0)
                {
                    RemoveCachedBinding(binding.Id);
                    int retryInterval = REGISTER_FAILURERETRY_INTERVAL + Crypto.GetRandomInt(0, REGISTER_FAILURERETRY_INTERVAL);
                    binding.RegistrationFailureMessage = "Registration to " + binding.RegistrarSIPEndPoint.ToString() + " timed out.";
                    binding.NextRegistrationTime = DateTimeOffset.UtcNow.AddSeconds(retryInterval);
                    binding.IsRegistered = false;
                    m_bindingPersistor.Update(binding);
                    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));
                    FireProxyLogEvent(new SIPMonitorMachineEvent(SIPMonitorMachineEventTypesEnum.SIPRegistrationAgentBindingUpdate, binding.Owner, binding.RegistrarSIPEndPoint, binding.ProviderId.ToString()));
                }
                else
                {
                    FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.Warn, "Registration request timed for unmatched call originally to " + sipTransaction.RemoteEndPoint + ".", null));
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception RegistrationTimedOut. " + excp.Message);
                RemoveCachedBinding(sipTransaction.TransactionRequest.Header.CallId);
            }
        }

        /// <summary>
        /// The event handler for responses to the initial register request.
        /// </summary>
        private void ServerResponseReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPTransaction sipTransaction, SIPResponse sipResponse)
        {
            try
            {
                string fromURI = (sipResponse.Header.From != null && sipResponse.Header.From.FromURI != null) ? sipResponse.Header.From.FromURI.ToString() : "NO FROM URI";
                string toURI = (sipResponse.Header.To != null && sipResponse.Header.To.ToURI != null) ? sipResponse.Header.To.ToURI.ToString() : "NO TO URI";
                FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterInProgress, "Server response " + sipResponse.Status + " received for " + fromURI + " and " + toURI + ".", null));

                if (sipResponse.Header.CallId.IsNullOrBlank())
                {
                    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));
                }
                else
                {
                    Guid callIdGuid = new Guid(sipResponse.Header.CallId);
                    SIPProviderBinding binding = GetBinding(callIdGuid);

                    if (binding != null)
                    {
                        if (sipResponse.Status == SIPResponseStatusCodesEnum.ProxyAuthenticationRequired || sipResponse.Status == SIPResponseStatusCodesEnum.Unauthorised)
                        {
                            if (sipResponse.Header.AuthenticationHeader != null)
                            {
                                SIPRequest authenticatedRequest = GetAuthenticatedRegistrationRequest(binding, sipTransaction.TransactionRequest, sipResponse);
                                SIPNonInviteTransaction regAuthTransaction = m_sipTransport.CreateNonInviteTransaction(authenticatedRequest, binding.RegistrarSIPEndPoint, localSIPEndPoint, m_outboundProxy);
                                regAuthTransaction.NonInviteTransactionFinalResponseReceived += (lep, rep, tn, rsp) => { ThreadPool.QueueUserWorkItem(delegate { AuthResponseReceived(lep, rep, tn, rsp); }); };
                                regAuthTransaction.NonInviteTransactionTimedOut += (tn) => { ThreadPool.QueueUserWorkItem(delegate { RegistrationTimedOut(tn); }); };
                                m_sipTransport.SendSIPReliable(regAuthTransaction);
                            }
                            else if (binding.BindingExpiry != 0)
                            {
                                RemoveCachedBinding(binding.Id);
                                binding.IsRegistered = false;
                                binding.RegistrationFailureMessage = "Server did not provide auth header, check realm.";
                                binding.NextRegistrationTime = DateTimeOffset.UtcNow.AddSeconds(REGISTER_FAILURERETRY_INTERVAL);
                                m_bindingPersistor.Update(binding);
                                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));
                            }
                        }
                        else if (binding.BindingExpiry != 0)
                        {
                            // Non 401 or 407 responses mean the registration attempt is finished and the call-id will be removed and state updated.
                            if (sipResponse.Status == SIPResponseStatusCodesEnum.Ok)
                            {
                                // Successful registration.
                                OkResponseReceived(sipTransaction, remoteEndPoint, sipResponse);
                            }
                            else if (sipResponse.Status == SIPResponseStatusCodesEnum.Forbidden || sipResponse.Status == SIPResponseStatusCodesEnum.NotFound)
                            {
                                // SIP account does not appear to exist. Disable registration attempts until user intervenes to correct.
                                RemoveCachedBinding(binding.Id);
                                m_bindingPersistor.Delete(binding);
                                DisableSIPProviderRegistration(binding.ProviderId, "Authentication failed (" + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + ").");
                                FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterFailed, "Registration failed with " + sipResponse.Status + " for " + binding.Owner + " on " + binding.RegistrarServer.ToString() + ", DISABLING.", binding.Owner));
                            }
                            else if (sipResponse.Status == SIPResponseStatusCodesEnum.IntervalTooBrief ||
                                     (sipResponse.Status == SIPResponseStatusCodesEnum.BusyEverywhere && sipResponse.ReasonPhrase == "Too Frequent Requests")) // FWD uses this to indicate it doesn't like the timing.
                            {
                                RemoveCachedBinding(binding.Id);
                                binding.IsRegistered = false;
                                binding.RegistrationFailureMessage = "Registration failed (" + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + ").";
                                binding.NextRegistrationTime = DateTimeOffset.UtcNow.AddSeconds(REGISTER_FAILURERETRY_INTERVAL + Crypto.GetRandomInt(0, REGISTER_FAILURERETRY_INTERVAL));
                                m_bindingPersistor.Update(binding);
                                FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterFailed, "Registration failed for " + binding.Owner + " on " + binding.RegistrarServer.ToString() + " due to " + sipResponse.ReasonPhrase + ".", binding.Owner));
                            }
                            else
                            {
                                RemoveCachedBinding(binding.Id);
                                binding.IsRegistered = false;
                                binding.RegistrationFailureMessage = "Registration failed (" + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + ").";
                                binding.NextRegistrationTime = DateTimeOffset.UtcNow.AddSeconds(REGISTER_FAILURERETRY_INTERVAL);
                                m_bindingPersistor.Update(binding);
                                FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterFailed, "Registration failed with " + sipResponse.Status + " for " + binding.Owner + " on " + binding.RegistrarServer.ToString() + ".", binding.Owner));
                            }
                        }
                    }
                    else
                    {
                        FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.Warn, "An " + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + " from " + remoteEndPoint + " was received for an unknown registration.", null));
                    }
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception SIPRegistrationAgent ServerResponseReceived (" + remoteEndPoint + "). " + excp.Message);
                RemoveCachedBinding(sipResponse.Header.CallId);
            }
        }

        /// <summary>
        /// The event handler for responses to the authenticated register request.
        /// </summary>
        private void AuthResponseReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPTransaction sipTransaction, SIPResponse sipResponse)
        {
            try
            {
                string fromURI = (sipResponse.Header.From != null && sipResponse.Header.From.FromURI != null) ? sipResponse.Header.From.FromURI.ToString() : "NO FROM URI";
                string toURI = (sipResponse.Header.To != null && sipResponse.Header.To.ToURI != null) ? sipResponse.Header.To.ToURI.ToString() : "NO TO URI";
                FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterInProgress, "Server auth response " + sipResponse.Status + " received for " + fromURI + " and " + toURI + ".", null));

                if (sipResponse.Header.CallId.IsNullOrBlank())
                {
                    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));
                }
                else
                {
                    Guid callIdGuid = new Guid(sipResponse.Header.CallId);
                    SIPProviderBinding binding = GetBinding(callIdGuid);

                    if (binding != null && binding.BindingExpiry != 0)
                    {
                        if (sipResponse.Status == SIPResponseStatusCodesEnum.Ok)
                        {
                            OkResponseReceived(sipTransaction, remoteEndPoint, sipResponse);
                        }
                        else if (sipResponse.Status == SIPResponseStatusCodesEnum.IntervalTooBrief)
                        {
                            RemoveCachedBinding(binding.Id);
                            binding.IsRegistered = false;
                            binding.RegistrationFailureMessage = "Registration failed (" + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + ").";
                            binding.NextRegistrationTime = DateTimeOffset.UtcNow.AddSeconds(REGISTER_FAILURERETRY_INTERVAL + Crypto.GetRandomInt(0, REGISTER_FAILURERETRY_INTERVAL));
                            m_bindingPersistor.Update(binding);
                            FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterFailed, "Registration failed for " + binding.Owner + " on " + binding.RegistrarServer.ToString() + " due to " + sipResponse.ReasonPhrase + ".", binding.Owner));
                        }
                        else if (sipResponse.Status == SIPResponseStatusCodesEnum.Forbidden || sipResponse.Status == SIPResponseStatusCodesEnum.NotFound)
                        {
                            // SIP account does not appear to exist. Disable registration attempts until user intervenes to correct.
                            RemoveCachedBinding(binding.Id);
                            m_bindingPersistor.Delete(binding);
                            DisableSIPProviderRegistration(binding.ProviderId, "Username was rejected by SIP Provider with " + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + ".");
                            FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterFailed, "Registration failed with " + sipResponse.Status + " for " + binding.Owner + " on " + binding.RegistrarServer.ToString() + ", DISABLING.", binding.Owner));
                        }
                        else
                        {
                            RemoveCachedBinding(binding.Id);
                            binding.IsRegistered = false;
                            binding.NextRegistrationTime = DateTimeOffset.UtcNow.AddSeconds(REGISTER_FAILURERETRY_INTERVAL);
                            binding.RegistrationFailureMessage = "Registration failed (" + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + ").";
                            m_bindingPersistor.Update(binding);
                            FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegisterFailed, "Registration failed for " + binding.Owner + " on " + binding.RegistrarServer.ToString() + " with " + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + ".", binding.Owner));
                        }

                        FireProxyLogEvent(new SIPMonitorMachineEvent(SIPMonitorMachineEventTypesEnum.SIPRegistrationAgentBindingUpdate, binding.Owner, binding.RegistrarSIPEndPoint, binding.ProviderId.ToString()));
                    }
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception SIPRegistrationAgent AuthResponseReceived. " + excp.Message);
                RemoveCachedBinding(sipResponse.Header.CallId);
            }
        }

        private void OkResponseReceived(SIPTransaction sipTransaction, SIPEndPoint remoteEndPoint, SIPResponse sipResponse)
        {
            try
            {
                Guid callIdGuid = new Guid(sipResponse.Header.CallId);
                SIPProviderBinding binding = GetBinding(callIdGuid);
                RemoveCachedBinding(callIdGuid);

                if (binding != null)
                {
                    // Updated contacts list.
                    // Find the contact in the list that matches the one being maintained by this agent in order to determine the expiry value.
                    int headerExpires = sipResponse.Header.Expires;
                    int contactExpires = -1;
                    if (sipResponse.Header.Contact != null && sipResponse.Header.Contact.Count > 0)
                    {
                        foreach (SIPContactHeader contactHeader in sipResponse.Header.Contact)
                        {
                            if (contactHeader.ContactURI.Parameters.Get(m_regAgentContactId) == binding.BindingSIPURI.Parameters.Get(m_regAgentContactId))
                            {
                                contactExpires = contactHeader.Expires;
                                break;
                            }
                        }
                    }

                    if (contactExpires != -1)
                    {
                        binding.BindingExpiry = contactExpires;
                    }
                    else if (headerExpires != -1)
                    {
                        binding.BindingExpiry = headerExpires;
                    }

                    if (binding.BindingExpiry < REGISTER_MINIMUM_EXPIRY)
                    {
                        // Make sure we don't do a 3CX and send registration floods.
                        binding.BindingExpiry = REGISTER_MINIMUM_EXPIRY;
                    }

                    binding.NextRegistrationTime = DateTimeOffset.UtcNow.AddSeconds(binding.BindingExpiry - REGISTRATION_HEAD_TIME);
                    binding.ContactsList = sipResponse.Header.Contact;
                    binding.IsRegistered = true;
                    binding.LastRegisterTime = DateTimeOffset.UtcNow;
                    binding.RegistrationFailureMessage = null;
                    m_bindingPersistor.Update(binding);
                    UpdateSIPProviderOutboundProxy(binding, sipResponse.Header.ProxyReceivedOn);
                    FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.RegisterAgent, SIPMonitorEventTypesEnum.ContactRegistered, "Contact successfully registered for " + binding.Owner + " on " + binding.RegistrarServer.ToString() + ", expiry " + binding.BindingExpiry + "s.", binding.Owner));
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception SIPRegistrationAgent OkResponseReceived. " + excp.Message);
                RemoveCachedBinding(sipResponse.Header.CallId);
            }
        }

        private SIPRequest GetRegistrationRequest(SIPProvider sipProvider, SIPProviderBinding binding, SIPEndPoint localSIPEndPoint, int expiry, SIPEndPoint registrarEndPoint)
        {
            try
            {
                if (!binding.BindingSIPURI.Parameters.Has(m_regAgentContactId))
                {
                    binding.BindingSIPURI.Parameters.Set(m_regAgentContactId, Crypto.GetRandomString(6));
                }

                string realm = binding.RegistrarRealm;

                SIPURI registerURI = SIPURI.ParseSIPURIRelaxed(realm);
                SIPURI regUserURI = SIPURI.ParseSIPURIRelaxed(sipProvider.ProviderUsername + "@" + realm);

                SIPFromHeader fromHeader = new SIPFromHeader(null, regUserURI, CallProperties.CreateNewTag());
                SIPToHeader toHeader = new SIPToHeader(null, regUserURI, null);
                SIPContactHeader contactHeader = new SIPContactHeader(null, binding.BindingSIPURI);
                //contactHeader.Expires = binding.BindingExpiry;
                string callId = binding.Id.ToString();
                int cseq = ++binding.CSeq;

                SIPRequest registerRequest = new SIPRequest(SIPMethodsEnum.REGISTER, registerURI);
                registerRequest.LocalSIPEndPoint = localSIPEndPoint;
                SIPHeader header = new SIPHeader(contactHeader, fromHeader, toHeader, cseq, callId);
                header.CSeqMethod = SIPMethodsEnum.REGISTER;
                header.UserAgent = m_userAgentString;
                header.Expires = binding.BindingExpiry;

                SIPViaHeader viaHeader = new SIPViaHeader(localSIPEndPoint, CallProperties.CreateBranchId());
                header.Vias.PushViaHeader(viaHeader);

                SIPRoute registrarRoute = new SIPRoute(new SIPURI(binding.RegistrarServer.Scheme, registrarEndPoint), true);
                header.Routes.PushRoute(registrarRoute);

                if (sipProvider != null && !sipProvider.CustomHeaders.IsNullOrBlank())
                {
                    string[] customerHeadersList = sipProvider.CustomHeaders.Split(SIPProvider.CUSTOM_HEADERS_SEPARATOR);

                    if (customerHeadersList != null && customerHeadersList.Length > 0)
                    {
                        foreach (string customHeader in customerHeadersList)
                        {
                            if (customHeader.IndexOf(':') == -1)
                            {
                                logger.Debug("Skipping custom header due to missing colon, " + customHeader + ".");
                                continue;
                            }
                            else
                            {
                                string headerName = customHeader.Substring(0, customHeader.IndexOf(':'));
                                if (headerName != null && Regex.Match(headerName.Trim(), "(Via|From|To|Contact|CSeq|Call-ID|Max-Forwards|Content)", RegexOptions.IgnoreCase).Success)
                                {
                                    logger.Debug("Skipping custom header due to an non-permitted string in header name, " + customHeader + ".");
                                    continue;
                                }
                                else
                                {
                                    if (headerName == SIPConstants.SIP_USERAGENT_STRING)
                                    {
                                        header.UserAgent = customHeader.Substring(customHeader.IndexOf(':') + 1);
                                    }
                                    else
                                    {
                                        header.UnknownHeaders.Add(customHeader.Trim());
                                    }
                                }
                            }
                        }
                    }
                }

                registerRequest.Header = header;
                return registerRequest;
            }
            catch (Exception excp)
            {
                logger.Error("Exception GetRegistrationRequest. " + excp.Message);
                throw excp;
            }
        }

        private SIPRequest GetAuthenticatedRegistrationRequest(SIPProviderBinding binding, SIPRequest registerRequest, SIPResponse sipResponse)
        {
            try
            {
                SIPAuthorisationDigest authRequest = sipResponse.Header.AuthenticationHeader.SIPDigest;
                authRequest.SetCredentials(binding.ProviderAuthUsername, binding.ProviderPassword, registerRequest.URI.ToString(), SIPMethodsEnum.REGISTER.ToString());

                SIPRequest regRequest = registerRequest.Copy();
                regRequest.LocalSIPEndPoint = registerRequest.LocalSIPEndPoint;
                regRequest.Header.Vias.TopViaHeader.Branch = CallProperties.CreateBranchId();
                regRequest.Header.From.FromTag = CallProperties.CreateNewTag();
                regRequest.Header.To.ToTag = null;
                regRequest.Header.CSeq = ++binding.CSeq;

                if (SIPProviderMagicJack.IsMagicJackRequest(sipResponse))
                {
                    regRequest.Header.AuthenticationHeader = SIPProviderMagicJack.GetAuthenticationHeader(sipResponse);
                }
                else
                {
                    regRequest.Header.AuthenticationHeader = new SIPAuthenticationHeader(authRequest);
                    regRequest.Header.AuthenticationHeader.SIPDigest.Response = authRequest.Digest;
                }

                return regRequest;
            }
            catch (Exception excp)
            {
                logger.Error("Exception GetAuthenticatedRegistrationRequest. " + excp.Message);
                throw excp;
            }
        }

        private void RemoveCachedBinding(string bindingId)
        {
            Guid bindingIDGUID = Guid.Empty;
            if (Guid.TryParse(bindingId, out bindingIDGUID))
            {
                RemoveCachedBinding(bindingIDGUID);
            }
        }

        private void RemoveCachedBinding(Guid bindingId)
        {
            if (m_inTransitBindings.ContainsKey(bindingId))
            {
                SIPProviderBinding removedBinding = null;
                m_inTransitBindings.TryRemove(bindingId, out removedBinding);
            }
        }

        private SIPProviderBinding GetBinding(Guid bindingId)
        {
            SIPProviderBinding binding = null;

            // If the binding is in the local cache use that.
            if (m_inTransitBindings.ContainsKey(bindingId))
            {
                binding = m_inTransitBindings[bindingId];
            }

            // If binding wasn't found in the cache try and load from persistence store.
            if (binding == null)
            {
                binding = m_bindingPersistor.Get(bindingId);
                //binding = m_bindingPersistor.GetFromDirectQuery(m_selectBinding, new SqlParameter("1", bindingId));
            }

            return binding;
        }

        private void DisableSIPProviderRegistration(Guid providerId, string disabledReason)
        {
            try
            {
                SIPProvider sipProvider = GetSIPProviderById_External(providerId);
                sipProvider.RegisterEnabled = false;
                sipProvider.RegisterDisabledReason = (!disabledReason.IsNullOrBlank()) ? disabledReason : "disabled with no reason given";
                UpdateSIPProvider_External(sipProvider);
            }
            catch (Exception excp)
            {
                logger.Error("Exception DisableSIPProviderRegistration. " + excp.Message);
            }
        }

        private void UpdateSIPProviderOutboundProxy(SIPProviderBinding binding, string outboundProxy)
        {
            try
            {
                if (binding.ProviderOutboundProxy != outboundProxy)
                {
                    UpdateSIPProviderProperty_External(binding.ProviderId, "ProviderOutboundProxy", outboundProxy);
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception UpdateSIPProviderOutboundProxy. " + excp.Message);
            }
        }

        private void FireProxyLogEvent(SIPMonitorEvent monitorEvent)
        {
            if (StatefulProxyLogEvent_External != null)
            {
                try
                {
                    StatefulProxyLogEvent_External(monitorEvent);
                }
                catch (Exception excp)
                {
                    logger.Error("Exception FireProxyLogEvent SIPRegistrationAgent. " + excp.Message);
                }
            }
        }
    }
}
Back to Top