diff --git a/controllers/route.go b/controllers/route.go index 15f08351..d659df76 100644 --- a/controllers/route.go +++ b/controllers/route.go @@ -4,6 +4,7 @@ import ( "fmt" "html/template" "log" + "net" "net/http" "os" @@ -101,6 +102,16 @@ func PhishTracker(w http.ResponseWriter, r *http.Request) { if err != nil { Logger.Println(err) } + // Update the GeoIP information + ip, _, err := net.SplitHostPort(r.RemoteAddr) + if err == nil { + err = rs.UpdateGeo(ip) + if err != nil { + Logger.Println(err) + } + } else { + Logger.Println(err) + } w.Write([]byte("")) } diff --git a/models/result.go b/models/result.go index 456fbc18..bcaf9ee7 100644 --- a/models/result.go +++ b/models/result.go @@ -4,25 +4,62 @@ import ( "crypto/rand" "fmt" "io" + "log" + "net" "github.com/jinzhu/gorm" + "github.com/oschwald/maxminddb-golang" ) +type mmCity struct { + GeoPoint mmGeoPoint `maxminddb:"location"` +} + +type mmGeoPoint struct { + Latitude float64 `maxminddb:"latitude"` + Longitude float64 `maxminddb:"longitude"` +} + type Result struct { - Id int64 `json:"-"` - CampaignId int64 `json:"-"` - UserId int64 `json:"-"` - RId string `json:"id"` - Email string `json:"email"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - Status string `json:"status" sql:"not null"` + Id int64 `json:"-"` + CampaignId int64 `json:"-"` + UserId int64 `json:"-"` + RId string `json:"id"` + Email string `json:"email"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + Status string `json:"status" sql:"not null"` + IP string `json:"ip"` + Latitude float64 `json:"latitude"` + Longitude float64 `json:"longitude"` } func (r *Result) UpdateStatus(s string) error { return db.Table("results").Where("id=?", r.Id).Update("status", s).Error } +func (r *Result) UpdateGeo(addr string) error { + // Open a connection to the maxmind db + mmdb, err := maxminddb.Open("static/db/geolite2-city.mmdb") + if err != nil { + log.Fatal(err) + } + defer mmdb.Close() + ip := net.ParseIP(addr) + var city mmCity + // Get the record + err = mmdb.Lookup(ip, &city) + if err != nil { + return err + } + // Update the database with the record information + return db.Table("results").Where("id=?", r.Id).Updates(map[string]interface{}{ + "ip": addr, + "latitude": city.GeoPoint.Latitude, + "longitude": city.GeoPoint.Longitude, + }).Error +} + func (r *Result) GenerateId() { // Keep trying until we generate a unique key (shouldn't take more than one or two iterations) k := make([]byte, 32) diff --git a/static/css/main.css b/static/css/main.css index 6edbed86..68a6490f 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -323,3 +323,6 @@ font-family: 'Courier New',Monospace !important; font-size: small !important; } +#resultsMap { + margin-top:-30px; +} diff --git a/static/db/geolite2-city.mmdb b/static/db/geolite2-city.mmdb new file mode 100644 index 00000000..26f117de Binary files /dev/null and b/static/db/geolite2-city.mmdb differ diff --git a/static/js/app/campaign_results.js b/static/js/app/campaign_results.js index fa38fbff..1f4ce488 100644 --- a/static/js/app/campaign_results.js +++ b/static/js/app/campaign_results.js @@ -183,25 +183,70 @@ $(document).ready(function(){ }); $("#loading").hide() $("#campaignResults").show() + map = new Datamap({ + element: document.getElementById("resultsMap"), + responsive: true, + fills: { + defaultFill: "#ffffff", + point: "#34495e" + }, + geographyConfig: { + highlightFillColor : "#1abc9c", + borderColor:"#34495e" + }, + bubblesConfig: { + borderColor: "#34495e" + } + }); + bubbles = [] + $.each(campaign.results, function(i, result){ + // Check that it wasn't an internal IP + if (result.latitude == 0 && result.longitude == 0) { return true; } + newIP = true + $.each(bubbles, function(i, bubble){ + if (bubble.ip == result.ip){ + bubbles[i].radius += 1 + newIP = false + return false + } + }) + if (newIP){ + console.log("Adding bubble at: ") + console.log({ + latitude : result.latitude, + longitude: result.longitude, + name : result.ip, + fillKey: "point" + }) + bubbles.push({ + latitude : result.latitude, + longitude: result.longitude, + name : result.ip, + fillKey: "point", + radius: 2 + }) + } + }) + map.bubbles(bubbles) } // Load up the map data (only once!) - // Slated for 0.2 release - coming soon! :) - // $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) { - // if ($(e.target).attr('href') == "#plugins"){ - // if (!map){ - // map = new Datamap({ - // element: document.getElementById("resultsMap"), - // responsive: true, - // fills: { - // defaultFill: "#34495e" - // }, - // geographyConfig: { - // highlightFillColor : "#1abc9c" - // } - // }); - // } - // } - // }) + $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) { + if ($(e.target).attr('href') == "#overview"){ + if (!map){ + map = new Datamap({ + element: document.getElementById("resultsMap"), + responsive: true, + fills: { + defaultFill: "#ffffff" + }, + geographyConfig: { + highlightFillColor : "#1abc9c", + borderColor:"#34495e" + } + }); + } + } + }) }) .error(function(){ $("#loading").hide() diff --git a/templates/campaign_results.html b/templates/campaign_results.html index c5234c8d..8bba8327 100644 --- a/templates/campaign_results.html +++ b/templates/campaign_results.html @@ -52,8 +52,8 @@