func (p *PreparedSample) Update(dbclients db.Clients) error { IP := shared.GetPublicIPAddr() if IP == nil { return errors.New("could not get own public ip address") } // resolve ip to asn. var ASN int ASNres, err := dbclients.Internet.IP2ASN(IP) if err != nil { lg.Errorln(err.Error()) return err } if ASNres != nil { ASN = ASNres.ASN } else { lg.Warningf("no ASN lookup result for IP: %s ", IP) return fmt.Errorf("no ASN lookup result for IP: %s ", IP) } // resolve ip to country code. countryCode := dbclients.Maxmind.IP2CountryCode(IP) s := db.Sample{ CountryCode: countryCode, ASN: ASN, } p.lastUpdated = time.Now() p.s = s return nil }
// AddSample adds a sample to the local cache ready to be sent to central using SendSamples. func (sugg *Suggestion) SendSamples(client *Client) (int, error) { s, ok := GetSuggestion(sugg.ID) if !ok { return 0, apierrors.NewNotFound("suggestion", sugg.ID) } if s.Token == "" { return 0, apierrors.NewConflict("suggestion", "", errors.New("No session token associated with suggestion")) } n := 0 var rerr error for _, v := range s.samples { r, err := client.CreateSample(shared.StoreSampleRequest{ Sample: &shared.Sample{ Token: s.Token, SampleType: v.sampleType, Data: v.data, URL: s.URL, }, ClientAddr: shared.GetPublicIPAddr().String(), }) if err != nil { rerr = errors.New("error sending sample: " + err.Error()) continue } if !r.Ok { rerr = errors.New("error sending sample, not accepted") continue } n++ modifySuggestion <- removeSample(s, v) } return n, rerr }
// Init does precondition check if the application can/should be started. // Init will return an error message with reason for exit printed. func Run() { if debugEnabled { log.Println("ALKASIR_DEBUG ENABLED!") err := os.Setenv("ALKASIR_DEBUG", "1") if err != nil { log.Fatal(err) } } if hotEnabled { log.Println("ALKASIR_HOT ENABLED!") err := os.Setenv("ALKASIR_HOT", "1") if err != nil { log.Fatal(err) } } // the darwin systray does not exit the main loop if runtime.GOOS != "darwin" { uiRunning.Add(1) } err := ui.Run(func() { Atexit(ui.Done) // start the getpublic ip updater. go func() { _ = shared.GetPublicIPAddr() }() if debugEnabled { go func() { err := http.ListenAndServe( fmt.Sprintf("localhost:%d", debugPort), nil) if err != nil { panic(err) } }() } // wipe user data if wipeData { settingsdir := clientconfig.ConfigPath() if settingsdir == "" { log.Println("[wipe] Configdir not set") os.Exit(1) } settingsfile := clientconfig.ConfigPath("settings.json") if _, err := os.Stat(settingsfile); os.IsNotExist(err) { log.Println("[wipe] No settings.json in configdir, will NOT wipe data") os.Exit(1) } log.Println("Wiping all user data") if err := os.RemoveAll(settingsdir); err != nil { log.Println(err) } } // Prepare logging logdir := clientconfig.ConfigPath("log") err := os.MkdirAll(logdir, 0775) if err != nil { log.Println("Could not create logging directory") os.Exit(1) } err = flag.Set("log_dir", logdir) if err != nil { panic(err) } lg.SetSrcHighlight("alkasir/cmd", "alkasir/pkg") lg.CopyStandardLogTo("INFO") // Start init if VERSION != "" { lg.Infoln("Alkasir v" + VERSION) } else { lg.Warningln("Alkasir dev version (VERSION not set)") } lg.V(1).Info("Log v-level:", lg.Verbosity()) _, err = clientconfig.Read() if err != nil { lg.Infoln("Could not read config") exit() } lg.V(30).Infoln("settings", clientconfig.Get().Settings) if saveChromeExt { err := saveChromeExtension() if err != nil { lg.Fatal(err) } } { configChanged, err := clientconfig.UpgradeConfig() if err != nil { lg.Fatalln("Could not upgrade config", err) } clientconfig.Update(func(conf *clientconfig.Config) error { if clientAuthKeyFlag != "" { lg.Warningln("Overriding generated authKey with", clientAuthKeyFlag) conf.Settings.Local.ClientAuthKey = clientAuthKeyFlag configChanged = true } if bindAddrFlag != "" { lg.Warningln("Overriding configured bindAddr with", bindAddrFlag) conf.Settings.Local.ClientBindAddr = bindAddrFlag configChanged = true } if centralAddrFlag != "" { lg.Warningln("Overriding central server addr with", centralAddrFlag) conf.Settings.Local.CentralAddr = centralAddrFlag configChanged = true } return nil }) if configChanged { if err := clientconfig.Write(); err != nil { lg.Warning(err) } } } conf := clientconfig.Get() loadTranslations(LanguageOptions...) if err := ui.Language(conf.Settings.Local.Language); err != nil { lg.Warningln(err) } go func() { select { case <-sigIntC: exit() case <-ui.Actions.Quit: exit() } }() for _, e := range []error{ mime.AddExtensionType(".json", "application/json"), mime.AddExtensionType(".js", "application/javascript"), mime.AddExtensionType(".css", "text/css"), mime.AddExtensionType(".md", "text/plain"), } { if e != nil { lg.Warning(e) } } err = startInternalHTTPServer(conf.Settings.Local.ClientAuthKey) if err != nil { lg.Fatal("could not start internal http services") } // Connect the default transport service.UpdateConnections(conf.Settings.Connections) service.UpdateTransports(conf.Settings.Transports) go service.StartConnectionManager(conf.Settings.Local.ClientAuthKey) // TODO: async pac.UpdateDirectList(conf.DirectHosts.Hosts) pac.UpdateBlockedList(conf.BlockedHostsCentral.Hosts, conf.BlockedHosts.Hosts) lastBlocklistChange = time.Now() go StartBlocklistUpgrader() if upgradeDiffsBaseURL != "" { lg.V(19).Infoln("upgradeDiffsBaseURL is ", upgradeDiffsBaseURL) go StartBinaryUpgradeChecker(upgradeDiffsBaseURL) } else { lg.Warningln("empty upgradeDiffsBaseURL, disabling upgrade checks") } lg.V(5).Info("Alkasir has started") }) // the darwin systray does not exit the main loop if runtime.GOOS != "darwin" { uiRunning.Done() } lg.Infoln("ui.Run ended") if err != nil { log.Println("client.Run error:", err) } }
// SubmitSuggestion initiates the comminication with Central for a Submission // session. func SubmitSuggestion(w rest.ResponseWriter, r *rest.Request) { // // TODO This is the response that must be forwarded from central/api and parsed by client and passed on to the browser. // apiutils.WriteRestError(w, // apierrors.NewInvalid("object", "suggestion", // fielderrors.ValidationErrorList{ // fielderrors.NewFieldValueNotSupported("URL", "...", []string{})})) // return ID := r.PathParam("id") suggestion, ok := client.GetSuggestion(ID) if !ok { apiutils.WriteRestError(w, apierrors.NewNotFound("suggestion", ID)) return } wanip := shared.GetPublicIPAddr() if wanip == nil { lg.Warning("could not resolve public ip addr") } conf := clientconfig.Get() restclient, err := NewRestClient() if err != nil { apiutils.WriteRestError(w, apierrors.NewInternalError(err)) return } tokenResp, err := suggestion.RequestToken( restclient, wanip, conf.Settings.Local.CountryCode) if err != nil { if apiutils.IsNetError(err) { apiutils.WriteRestError(w, apierrors.NewServerTimeout("alkasir-central", "request-submission-token", 0)) } else { apiutils.WriteRestError(w, apierrors.NewInternalError(err)) } return } n, err := suggestion.SendSamples(restclient) if err != nil { lg.Warningln("error sending samples", err.Error()) } lg.V(5).Infof("sent %d samples", n) // continue sending samples if future measuremetns are expected to come prepared, err := suggestion.Prepared() if err != nil { lg.Errorln(err) } else if !prepared { lg.V(5).Infof("all samples not collected, will try to send the rest when they are done") go func(s client.Suggestion) { start := time.Now() t := time.NewTicker(30 * time.Second) defer t.Stop() submitSamples: for range t.C { if time.Now().After(start.Add(15 * time.Minute)) { lg.Errorln("Stopping trying to send additional samples") return } prepared, err := suggestion.Prepared() if err != nil { lg.Errorln(err) return } if prepared { restclient, err := NewRestClient() if err != nil { continue submitSamples } n, err := suggestion.SendSamples(restclient) lg.V(5).Infof("sent %d samples", n) if err != nil { lg.Warningln("error sending samples", err.Error()) continue submitSamples } return } } }(suggestion) } u, err := url.Parse(suggestion.URL) if err != nil { lg.Errorln(err) } else { err := clientconfig.Update(func(conf *clientconfig.Config) error { conf.BlockedHosts.Add(u.Host) lastBlocklistChange = time.Now() pac.UpdateBlockedList(conf.BlockedHostsCentral.Hosts, conf.BlockedHosts.Hosts) return nil }) if err != nil { lg.Errorln(err) } } w.WriteJson(tokenResp) }
// Run runs the initialized server. func Run() { var wg sync.WaitGroup // start monitor server go startMonitoring(*monitorBindAddr) // start the getpublic ip updater. go func() { _ = shared.GetPublicIPAddr() }() wg.Add(1) go func() { defer wg.Done() lg.V(2).Infoln("Loading recent sessions from postgres...") recents, err := sqlDB.RecentSuggestionSessions(20000) if err != nil { lg.Fatal(err) } db.SessionTokens.Reset(recents) lg.V(2).Infof("Loaded %d sessions from postgres...", len(recents)) lg.Flush() }() wg.Add(1) go func() { defer wg.Done() conn := redisPool.Get() defer conn.Close() lg.V(2).Infoln("BGPDump refresh started...") n, err := internet.RefreshBGPDump(conn) lg.V(2).Infof("BGPDump refresh ended, %d items added.", n) lg.Flush() if err != nil { if *offline { lg.Infoln("offline", err) } else { lg.Fatal(err) } } }() wg.Add(1) go func() { defer wg.Done() conn := redisPool.Get() defer conn.Close() lg.V(2).Infoln("CIDRReport refresh started...") n, err := internet.RefreshCIDRReport(conn) lg.V(2).Infof("CIDRReport refresh ended, %d items added", n) if err != nil { if *offline { lg.Infoln("offline", err) } else { lg.Fatal(err) } } }() wg.Wait() // start signal handling wg.Add(1) go func() { ch := make(chan os.Signal) signal.Notify(ch, syscall.SIGINT) lg.Infoln(<-ch) wg.Done() }() internetClient := db.NewInternetClient(redisPool) maxmindClient := db.NewMaxmindClient(mmCountryDB, mmCityDB) clients := db.Clients{ DB: sqlDB, Internet: internetClient, Maxmind: maxmindClient, } // start http json api server go func(addr string, dba db.Clients) { mux, err := apiMux(dba) lg.Info("Starting http server", addr) err = http.ListenAndServe(addr, mux) if err != nil { lg.Fatal(err) } }(*apiBindAddr, clients) // start http export api server go func(addr string, dba db.Clients) { if *exportApiSecretKey == "" { lg.Warningln("exportApiSecretKey flag/env not set, will not start export api server") b := make([]byte, 32) _, err := rand.Read(b) if err != nil { lg.Fatalf("random generator not functioning...") return } suggestedkey := base64.StdEncoding.EncodeToString(b) lg.Infoln("suggested export key:", suggestedkey) return } key, err := base64.StdEncoding.DecodeString(*exportApiSecretKey) if err != nil { lg.Fatalf("could not decode export api secret key: %s", *exportApiSecretKey) } mux, err := apiMuxExport(dba, key) lg.Info("Starting export api server", addr) err = http.ListenAndServe(addr, mux) if err != nil { lg.Fatal(err) } }(*exportApiBindAddr, clients) go analysis.StartAnalysis(clients) startMeasurer(clients) wg.Wait() }
// SubmitSuggestion initiates the comminication with Central for a Submission // session. func SubmitSuggestion(w rest.ResponseWriter, r *rest.Request) { // // TODO This is the response that must be forwarded from central/api and parsed by client and passed on to the browser. // apiutils.WriteRestError(w, // apierrors.NewInvalid("object", "suggestion", // fielderrors.ValidationErrorList{ // fielderrors.NewFieldValueNotSupported("URL", "...", []string{})})) // return ID := r.PathParam("id") suggestion, ok := client.GetSuggestion(ID) if !ok { apiutils.WriteRestError(w, apierrors.NewNotFound("suggestion", ID)) return } wanip := shared.GetPublicIPAddr() if wanip == nil { lg.Warning("could not resolve public ip addr") } conf := clientconfig.Get() restclient, err := NewRestClient() if err != nil { apiutils.WriteRestError(w, apierrors.NewInternalError(err)) return } tokenResp, err := suggestion.RequestToken( restclient, wanip, conf.Settings.Local.CountryCode) if err != nil { if apiutils.IsNetError(err) { apiutils.WriteRestError(w, apierrors.NewServerTimeout("alkasir-central", "request-submission-token", 0)) } else { apiutils.WriteRestError(w, apierrors.NewInternalError(err)) } return } n, err := suggestion.SendSamples(restclient) if err != nil { lg.Warningln("error sending samples", err.Error()) } lg.V(5).Infoln("sent ", n) // FIXME PRESENTATION: just add the url locally u, err := url.Parse(suggestion.URL) if err != nil { lg.Errorln(err) } else { err := clientconfig.Update(func(conf *clientconfig.Config) error { conf.BlockedHosts.Add(u.Host) lastBlocklistChange = time.Now() pac.UpdateBlockedList(conf.BlockedHostsCentral.Hosts, conf.BlockedHosts.Hosts) return nil }) if err != nil { lg.Errorln(err) } } w.WriteJson(tokenResp) }
// deprecated, function moved to shared package func getPublicIPAddr() net.IP { return shared.GetPublicIPAddr() }