/// <summary> /// Stop the network capture /// </summary> public static void Stop() { TraceLog.TraceInfo("Stopping collector"); if (devices != null) { try { foreach (var device in devices) { // Stop the capturing process TraceLog.TraceInfo(string.Format("Stopping {0} {1}", device.Name, device.Description)); device.OnPacketArrival -= new PacketArrivalEventHandler(device_OnPacketArrival); device.StopCapture(); device.Close(); // Print out the device statistics TraceLog.TraceInfo(device.Statistics.ToString()); } } catch (Exception ex) { TraceLog.TraceException("Stop caught exception", ex); } } TraceLog.TraceInfo("Stopped collector"); }
/// <summary> /// stop the upload client /// </summary> public static void Stop() { lock (startLock) { TraceLog.TraceInfo("Stopping updater"); // terminate the loop loopFlag = false; TraceLog.TraceInfo("Stopped updater"); } }
/// <summary> /// Start the upload client /// </summary> public static void Start() { lock (startLock) { if (!started) { TraceLog.TraceInfo("Starting updater"); ThreadStart ts = new ThreadStart(GetVersionLoop); Thread thread = new Thread(ts); loopFlag = true; thread.Start(); started = true; } } }
/// <summary> /// stop the upload client /// </summary> public static void Stop() { lock (startLock) { TraceLog.TraceInfo("Stopping uploader"); // terminate the loop uploadFlag = false; // one last send TraceLog.TraceInfo("Stop processing: sending last buffer"); Send(); TraceLog.TraceInfo("Stopped uploader"); } }
/// <summary> /// Start the upload client /// </summary> public static void Start() { lock (startLock) { if (!started) { TraceLog.TraceInfo("Starting uploader"); // retrieve creds and device ID credentials = ConfigClient.Read(ConfigClient.Credentials); deviceId = ConfigClient.Read(ConfigClient.DeviceId); deviceName = ConfigClient.Read(ConfigClient.DeviceName); // start the uploader loop on a new thread ThreadStart ts = new ThreadStart(SendLoop); Thread thread = new Thread(ts); uploadFlag = true; thread.Start(); started = true; } } }
private static void Install(string filename) { // spawn a new process process - we must do this outside of the service because the // installer will shut down the current running service as part of the uninstall using (var process = new Process()) { process.StartInfo.FileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "msiexec.exe"); process.StartInfo.Arguments = string.Format( "/i {0} /qn /quiet /L*vx \"{1}\" REBOOT=ReallySuppress", filename, LogFilename); process.StartInfo.CreateNoWindow = true; process.StartInfo.UseShellExecute = false; TraceLog.TraceInfo(string.Format("Starting msiexec {0}", process.StartInfo.Arguments)); process.Start(); } // note - if the update does not succeed, the updater service will continue running and updateFlag will still be true. // this is a FEATURE since the update failed. The next time the update will be downloaded will be when the service restarts, // not in the next update interval. this in turn will reduce the likelihood that a bad update will create lots of junk 1MB images // on the user's HDD. }
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; } } }
/// <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; } } }
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); } }
// 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); } }
// 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); } } }
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); } }