Beispiel #1
0
        /// <summary>
        /// Start a network capture
        /// </summary>
        public static void Start()
        {
            lock (startingLock)
            {
                try
                {
                    // Retrieve the device list
                    devices = CaptureDeviceList.Instance;

                    // If no devices were found print an error
                    if (devices.Count < 1)
                    {
                        var error = "No devices were found on this machine";
                        TraceLog.TraceFatal(error);
                        throw new Exception(error);
                    }

                    // if capture is started on all devices, nothing to do
                    var started = true;
                    foreach (var dev in devices)
                    {
                        if (!dev.Started || !string.IsNullOrEmpty(dev.LastError))
                        {
                            started = false;
                            break;
                        }
                    }
                    if (started)
                    {
                        return;
                    }

                    // Print SharpPcap version
                    string ver = SharpPcap.Version.VersionString;
                    TraceLog.TraceInfo(string.Format("Starting collector with version {0}", ver));

                    foreach (var device in devices)
                    {
                        // reset device if already started
                        if (device.Started)
                        {
                            TraceLog.TraceInfo(string.Format("Stopping {0} {1}", device.Name, device.Description));
                            device.OnPacketArrival -= new PacketArrivalEventHandler(device_OnPacketArrival);
                            device.StopCapture();
                            device.Close();
                        }

                        // Register our handler function to the 'packet arrival' event
                        device.OnPacketArrival +=
                            new PacketArrivalEventHandler(device_OnPacketArrival);

                        // Open the device for capturing
                        int readTimeoutMilliseconds = 1000;
                        device.Open(DeviceMode.Promiscuous, readTimeoutMilliseconds);

                        TraceLog.TraceInfo(string.Format("Listening on {0} {1}",
                                                         device.Name, device.Description));

                        // DNS only
                        string filter = "udp dst port 53";
                        device.Filter = filter;

                        // Start the capturing process
                        device.StartCapture();
                    }

                    // get the device ID, and store it if it's not in the config file already
                    deviceId = ConfigClient.Read(ConfigClient.DeviceId);
                    if (deviceId == null)
                    {
                        deviceId = devices[0].MacAddress.ToString();
                        ConfigClient.Write(ConfigClient.DeviceId, deviceId);
                    }
                }
                catch (Exception ex)
                {
                    TraceLog.TraceException("FATAL: Start caught exception", ex);
                    throw ex;
                }
            }
        }
Beispiel #2
0
        private static void GetVersionLoop()
        {
            while (loopFlag)
            {
                // sleep for timeout
                Thread.Sleep(timeout);

                // don't want two concurrent updates
                if (checkingFlag || updatingFlag)
                {
                    continue;
                }

                try
                {
                    // locking is overkill here because only one thread that wakes up every <timeout>
                    checkingFlag = true;

                    WebServiceHelper.GetCurrentSoftwareVersion(
                        VersionUrl,
                        new WebServiceHelper.AccountDelegate((version) =>
                    {
                        if (string.IsNullOrEmpty(version))
                        {
                            TraceLog.TraceError("Could not download version file");
                            checkingFlag = false;
                            return;
                        }
                        try
                        {
                            // compare versions
                            var newVersion = new Version(version);
                            if (newVersion > CurrentVersion)
                            {
                                TraceLog.TraceInfo(string.Format(
                                                       "New version {0} more recent than current version {1}; downloading new version",
                                                       newVersion, CurrentVersion));
                                updatingFlag = true;

                                // download and install new software
                                WebServiceHelper.DownloadCurrentSoftware(
                                    DownloadUrl,
                                    new WebServiceHelper.AccountDelegate((filename) =>
                                {
                                    // install the new software
                                    TraceLog.TraceInfo(string.Format("Download successful; installing {0}", filename));
                                    Install(filename);
                                }),
                                    new WebServiceHelper.NetOpDelegate((inProgress, status) =>
                                {
                                    // cleanup the checking flag based on network operation status (typically failures)
                                    if (inProgress == false)
                                    {
                                        updatingFlag = false;
                                    }
                                }));
                            }
                            checkingFlag = false;
                        }
                        catch (Exception ex)
                        {
                            TraceLog.TraceException("Version check failed", ex);
                            checkingFlag = false;
                            updatingFlag = false;
                        }
                    }),
                        new WebServiceHelper.NetOpDelegate((inProgress, status) =>
                    {
                        // cleanup the checking flag based on network operation status (typically failures)
                        if (inProgress == false)
                        {
                            checkingFlag = false;
                        }
                    }));
                }
                catch (Exception ex)
                {
                    TraceLog.TraceException("GetVersionLoop failed", ex);
                    checkingFlag = false;
                    updatingFlag = false;
                }
            }
        }
