internal static void ProcessData(DevicesSeen observationReport) { // which AP is sending the report string queryString = "SELECT * FROM ReportingAPs WHERE apMac = '" + observationReport.Data.ApMac + "'"; if (!DBConnection.DoesRecordExist(queryString)) { // new reporting AP queryString = "INSERT INTO ReportingAPs (apMac) Values ('" + observationReport.Data.ApMac + "')"; DBConnection.UpdateDB(queryString); } // hacking temporal proximity... long reportReceived = DateTimeOffset.Now.ToUnixTimeSeconds(); // Carve up the observationReport and push it into db int x; for (x = 0; x < observationReport.Data.Observations.Count; x++) { if (observationReport.Data.Observations[x].Location != null) { queryString = "INSERT INTO ObservationReports " + "(ipv4, locationlat, locationlng, locationunc, x, y, seenTime, ssid, os, " + "clientMac, name, seenEpoch, rssi, ipv6, manufacturer, seenByApMac, " + "reportReceived) " + "Values ("; // a crude hack. the reports dont contain the hostname preceding the / so just del the / // v3 of the api drops the hostname entirely anyway if (observationReport.Data.Observations[x].Ipv4 != null) { queryString += "'" + (observationReport.Data.Observations[x].Ipv4).Substring(1) + "', "; } else { queryString += "'" + observationReport.Data.Observations[x].Ipv4 + "', "; } queryString += "'" + observationReport.Data.Observations[x].Location.Lat + "', " + "'" + observationReport.Data.Observations[x].Location.Lng + "', " + "'" + observationReport.Data.Observations[x].Location.Unc + "', " + // x and y are actually lists as the values can be returned as offsets // on multiple floorplans. just grabbing the first value for simplicity "'" + observationReport.Data.Observations[x].Location.X[0] + "', " + "'" + observationReport.Data.Observations[x].Location.Y[0] + "', " + "'" + observationReport.Data.Observations[x].SeenTime + "', " + "'" + observationReport.Data.Observations[x].Ssid + "', " + "'" + observationReport.Data.Observations[x].Os + "', " + "'" + observationReport.Data.Observations[x].ClientMac + "', " + "'" + observationReport.Data.Observations[x].Name + "', " + "'" + observationReport.Data.Observations[x].SeenEpoch + "', " + "'" + observationReport.Data.Observations[x].Rssi + "', " + "'" + observationReport.Data.Observations[x].Ipv6 + "', " + "'" + observationReport.Data.Observations[x].Manufacturer + "', " + "'" + observationReport.Data.ApMac + "', " + "'" + reportReceived + "'" + ")"; DBConnection.UpdateDB(queryString); } } Console.WriteLine("{0} reports received in this POST", x); }
private void ProcessRequestHandler(Task <HttpListenerContext> result) { var context = result.Result; HttpListenerRequest typeoHTTPRrequest = context.Request; HttpListenerResponse serverResponse = context.Response; // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // // INSERT YOUR KEY AND SECRET BELOW // // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! string validationResponse = "the.string.you.copied.from.the.meraki.portal"; string MySharedSecret = "the.secret.you.configured.in.the.meraki.portal"; if (!listener.IsListening) { return; } // Start new listener which replace this listener.GetContextAsync().ContinueWith(ProcessRequestHandler); // Read request string request = new StreamReader(context.Request.InputStream).ReadToEnd(); // the only GET we care about is the Meraki backend verifying this server as // a POST target, so we send the expected response below. Firewall yourself // appropriately... if (typeoHTTPRrequest.HttpMethod == "GET") { Console.WriteLine("SUCCESSFUL GET"); var responseBytes = System.Text.Encoding.UTF8.GetBytes(validationResponse); serverResponse.ContentLength64 = responseBytes.Length; var output = serverResponse.OutputStream; output.WriteAsync(responseBytes, 0, responseBytes.Length); output.Close(); } else if (typeoHTTPRrequest.HttpMethod == "POST") { Console.WriteLine("SUCCESSFUL POST"); // the v2 API will return a non-rfc "NaN" string where you've been // told to expect a float/double. The following prevents that from // nuking everything. Pre-req is .NET 5.0 var jsonOptions = new JsonSerializerOptions { NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals // | JsonNumberHandling.WriteAsString }; DevicesSeen observationReport = new DevicesSeen(); observationReport = JsonSerializer.Deserialize <DevicesSeen>(request, jsonOptions); // is the shared secret a match if (observationReport.Secret != MySharedSecret) { Console.WriteLine("Shared Secret does not match. Discarding received data."); return; } // for debug //DumpAsYaml(observationReport); // Deal with the POSTed data DataParser.ProcessData(observationReport); } }