Example #1
0
// CreateSuggestion .
func CreateSuggestion(w rest.ResponseWriter, r *rest.Request) {

	form := shared.BrowserSuggestionTokenRequest{}
	err := r.DecodeJsonPayload(&form)
	if err != nil {
		// apiError(w, err.Error(), http.StatusInternalServerError)
		apiutils.WriteRestError(w, err)
		return
	}

	var invalids fielderrors.ValidationErrorList

	// parse and validate url.
	URL := strings.TrimSpace(form.URL)
	if URL == "" {
		invalids = append(invalids, fielderrors.NewFieldRequired("URL"))
		// apiError(w, "no or empty URL", http.StatusBadRequest)

	}
	u, err := url.Parse(URL)
	if err != nil {
		invalids = append(invalids, fielderrors.NewFieldInvalid("URL", URL, err.Error()))
		// apiError(w, fmt.Sprintf("%s is not a valid URL", URL), http.StatusBadRequest)

	}

	host := u.Host
	if strings.Contains(host, ":") {
		host, _, err = net.SplitHostPort(u.Host)
		if err != nil {
			invalids = append(invalids, fielderrors.NewFieldInvalid("URL", URL, err.Error()))
		}
	}

	if !shared.AcceptedURL(u) {
		invalids = append(invalids, fielderrors.NewFieldValueNotSupported("URL", URL, nil))
	}

	if len(invalids) > 0 {
		apiutils.WriteRestError(w, apierrors.NewInvalid("create-suggestion", "URL", invalids))
		return
	}

	s := client.NewSuggestion(u.String())
	defer s.DoneAddingSamples()
	measurers, err := measure.DefaultMeasurements(form.URL)
	if err != nil {
		apiutils.WriteRestError(w, apierrors.NewInternalError(err))
		return
	}

	for _, v := range measurers {
		m, err := v.Measure()
		if err != nil {
			lg.Errorf("could not measure: %s", err.Error())
		} else {
			switch m.Type() {
			case sampletypes.DNSQuery, sampletypes.HTTPHeader:
				err = s.AddMeasurement(m)
				if err != nil {
					lg.Errorln(err.Error())
					return
				}
			default:
				lg.Warningf("unsupported sample type: %s", m.Type().String())
			}
		}
	}
}
Example #2
0
// SuggestionToken JSON API method.
func SuggestionToken(dbclients db.Clients) func(w rest.ResponseWriter, r *rest.Request) {
	return func(w rest.ResponseWriter, r *rest.Request) {
		// HANDLE USERIP BEGIN
		req := shared.SuggestionTokenRequest{}
		err := r.DecodeJsonPayload(&req)
		if err != nil {
			apiError(w, shared.SafeClean(err.Error()), http.StatusInternalServerError)
			return
		}

		// validate country code.
		if !validCountryCode(req.CountryCode) {
			apiError(w,
				fmt.Sprintf("invalid country code: %s", req.CountryCode),
				http.StatusBadRequest)
			return
		}

		// parse/validate client ip address.

		IP := req.ClientAddr
		if IP == nil {
			apiError(w, "bad ClientAddr", http.StatusBadRequest)
			return
		}

		// parse and validate url.
		URL := strings.TrimSpace(req.URL)
		if URL == "" {
			apiError(w, "no or empty URL", http.StatusBadRequest)
			return
		}

		u, err := url.Parse(URL)
		if err != nil {
			apiError(w, fmt.Sprintf("%s is not a valid URL", URL), http.StatusBadRequest)
			return
		}

		if !shared.AcceptedURL(u) {
			apiError(w, fmt.Sprintf("%s is not a valid URL", URL), http.StatusBadRequest)
			return
		}

		// resolve ip to asn.
		var ASN int
		ASNres, err := dbclients.Internet.IP2ASN(IP)
		if err != nil {
			lg.Errorln(shared.SafeClean(err.Error()))
			apiError(w, shared.SafeClean(err.Error()), http.StatusInternalServerError)
			return
		}
		if ASNres != nil {
			ASN = ASNres.ASN
		} else {
			lg.Warningf("no ASN lookup result for IP: %s ", shared.SafeClean(IP.String()))
		}

		// reoslve ip to country code.
		countryCode := dbclients.Maxmind.IP2CountryCode(IP)

		// resolve ip to city geonameid
		geoCityID := dbclients.Maxmind.IP2CityGeoNameID(IP)

		req.ClientAddr = net.IPv4zero
		IP = net.IPv4zero
		// HANDLE USERIP END

		{
			supported, err := dbclients.DB.IsURLAllowed(u, countryCode)
			if err != nil {
				// TODO: standardize http status codes
				apiError(w, err.Error(), http.StatusForbidden)
				return
			}
			if !supported {
				lg.Infof("got request for unsupported URL %s")
				w.WriteJson(shared.SuggestionTokenResponse{
					Ok:  false,
					URL: req.URL,
				})
				return
			}
		}
		// start new submission token session
		token := db.SessionTokens.New(URL)

		// create newclienttoken sample data
		sample := shared.NewClientTokenSample{
			URL:         URL,
			CountryCode: req.CountryCode,
		}
		sampleData, err := json.Marshal(sample)
		if err != nil {
			lg.Errorln(err)
			apiError(w, "error #20150424-002542-CEST", http.StatusInternalServerError)
			return
		}

		// create extraData
		extra := shared.IPExtraData{
			CityGeoNameID: geoCityID,
		}
		extraData, err := json.Marshal(extra)
		if err != nil {
			lg.Errorln(err)
			apiError(w, "error #20150427-211052-CEST", http.StatusInternalServerError)
			return
		}

		// insert into db
		{
			err := dbclients.DB.InsertSample(db.Sample{
				Host:        u.Host,
				CountryCode: countryCode,
				ASN:         ASN,
				Type:        "NewClientToken",
				Origin:      "Central",
				Token:       token,
				Data:        sampleData,
				ExtraData:   extraData,
			})
			if err != nil {
				apiError(w, err.Error(), http.StatusInternalServerError)
				return
			}
		}

		// queue central measurements
		measurements, err := measure.DefaultMeasurements(req.URL)
		if err != nil {
			lg.Warningf("could not create standard measurements: %s", err.Error())
		} else {
			queueMeasurements(token, measurements...)
		}

		// write json response
		{
			err := w.WriteJson(shared.SuggestionTokenResponse{
				Ok:    true,
				URL:   URL,
				Token: token,
			})
			if err != nil {
				lg.Errorln(err.Error())
				return
			}
		}

	}
}