예제 #1
0
func queryServerIDRetriever(w http.ResponseWriter, ids []string) {
	s := make(chan map[string]string, len(ids))
	db.ServerDB.GetHostsAndGameFromIDAPIQuery(s, ids)
	hostsgames := <-s
	if len(hostsgames) == 0 {
		w.WriteHeader(http.StatusOK)
		if err := json.NewEncoder(w).Encode(models.GetDefaultServerList()); err != nil {
			writeJSONEncodeError(w, err)
		}
		return
	}
	serverlist, err := steam.Query(hostsgames)
	if err != nil {
		setNotFoundAndLog(w, err)
		if err := json.NewEncoder(w).Encode(models.GetDefaultServerList()); err != nil {
			writeJSONEncodeError(w, err)
			return
		}
		return
	}
	if err := json.NewEncoder(w).Encode(serverlist); err != nil {
		writeJSONEncodeError(w, err)
		logger.LogWebError(err)
	}
}
예제 #2
0
파일: query.go 프로젝트: syncore/a2sapi
// Query retrieves the server information for a given set of host to game pairs
// and returns it in a format that is presented to the API. It takes a map consisting
// of host(s) and their corresponding game names (i.e: k:127.0.0.1:27960, v:"QuakeLive")
func Query(hostsgames map[string]string) (*models.APIServerList, error) {
	hg := make(map[string]filters.Game, len(hostsgames))
	needsPlayers := make([]string, len(hostsgames))
	needsRules := make([]string, len(hostsgames))
	needsInfo := make([]string, len(hostsgames))

	for host, game := range hostsgames {
		fg := filters.GetGameByName(game)
		hg[host] = fg
		if !fg.IgnoreRules {
			needsRules = append(needsRules, host)
		}
		if !fg.IgnorePlayers {
			needsPlayers = append(needsPlayers, host)
		}
		if !fg.IgnoreInfo {
			needsInfo = append(needsInfo, host)
		}
	}
	data := a2sData{
		HostsGames: hg,
		Info:       batchInfoQuery(needsInfo),
		Rules:      batchRuleQuery(needsRules),
		Players:    batchPlayerQuery(needsPlayers),
	}

	sl, err := buildServerList(data, true)
	if err != nil {
		return models.GetDefaultServerList(), logger.LogAppError(err)
	}
	return sl, nil
}
예제 #3
0
파일: handlers.go 프로젝트: syncore/a2sapi
func queryServerAddrs(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")

	if !config.Config.WebConfig.AllowDirectUserQueries {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w,
			`{"error": {"code": 400,"message": "Direct server queries are disabled. Use the %s parameter."}}`,
			qsQueryServerIDs)
		return
	}
	addresses := getQStringValues(r.URL.Query(), qsQueryServerAddrs)
	logger.WriteDebug("addresses length: %d", len(addresses))
	logger.WriteDebug("addresses are: %s", addresses)

	if addresses == nil {
		w.WriteHeader(http.StatusOK)
		logger.WriteDebug("queryServerAddr: Got empty address query. Ignoring.")
		writeJSONResponse(w, models.GetDefaultServerList())
		return
	}

	var parsedaddresses []string
	for _, addr := range addresses {
		host, err := net.ResolveTCPAddr("tcp4", addr)
		if err != nil {
			continue
		}
		parsedaddresses = append(parsedaddresses, fmt.Sprintf("%s:%d", host.IP, host.Port))
	}

	if len(parsedaddresses) == 0 {
		w.WriteHeader(http.StatusOK)
		logger.WriteDebug("queryServerAddr: No valid addresses for query. Ignoring.")
		writeJSONResponse(w, models.GetDefaultServerList())
		return
	}

	if len(parsedaddresses) > config.Config.WebConfig.MaximumHostsPerAPIQuery {
		logger.WriteDebug("Maximum number of allowed API query hosts exceeded, truncating")
		parsedaddresses = parsedaddresses[:config.Config.WebConfig.MaximumHostsPerAPIQuery]
	}
	queryServerAddrRetriever(w, parsedaddresses)
}
예제 #4
0
func queryServerAddrRetriever(w http.ResponseWriter, addresses []string) {
	serverlist, err := steam.DirectQuery(addresses)
	if err != nil {
		setNotFoundAndLog(w, err)
		if err := json.NewEncoder(w).Encode(models.GetDefaultServerList()); err != nil {
			writeJSONEncodeError(w, err)
			return
		}
		return
	}
	if err := json.NewEncoder(w).Encode(serverlist); err != nil {
		writeJSONEncodeError(w, err)
	}
}
예제 #5
0
파일: query.go 프로젝트: syncore/a2sapi
// DirectQuery allows a user to query any host even if it is not in the internal
// server ID database. It is primarily intended for testing as it has two main
// issues: 1) obvious security implications, 2) determining which game a user-
// supplied host represents rests on potentially unreliable assumptions, which if
// not true would cause games with incomplete support for all three A2S queries
// (e.g. Reflex) to always fail. A production environment should use Query() instead.
func DirectQuery(hosts []string) (*models.APIServerList, error) {
	hg := make(map[string]filters.Game, len(hosts))

	// Try to account for the fact that we can't determine the game ahead of time
	// for user-specified direct host queries -- a number of assumptions:
	// (1) A2S_INFO for game/host, (2) extra data A2S_INFO flag & field w/ appid,
	//(3) game has been defined in game.go with the correct AppID and A2S ignore flags
	info := batchInfoQuery(hosts)
	needsRules := make([]string, len(hosts))
	needsPlayers := make([]string, len(hosts))

	for _, h := range hosts {
		logger.WriteDebug("direct query for %s. will try to figure out needed queries", h)
		if (info[h] != models.SteamServerInfo{}) {
			logger.WriteDebug("A2S_INFO not empty. got gameid: %d", info[h].ExtraData.GameID)
			fg := filters.GetGameByAppID(info[h].ExtraData.GameID)
			hg[h] = fg
			if !fg.IgnoreRules {
				logger.WriteDebug("based on game %s for %s, will need to get A2S_RULES",
					fg.Name, h)
				needsRules = append(needsRules, h)
			}
			if !fg.IgnorePlayers {
				logger.WriteDebug("based on game %s for %s, will need to get A2S_PLAYERS",
					fg.Name, h)
				needsPlayers = append(needsPlayers, h)
			}
		} else {
			logger.WriteDebug("A2S_INFO is nil. game will be unspecified; results may vary")
			hg[h] = filters.GameUnspecified
		}
	}
	data := a2sData{
		HostsGames: hg,
		Info:       info,
		Rules:      batchRuleQuery(needsRules),
		Players:    batchPlayerQuery(needsPlayers),
	}
	sl, err := buildServerList(data, true)
	if err != nil {
		return models.GetDefaultServerList(), logger.LogAppError(err)
	}
	return sl, nil
}
예제 #6
0
파일: handlers.go 프로젝트: syncore/a2sapi
func queryServerIDs(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	ids := getQStringValues(r.URL.Query(), qsQueryServerIDs)
	logger.WriteDebug("queryServerID: ids length: %d", len(ids))
	logger.WriteDebug("queryServerID: ids are: %s", ids)

	if ids == nil {
		w.WriteHeader(http.StatusOK)
		logger.WriteDebug("queryServerID: Got empty query. Ignoring.")
		writeJSONResponse(w, models.GetDefaultServerList())
		return
	}
	if len(ids) > config.Config.WebConfig.MaximumHostsPerAPIQuery {
		logger.WriteDebug("Maximum number of allowed API query hosts exceeded, truncating")
		ids = ids[:config.Config.WebConfig.MaximumHostsPerAPIQuery]
	}

	queryServerIDRetriever(w, ids)
}
예제 #7
0
파일: handlers.go 프로젝트: syncore/a2sapi
func getServers(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	var asl *models.APIServerList

	if config.Config.DebugConfig.ServerDumpFileAsMasterList {
		asl = useDumpFileAsMasterList(constants.DumpFileFullPath(
			config.Config.DebugConfig.ServerDumpFilename))
	} else {
		asl = models.MasterList
	}
	// Empty (i.e. during first retrieval/startup)
	if asl == nil {
		writeJSONResponse(w, models.GetDefaultServerList())
		return
	}
	srvfilters := getSrvFilterFromQString(r.URL.Query(), getServersQueryStrings)
	logger.WriteDebug("server list will be filtered with: %v", srvfilters)
	list := filterServers(srvfilters, asl)
	writeJSONResponse(w, list)
}
예제 #8
0
// filterServers takes the server filters and the last retrieved server list and
// returns a new, filtered server list based on the matched filters.
func filterServers(sqf []slQueryFilter,
	a *models.APIServerList) *models.APIServerList {
	if a == nil {
		return models.GetDefaultServerList()
	}
	filtered := make([]models.APIServer, len(a.Servers))
	copy(filtered, a.Servers)

	for _, s := range sqf {
		filtered = findMatches(s, filtered)
	}
	if filtered == nil {
		// JSON empty array instead of null
		filtered = make([]models.APIServer, 0)
	}
	return &models.APIServerList{
		RetrievedAt:        a.RetrievedAt,
		RetrievedTimeStamp: a.RetrievedTimeStamp,
		Servers:            filtered,
		ServerCount:        len(filtered),
		FailedCount:        0,
		FailedServers:      make([]string, 0),
	}
}