Beispiel #3
0
        public static void WriteLine(string message, string level)
        {
            // create a json record
            var record = new TraceRecord()
            {
                //Client = TraceLog.Client,
                LogLevel  = level,
                Message   = message,
                Session   = TraceLog.Session,
                Timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff")
            };
            var json = JsonConvert.SerializeObject(record);

            lock (writeLock)
            {
                // enter a retry loop writing the record to the trace file
                int retryCount = 2;
                while (retryCount > 0)
                {
                    try
                    {
                        if (traceFilename == null)
                        {
                            // create the file
                            using (var stream = File.Create(TraceFilename))
                                using (var writer = new StreamWriter(stream))
                                {
                                    // log the file creation
                                    var createRecord = new TraceRecord()
                                    {
                                        //Client = TraceLog.Client,
                                        LogLevel  = TraceLog.LevelText(TraceLog.LogLevel.Info),
                                        Message   = "Created new trace file " + TraceFilename,
                                        Session   = TraceLog.Session,
                                        Timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff")
                                    };
                                    var createJson = JsonConvert.SerializeObject(createRecord);
                                    writer.WriteLine(createJson);
                                    writer.Flush();
                                }
                        }

                        // open the file
                        using (var stream = File.Open(TraceFilename, FileMode.Append, FileAccess.Write, FileShare.Read))
                            using (var writer = new StreamWriter(stream))
                            {
                                writer.WriteLine(json);
                                writer.Flush();

                                // reset the trace filename if it exceeds the maximum file size
                                if (writer.BaseStream.Position > MaxFileSize)
                                {
                                    traceFilename = null;
                                }
                            }

                        // success - terminate the enclosing retry loop
                        break;
                    }
                    catch (Exception)
                    {
                        // the file wasn't opened or written to correctly - try to start with a new file in the next iteration of the retry loop
                        traceFilename = null;
                        retryCount--;
                    }
                }
            }
        }
