Skip to content

Commit 3fe561f

Browse files
rayfoRaymond Fowkes
andauthored
Update GeoLocation free service to ip-api.com (#47)
Co-authored-by: Raymond Fowkes <REFowkes@frontier.com>
1 parent cd8bd55 commit 3fe561f

4 files changed

Lines changed: 83 additions & 108 deletions

File tree

src/BETA/ADDIN/ReadMe.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
Copyright (c) Microsoft Corporation. Licensed under the MIT License.
22

3-
This product includes GeoLite2 data created by MaxMind, available from
4-
https://www.maxmind.com
3+
This component uses the free IP-GeoLocation API from ip-api at: https://ip-api.com
54

65
This is the _only_ folder with binary executable code, consisting of WPA add-ins.
76
It can be removed with only limited loss of functionality for the WPA viewer.

src/BETA/WPAP/Network.wpaProfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
<Column Guid="e6b391a8-d2c4-d459-02a2-3a8b6585703d" Name="URL" SortPriority="2" Width="386" IsVisible="true" HelpText="Full URL Path" />
3535
<Column Guid="da5cd305-10bf-285c-61e2-493e6f232fb3" Name="IPAddr:Port" SortPriority="2" Width="170" IsVisible="true" HelpText="Remote IP Address &amp; Port" />
3636
<Column Guid="ec53a8c4-f07b-aed5-d882-5072c89799be" Name="Status" SortPriority="0" Width="150" IsVisible="false" HelpText="Last non-zero status of the transaction" />
37-
<Column Guid="6c2c4e61-8f05-ab1d-567a-d53851e77c3d" Name="GeoLocation" SortPriority="0" Width="180" IsVisible="false" HelpText="IP Geolocation by geoPlugin:&#xA; https://www.geoplugin.com/ &#xA;This product includes GeoLite data created by MaxMind,&#xA;available from: https://www.maxmind.com" />
37+
<Column Guid="6c2c4e61-8f05-ab1d-567a-d53851e77c3d" Name="GeoLocation" SortPriority="0" Width="180" IsVisible="false" HelpText="IP Geolocation provided by ip-api:&#xA; https://ip-api.com" />
3838
<Column Guid="e02d2ae0-3de9-d493-df2b-6b2d2813d302" Name="Duration" AggregationMode="Max" SortPriority="2" TextAlignment="Right" Width="96" CellFormat="mN" IsVisible="true" HelpText="Time from Open to Close (ms)">
3939
<DurationInViewOptionsParameter TimeStampColumnGuid="c2a764ec-9a6c-eb16-8137-cf1b6b8b8bea" TimeStampType="Start" InViewEnabled="false" />
4040
</Column>

src/NetBlame/Auxiliary/GeoLocation.cs

Lines changed: 80 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,47 @@
33

44
using System.Net;
55
using System.Net.Http;
6-
using System.Xml;
76
using System.IO;
87

8+
using System.Text.Json;
9+
using System.Text.Json.Serialization;
10+
911
using static NetBlameCustomDataSource.Util;
1012

1113
namespace NetBlameCustomDataSource
1214
{
13-
// IP Geolocation by geoPlugin: http://www.geoplugin.com/
14-
// This product includes GeoLite data created by MaxMind, available from: http://www.maxmind.com
15-
15+
/*
16+
This component uses the free IP-GeoLocation API from ip-api.
17+
- Service: https://ip-api.com
18+
- Attribution: Geolocation data provided by ip-api.com
19+
- License: Free for non-commercial use only
20+
- Rate limit: 45 requests per minute (free tier)
21+
*/
1622
static class GeoLocation
1723
{
18-
public static string Attribution => "IP Geolocation by geoPlugin:\n http://www.geoplugin.com/ \nThis product includes GeoLite data created by MaxMind,\navailable from: http://www.maxmind.com";
24+
public static string Attribution => "\nIP GeoLocation powered by ip-api.com – the free, non-commercial API.\nhttps://www.ip-api.com";
1925

20-
static string strGetXmlService = "http://www.geoplugin.net/xml.gp?ip=";
26+
static string strGeoService = "ip-api.com";
27+
static string strGetGeoService1 = "http://ip-api.com/json/";
28+
static string strGetGeoService2 = "?fields=status,message,country,regionName,city";
2129

22-
enum XmlName
30+
public class Place
2331
{
24-
none = 0x00,
25-
bitfCountry = 0x01,
26-
bitfRegion = 0x02,
27-
bitfCity = 0x04,
28-
bitfStatus = 0x08,
29-
bitfAll = bitfCountry | bitfRegion | bitfCity | bitfStatus
30-
};
32+
[JsonPropertyName("status")]
33+
public string Status { get; set; }
34+
35+
[JsonPropertyName("message")]
36+
public string Message { get; set; }
37+
38+
[JsonPropertyName("country")]
39+
public string Country { get; set; }
3140

41+
[JsonPropertyName("regionName")]
42+
public string Region { get; set; }
43+
44+
[JsonPropertyName("city")]
45+
public string City { get; set; }
46+
}
3247

3348
/*
3449
Return a string representing the geolocation of this IPAddress.
@@ -47,107 +62,68 @@ public static string GetGeoLocation(IPAddress ipAddr)
4762
if (ipAddr.IsIPv4MappedToIPv6)
4863
ipAddr = ipAddr.MapToIPv4();
4964

50-
string strRequest = strGetXmlService + ipAddr.ToString();
65+
string strRequest = strGetGeoService1 + ipAddr.ToString() + strGetGeoService2;
66+
67+
Place place = null;
5168

5269
try
5370
{
5471
HttpClient client = new HttpClient();
5572
using HttpResponseMessage response = client.GetAsync(strRequest).GetAwaiter().GetResult();
73+
74+
if (response.StatusCode == HttpStatusCode.TooManyRequests)
75+
return "Too Many Requests: " + strGeoService;
76+
77+
AssertImportant(response.StatusCode == HttpStatusCode.OK);
78+
5679
response.EnsureSuccessStatusCode(); // may throw
80+
5781
Stream stream = response.Content.ReadAsStreamAsync().GetAwaiter().GetResult();
82+
AssertImportant(stream?.CanRead ?? false);
5883

59-
// Parse the response XML into: Country / Region / City
60-
61-
XmlReader xmlReader = new XmlTextReader(stream);
62-
63-
string strCountryName = null;
64-
string strRegionName = null;
65-
string strCityName = null;
66-
string strStatus = null;
67-
68-
XmlName grbitName = XmlName.none;
69-
string strXmlName = string.Empty;
70-
while (xmlReader.Read())
71-
{
72-
string strValue = null;
73-
74-
switch (xmlReader.NodeType)
75-
{
76-
case XmlNodeType.Element:
77-
strXmlName = xmlReader.Name;
78-
continue; // no break test needed
79-
80-
case XmlNodeType.Text:
81-
strValue = xmlReader.Value;
82-
goto case XmlNodeType.Whitespace;
83-
84-
case XmlNodeType.Whitespace:
85-
switch (strXmlName)
86-
{
87-
case "geoplugin_countryName":
88-
grbitName |= XmlName.bitfCountry;
89-
strCountryName = strValue;
90-
break;
91-
case "geoplugin_region":
92-
grbitName |= XmlName.bitfRegion;
93-
strRegionName = strValue;
94-
break;
95-
case "geoplugin_city":
96-
grbitName |= XmlName.bitfCity;
97-
strCityName = strValue;
98-
break;
99-
case "geoplugin_status":
100-
grbitName |= XmlName.bitfStatus;
101-
strStatus = strValue;
102-
break;
103-
default:
104-
continue; // no break test needed
105-
}
106-
strXmlName = null;
107-
break;
108-
default:
109-
continue; // no break test needed
110-
} // switch xmlReader.NodeType
111-
112-
if (grbitName == XmlName.bitfAll)
113-
break;
114-
} // while mlReader.Read
115-
116-
AssertImportant(grbitName == XmlName.bitfAll); // Saw the expected XML records.
117-
118-
// Turn the three location strings into one.
119-
120-
System.Text.StringBuilder sbLocation = new System.Text.StringBuilder(32);
121-
122-
if (strCountryName != null)
123-
{
124-
if (strRegionName != null && strCountryName == "United States")
125-
strCountryName = "USA";
126-
127-
sbLocation.Append(strCountryName);
128-
}
129-
if (strRegionName != null) // This is the State if the Country is USA.
130-
{
131-
sbLocation.Append(" / ");
132-
sbLocation.Append(strRegionName);
133-
}
134-
if (strCityName != null)
135-
{
136-
sbLocation.Append(" / ");
137-
sbLocation.Append(strCityName);
138-
}
139-
if (strStatus != null && sbLocation.Length == 0)
140-
{
141-
sbLocation.Append("Geo status: ");
142-
sbLocation.Append(strStatus);
143-
}
144-
145-
return sbLocation.ToString();
84+
// Parse the response JSON into: Country / Region / City
85+
86+
if (stream?.CanRead ?? false)
87+
place = JsonSerializer.Deserialize<Place>(stream);
14688
}
147-
catch
89+
catch (System.Exception x)
14890
{
149-
return string.Empty;
91+
return x.Message;
92+
}
93+
94+
if (place == null)
95+
return Util.strNA; // N/A
96+
97+
if (!place.Status.Equals("success"))
98+
return place.Message;
99+
100+
// Turn the three location strings into one.
101+
102+
System.Text.StringBuilder sbLocation = new System.Text.StringBuilder(32);
103+
104+
if (!string.IsNullOrWhiteSpace(place.Country))
105+
{
106+
if (!string.IsNullOrWhiteSpace(place.Region) && place.Country.Equals("United States"))
107+
place.Country = "USA";
108+
109+
sbLocation.Append(place.Country);
150110
}
111+
if (!string.IsNullOrWhiteSpace(place.Region)) // This is the State if the Country is USA.
112+
{
113+
sbLocation.Append(" / ");
114+
sbLocation.Append(place.Region);
115+
}
116+
if (!string.IsNullOrWhiteSpace(place.City))
117+
{
118+
sbLocation.Append(" / ");
119+
sbLocation.Append(place.City);
120+
}
121+
122+
AssertImportant(sbLocation.Length > 0);
123+
if (sbLocation.Length == 0)
124+
return Util.strNA; // N/A
125+
126+
return sbLocation.ToString();
151127
} // GetGeoLocation
152128
} // class GeoLocation
153129
}

src/NetBlame/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ This add-in analyzes and summarizes network and thread pool ETW events:
1717

1818
It is based on the [Microsoft Performance Toolkit SDK](https://github.com/microsoft/microsoft-performance-toolkit-sdk)
1919

20-
This product includes GeoLite2 data created by MaxMind, available from https://www.maxmind.com
20+
This product includes GeoLocation data created by ip-api, available at https://ip-api.com
2121

2222
# Build
2323

0 commit comments

Comments
 (0)