Beispiel #4
0
        private static void device_OnPacketArrival(object sender, CaptureEventArgs e)
        {
            try
            {
                var packet = PacketDotNet.Packet.ParsePacket(e.Packet.LinkLayerType, e.Packet.Data);
                if (packet is PacketDotNet.EthernetPacket)
                {
                    var eth = packet as EthernetPacket;

                    // this syntax is from PacketDotNet 0.13.0 which isn't supported in the nuget package (0.12.0)
                    //var udp = packet.Extract(typeof(UdpPacket)) as UdpPacket;
                    var udp = Extract(packet, typeof(UdpPacket)) as UdpPacket;
                    if (udp != null)
                    {
                        string websiteName = null;

                        if (udp.DestinationPort == 53) // DNS
                        {
                            // extract hostname.  it is encoded in segments: one-byte length followed by the segment in ASCII
                            // the first segment starts at 0x0c

                            const int START = 0x0c;

                            int  start = START;
                            byte len   = udp.PayloadData[start];
                            var  sb    = new StringBuilder();
                            while (len > 0)
                            {
                                sb.Append(Encoding.ASCII.GetString(udp.PayloadData, start + 1, len));
                                start += len + 1;
                                len    = udp.PayloadData[start];
                                if (len > 0)
                                {
                                    sb.Append('.');
                                }
                            }
                            websiteName = sb.ToString();
                        }

                        var hostMacAddress = eth.SourceHwAddress.ToString();

#if DEBUG
                        // only log this level of detail if we're logging to the console
                        if (TraceLog.TraceDestination == TraceLog.Destination.Console)
                        {
                            // get the IP packet info
                            var ip = packet.PayloadPacket as IPv4Packet;
                            if (ip != null)
                            {
                                var hostIpAddress = ip.SourceAddress.ToString();
                                var destIpAddress = ip.DestinationAddress.ToString();
                                var sourcePort    = udp.SourcePort;
                                var destPort      = udp.DestinationPort;
                                var hostName      = hostIpAddress;

                                try
                                {
                                    IPHostEntry entry = null;
                                    if (Environment.Version.CompareTo(new System.Version(4, 0)) < 0)
                                    {
                                        entry = Dns.GetHostByAddress(ip.SourceAddress);
                                    }
                                    else
                                    {
                                        entry = Dns.GetHostEntry(ip.SourceAddress);
                                    }
                                    if (entry != null)
                                    {
                                        hostName = entry.HostName;
                                    }
                                }
                                catch (Exception ex)
                                {
                                    TraceLog.TraceException(String.Format("GetHostByAddress failed for {0}", hostIpAddress), ex);
                                }

                                var destMacAddress = eth.DestinationHwAddress.ToString();

                                TraceLog.TraceInfo(String.Format("Source: [{0}; {1}:{2}; {3}]; Dest: [{4}; {5}:{6}; Website: {7}",
                                                                 hostMacAddress, hostIpAddress, sourcePort, hostName,
                                                                 destMacAddress, destIpAddress, destPort,
                                                                 websiteName));
                            }
                        }
#endif

                        lock (records)
                        {
                            records.Add(new Record()
                            {
                                HostMacAddress = hostMacAddress,
                                WebsiteName    = websiteName,
                                Timestamp      = DateTime.Now.ToString("s")
                            });
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                TraceLog.TraceException("OnPacketArrival caught exception", ex);
            }
        }
Beispiel #5
0
        // Common code to process the response from any web service call.  This is invoked from the callback
        // method for the web service, and passed a Type for deserializing the response body.
        // This method will also invoke the delegate with the result of the Web Service call
        private static void ProcessResponse <T>(IAsyncResult result)
        {
            WebServiceState state = result.AsyncState as WebServiceState;

            if (state == null)
            {
                TraceLog.TraceError("Web Service State not found");
                return;
            }

            // get the network operation status delegate
            Delegate netOpInProgressDel = state.NetworkOperationInProgressDelegate as Delegate;

            // get the web response and make sure it's not null (failed)
            //HttpWebResponseWrapper<T> resp = GetWebResponse<T>(result);
            HttpWebResponse resp = GetWebResponse(result);

            if (resp == null)
            {
                TraceLog.TraceError("GetWebResponse failed");

                // signal that the network operation completed unsuccessfully
                if (netOpInProgressDel != null)
                {
                    netOpInProgressDel.DynamicInvoke(false, OperationStatus.Failed);
                }
                return;
            }
            else
            {
                OperationStatus status = AsOperationStatus(resp.StatusCode);
                if (resp.StatusCode == HttpStatusCode.Unauthorized)
                {     // using this status code to indicate cookie has expired or is invalid
                    if (authCookie != null)
                    { // remove cookie and retry with credentials
                        status     = OperationStatus.Retry;
                        authCookie = null;
                    }
                }
                if (resp.StatusCode == HttpStatusCode.Forbidden)
                {   // remove cookie and force authentication on next request
                    authCookie = null;
                }

                if (netOpInProgressDel != null)
                {   // signal the network operation completed and whether it completed successfully
                    netOpInProgressDel.DynamicInvoke(false, status);
                    if (status == OperationStatus.Retry)
                    {   // delegate will retry, exit now
                        TraceLog.TraceInfo("Received a Retry response from Service");
                        return;
                    }
                }
            }

            // get the method-specific delegate
            Delegate del = state.Delegate as Delegate;

            //if (del == null)
            //    return;  // if no delegate was passed, the results can't be processed

            // invoke the delegate with the response body
            try
            {
                T resultObject = (T) new DataContractJsonSerializer(typeof(T)).ReadObject(resp.GetResponseStream());
                //T resultObject = resp.GetBody();
                if (del == null)
                {
                    return;  // if no delegate was passed, the results can't be processed
                }
                del.DynamicInvoke(resultObject);
            }
            catch (Exception ex)
            {
                TraceLog.TraceException("ProcessResponse: exception from GetBody or DynamicInvoke", ex);
                // signal the operation is done and unsuccessful
                if (netOpInProgressDel != null)
                {
                    netOpInProgressDel.DynamicInvoke(false, OperationStatus.Failed);
                }
                if (del == null)
                {
                    return;  // if no delegate was passed, the results can't be processed
                }
                del.DynamicInvoke(null);
            }
        }
Beispiel #6
0
        // This method is the callback for an HTTP request that returns a file.  It will save the
        // file to a temporary location and invoke the delegate with the location.
        private static void ProcessFile(IAsyncResult result)
        {
            const string updateDir = @"updates";
            const string msiname   = @"WebTimer.msi";

            WebServiceState state = result.AsyncState as WebServiceState;

            if (state == null)
            {
                TraceLog.TraceError("Web Service State not found");
                return;
            }

            // get the network operation status delegate
            Delegate netOpInProgressDel = state.NetworkOperationInProgressDelegate as Delegate;

            // get the web response and make sure it's not null (failed)
            HttpWebResponse resp = GetWebResponse(result);

            if (resp == null)
            {
                TraceLog.TraceError("GetWebResponse failed");

                // signal that the network operation completed unsuccessfully
                if (netOpInProgressDel != null)
                {
                    netOpInProgressDel.DynamicInvoke(false, OperationStatus.Failed);
                }
                return;
            }

            // get the method-specific delegate
            Delegate del = state.Delegate as Delegate;

            if (del == null)
            {
                return;  // if no delegate was passed, the results can't be processed
            }
            try
            {
                // create directory if it doesn't exist
                if (!Directory.Exists(updateDir))
                {
                    Directory.CreateDirectory(updateDir);
                }
            }
            catch (Exception ex)
            {
                TraceLog.TraceException("Could not create updates directory", ex);
                // signal the operation is done and unsuccessful
                if (netOpInProgressDel != null)
                {
                    netOpInProgressDel.DynamicInvoke(false, OperationStatus.Failed);
                }
                // can't process further
                return;
            }

            // write the result of the GET to a temp file
            //string updatepath = Path.GetRandomFileName();
            var    now        = DateTime.Now;
            string updatepath = Path.Combine(updateDir, string.Format("update-{0}", now.ToString("yyyy-MM-dd-HH-mm-ss")));
            string updatefile = Path.Combine(updatepath, msiname);

            try
            {
                TraceLog.TraceInfo(string.Format("Creating update directory {0}", updatepath));
                Directory.CreateDirectory(updatepath);

                TraceLog.TraceInfo(string.Format("Saving MSI in {0}", updatefile));
                using (var inputStream = resp.GetResponseStream())
                    using (var fileStream = File.OpenWrite(updatefile))
                    {
                        byte[] buffer = new byte[8 * 1024];
                        int    len;
                        while ((len = inputStream.Read(buffer, 0, buffer.Length)) > 0)
                        {
                            fileStream.Write(buffer, 0, len);
                        }
                    }
            }
            catch (Exception ex)
            {
                TraceLog.TraceException(string.Format("Could not create temporary file {0}", updatefile), ex);
                // signal the operation is done and unsuccessful
                if (netOpInProgressDel != null)
                {
                    netOpInProgressDel.DynamicInvoke(false, OperationStatus.Failed);
                }
                return;
            }

            // invoke the delegate with the temp file name
            try
            {
                del.DynamicInvoke(updatefile);
            }
            catch (Exception ex)
            {
                TraceLog.TraceException("ProcessFile: exception from DynamicInvoke", ex);
                // signal the operation is done and unsuccessful
                if (netOpInProgressDel != null)
                {
                    netOpInProgressDel.DynamicInvoke(false, OperationStatus.Failed);
                }
            }
        }
Beispiel #7
0
        private static void InvokeWebServiceRequest_Inner(IAsyncResult res)
        {
            WebInvokeServiceState state = res.AsyncState as WebInvokeServiceState;

            if (state == null)
            {
                TraceLog.TraceError("Web Service State not found");
                return;
            }

            var request            = state.Request;
            var netOpInProgressDel = state.NetworkOperationInProgressDelegate as Delegate;

            if (request == null)
            {
                TraceLog.TraceError("Web Service Request not found");
                // signal the operation is done and unsuccessful
                if (netOpInProgressDel != null)
                {
                    netOpInProgressDel.DynamicInvoke(false, OperationStatus.Failed);
                }
                return;
            }

            Stream stream = null;

            try
            {
                // this will throw if the connection can't be established
                stream = request.EndGetRequestStream(res);
            }
            catch (Exception ex)
            {
                TraceLog.TraceException("Can't obtain stream", ex);
                // signal the operation is done and unsuccessful
                if (netOpInProgressDel != null)
                {
                    netOpInProgressDel.DynamicInvoke(false, OperationStatus.Failed);
                }
                return;
            }

            // serialize a request body if one was passed in (and the verb will take it)
            if (state.RequestBody != null && request.Method != "GET")
            {
                // a null request body means that the caller wants to get the stream back and write to it directly
                if (state.RequestBody as Delegate != null)
                {
                    Delegate streamDel = (Delegate)state.RequestBody;
                    // invoke the delegate passed in with the request stream, so that the external caller
                    // can push data into the stream as it becomes available
                    streamDel.DynamicInvoke(stream);
                }
                else
                {
                    // the caller passed the complete object - so just serialize it onto the stream
                    if (state.RequestBody.GetType() == typeof(byte[]))
                    {
                        /*
                         * byte[] bytes = (byte[])state.RequestBody;
                         #if !IOS
                         * stream = new GZipStream(stream, CompressionMode.Compress);
                         * request.ContentType = "application/x-gzip";
                         #else
                         *                      stream = new MemoryStream();
                         * request.ContentType = "application/octet-stream";
                         #endif
                         * stream.Write(bytes, 0, bytes.Length);
                         */
                    }
                    else
                    {
                        request.ContentType = "application/json";
                        DataContractJsonSerializer ser = new DataContractJsonSerializer(state.RequestBody.GetType());
                        ser.WriteObject(stream, state.RequestBody);
                    }
                    // we got all the data into the stream, so flush/close it
                    stream.Flush();
                    stream.Close();
                }
            }

            // complete the invocation (this is not done inline because the InvokeWebServiceRequest_Inner_Complete() method
            // is reused by external callers that want to write to a stream directly and then invoke the operation)
            InvokeWebServiceRequest_Invoke(request, state.Delegate, state.NetworkOperationInProgressDelegate, state.Callback);
        }
Beispiel #8
0
        // Common code for invoking all the web service calls.
        // GET requests will be served directly from this method,
        // POST/PUT/DELETE requests are served from the InvokeWebServiceRequest_Inner method (which is an async callback)
        private static void InvokeWebServiceRequest(string userCreds, string url, string verb, object obj, Delegate del, Delegate netOpInProgressDel, AsyncCallback callback)
        {
            // signal that a network operation is starting
            if (netOpInProgressDel != null)
            {
                netOpInProgressDel.DynamicInvoke(true, OperationStatus.Started);
            }

            Uri uri = null;

            if (Uri.TryCreate(url, UriKind.RelativeOrAbsolute, out uri) == false ||
                uri.Scheme != "http" && uri.Scheme != "https")
            {
                TraceLog.TraceError("InvokeWebServiceRequest: bad URL: " + url);
                return;
            }

            var request = (HttpWebRequest)WebRequest.Create(uri);

            request.UserAgent = UserAgentString;
            request.Accept    = "application/json";
            request.Method    = verb == null ? "GET" : verb;

            if (authCookie != null)
            {   // send auth cookie
                request.Headers[authRequestHeader] = authCookie;
            }
            else if (userCreds != null)
            {   // send credentials in authorization header
                // url form encoded
                //string credentials = string.Format("UserName={0}&Password={1}", user.UserName, user.Password);

                // basic auth encoded
                request.Headers[authorizationHeader] = string.Format("Basic {0}", userCreds);
                request.Headers[HttpApplicationHeaders.RequestedWith] = UserAgents.WebTimerWindows;
            }

            // set the session ID header
            var sessionToken = TraceLog.Session;

            if (sessionToken != null)
            {
                request.Headers[HttpApplicationHeaders.Session] = sessionToken;
            }

            // if this is a GET request, we can execute from here
            if (request.Method == "GET")
            {
                // execute the web request and get the response
                try
                {
                    WebServiceState reqState = new WebServiceState()
                    {
                        Request  = request,
                        Delegate = del,
                        NetworkOperationInProgressDelegate = netOpInProgressDel
                    };
                    request.BeginGetResponse(callback, reqState);
                }
                catch (Exception ex)
                {
                    TraceLog.TraceException("Exception in BeginGetResponse", ex);

                    // signal that a network operation is done and unsuccessful
                    if (netOpInProgressDel != null)
                    {
                        netOpInProgressDel.DynamicInvoke(false, OperationStatus.Failed);
                    }
                }
            }
            else
            {
                // this is a request that contains a body (PUT, POST, DELETE)
                // need to nest another async call - this time to get the request stream
                try
                {
                    request.BeginGetRequestStream(
                        new AsyncCallback(InvokeWebServiceRequest_Inner),
                        new WebInvokeServiceState()
                    {
                        Request  = request,
                        Callback = callback,
                        Delegate = del,
                        NetworkOperationInProgressDelegate = netOpInProgressDel,
                        RequestBody = obj
                    });
                }
                catch (Exception ex)
                {
                    // trace the exception
                    TraceLog.TraceException("Exception in BeginGetResponse", ex);

                    // signal that a network operation is done and unsuccessful
                    if (netOpInProgressDel != null)
                    {
                        netOpInProgressDel.DynamicInvoke(false, OperationStatus.Failed);
                    }
                }
            }
        }
Beispiel #9
0
        private static void Send()
        {
            try
            {
                // snap the record list
                var recordList = CollectorClient.GetRecords();

                // if no records, may need to recover capture service
                if (recordList.Count == 0)
                {
                    if (recovering)
                    {
                        // this is the second timeout period that we didn't see records.
                        // attempt to restart the collector
                        CollectorClient.Start();
                        recovering = false;
                    }
                    else
                    {
                        // this will trigger recovery if the next run through also has zero records
                        recovering = true;
                    }

                    // nothing to do this time (no records to process)
                    return;
                }
                else
                {
                    // reset recovery mode
                    recovering = false;
                }

                // compact the record list
                var uniqueHosts = recordList.Select(r => r.HostMacAddress).Distinct();
                foreach (var host in uniqueHosts)
                {
                    var uniqueDests = recordList.Where(r => r.HostMacAddress == host).Select(r => r.WebsiteName).Distinct();
                    foreach (var dest in uniqueDests)
                    {
                        var records = recordList.Where(r => r.HostMacAddress == host && r.WebsiteName == dest).OrderBy(r => r.Timestamp);

                        // only keep the first and last record so that the duration calculated on the server will be > 0
                        var firstRecord = records.FirstOrDefault();
                        var lastRecord  = records.LastOrDefault();

                        // if the last record is a second or more later, add the duration to the first record
                        TimeSpan duration;
                        if (lastRecord != null && lastRecord != firstRecord &&
                            (duration = Convert.ToDateTime(lastRecord.Timestamp) - Convert.ToDateTime(firstRecord.Timestamp))
                            >= TimeSpan.FromSeconds(1.0))
                        {
                            firstRecord.Duration = (int)duration.TotalSeconds;
                        }

                        lock (sendQueue)
                        {
                            var existingRecord = sendQueue.FirstOrDefault(r => r.HostMacAddress == host && r.WebsiteName == dest);
                            if (existingRecord == null ||
                                Convert.ToDateTime(firstRecord.Timestamp) - (Convert.ToDateTime(existingRecord.Timestamp) + TimeSpan.FromSeconds(existingRecord.Duration)) >= TimeSpan.FromSeconds(300.0)) // more than 5 minutes between records
                            {
                                // add the record to the send queue
                                sendQueue.Add(firstRecord);
                            }
                            else
                            {
                                // augment existing record with new duration
                                duration = Convert.ToDateTime(firstRecord.Timestamp) - Convert.ToDateTime(existingRecord.Timestamp) + TimeSpan.FromSeconds(firstRecord.Duration);
                                existingRecord.Duration = (int)duration.TotalSeconds;
                            }
                        }
                    }
                }

                if (sendQueue.Count > 0)
                {
                    // snap the send queue
                    var recordBuffer = new List <Record>();
                    lock (sendQueue)
                    {
                        // clear this buffer if collection is suspended
                        if (collectionStatus == ControlMessage.SuspendCollection)
                        {
                            sendQueue.Clear();
                            TraceLog.TraceInfo("Probing for new collection status");
                        }
                        else
                        {
                            recordBuffer.AddRange(sendQueue);
                            TraceLog.TraceInfo(String.Format("Sending records: {0}", JsonConvert.SerializeObject(sendQueue)));
                        }
                    }

                    var sendList = new RecordList()
                    {
                        DeviceId        = deviceId,
                        DeviceName      = deviceName,
                        SoftwareVersion = UpdateClient.CurrentVersion.ToString(),
                        Records         = recordBuffer
                    };

                    // send the queue to the web service
                    WebServiceHelper.PostRecords(
                        credentials,
                        sendList,
                        new WebServiceHelper.PostRecordsDelegate((response) =>
                    {
                        // a callback means the service processed the records successfully; we can free them now.
                        // note a slight race condition - if the callback takes longer than the sleep interval, we
                        // could lose a buffer.  since the sleep interval is 60sec, this isn't an important issue
                        // to address.
                        lock (sendQueue)
                        {
                            sendQueue.Clear();
                        }
                        var msg = (ControlMessage)response.controlMessage;
                        if (collectionStatus == msg)
                        {
                            return;
                        }

                        // process the control message
                        lock (configLock)
                        {
                            switch (msg)
                            {
                            case ControlMessage.Normal:
                                TraceLog.TraceInfo("Changing collection status to Normal");
                                collectionStatus = msg;
                                ConfigClient.Write(ConfigClient.Disabled, Convert.ToString(false));
                                break;

                            case ControlMessage.SuspendCollection:
                                TraceLog.TraceInfo("Changing collection status to Suspended");
                                collectionStatus = msg;
                                ConfigClient.Write(ConfigClient.Disabled, Convert.ToString(true));
                                break;

                            case ControlMessage.DisableDevice:
                                TraceLog.TraceInfo("Disabling the device by wiping the config");
                                collectionStatus = msg;
                                deviceId         = null;
                                deviceName       = null;
                                // wipe the config
                                ConfigClient.Clear();
                                break;

                            default:
                                // new message we don't understand yet
                                TraceLog.TraceInfo(string.Format("Received unknown control message {0}", msg));
                                break;
                            }
                        }
                    }),
                        new WebServiceHelper.NetOpDelegate((inProgress, status) =>
                    {
                        if (status == OperationStatus.Retry)
                        {
                            // no need to do anything: next iteration will send original credentials
                        }
                        // no failure state to clean up
                    }));
                }
            }
            catch (Exception ex)
            {
                TraceLog.TraceException("Upload processing failed", ex);
            }
        }