Example #1
0
// OpenServerDB Opens a database connection to the server database file or if
// that file does not exists, creates it and then opens a database connection to it.
func OpenServerDB() (*SDB, error) {
	if err := verifyServerDbPath(); err != nil {
		// will panic if not verified
		return nil, logger.LogAppError(err)
	}
	conn, err := sql.Open("sqlite3", constants.GetServerDBPath())
	if err != nil {
		return nil, logger.LogAppError(err)
	}
	return &SDB{db: conn}, nil
}
Example #2
0
func retrieve(filter filters.Filter) (*models.APIServerList, error) {
	var mq MasterQuery
	var err error
	if config.Config.SteamConfig.UseWebServerList {
		mq, err = NewMasterWebQuery(filter)
	} else {
		mq, err = NewMasterQuery(filter)
	}

	if err != nil {
		return nil, logger.LogSteamErrorf("Master server error: %s", err)
	}

	if filter.Game.IgnoreInfo && filter.Game.IgnorePlayers && filter.Game.IgnoreRules {
		return nil, logger.LogAppErrorf("Cannot ignore all three AS2 requests!")
	}

	data := a2sData{}
	hg := make(map[string]filters.Game, len(mq.Servers))
	for _, h := range mq.Servers {
		hg[h] = filter.Game
	}
	data.HostsGames = hg

	// Order of retrieval is by amount of work that must be done (generally 1, 2, 3)
	// 1. rules (request chal #, recv chal #, req rules, recv rules)
	// games with multi-packet A2S_RULES replies do the most work; otherwise 1 = 2, 3
	// 2. players (request chal #, recv chal #, req players, recv players)
	// 3. info: just request info & receive info
	// Note: some servers (i.e. new beta games) don't have all 3 of AS2_RULES/PLAYER/INFO
	if !filter.Game.IgnoreRules {
		data.Rules = batchRuleQuery(mq.Servers)
	}
	if !filter.Game.IgnorePlayers {
		data.Players = batchPlayerQuery(mq.Servers)
	}
	if !filter.Game.IgnoreInfo {
		data.Info = batchInfoQuery(mq.Servers)
	}

	serverlist, err := buildServerList(data, true)
	if err != nil {
		return nil, logger.LogAppError(err)
	}

	if config.Config.DebugConfig.EnableServerDump {
		if err := dumpServersToDisk(filter.Game.Name, serverlist); err != nil {
			logger.LogAppError(err)
		}
	}

	return serverlist, nil
}
Example #3
0
// 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
}
Example #4
0
func verifyServerDbPath() error {
	if err := util.CreateDirectory(constants.DbDirectory); err != nil {
		logger.LogAppError(err)
		panic(fmt.Sprintf("Unable to create database directory %s: %s",
			constants.DbDirectory, err))
	}
	if err := createServerDBtable(constants.GetServerDBPath()); err != nil {
		logger.LogAppErrorf("Unable to verify database path: %s", err)
		panic("Unable to verify database path")
	}

	return nil
}
Example #5
0
// 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
}
Example #6
0
// OpenCountryDB opens the country lookup database for reading. The caller of
// this function will be responsinble for calling .Close().
func OpenCountryDB() (*CDB, error) {
	// Note: the caller of this function needs to handle .Close()
	conn, err := maxminddb.Open(constants.CountryDbFilePath)
	if err != nil {
		dir := "build/nix"
		if runtime.GOOS == "windows" {
			dir = "build\\win"
		}
		logger.LogAppError(err)
		panic(
			fmt.Sprintf(
				`Unable to open country database! Use the get_countrydb script in the %s
directory to get the country DB file, or download it from:
http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.mmdb.gz and
extract the "GeoLite2-City.mmdb" file into a directory called "db" in the same
directory as the a2sapi executable. Error: %s`, dir, err))
	}
	return &CDB{db: conn}, nil
}
Example #7
0
// Start listening for and responding to HTTP requests via the web server. Panics
// if unable to start.
func Start(runSilent bool) {
	r := newRouter()

	if !runSilent {
		printStartInfo()
	}

	logger.LogAppInfo("Starting HTTP server on port %d",
		config.Config.WebConfig.APIWebPort)

	srv := http.Server{
		Addr:           fmt.Sprintf(":%d", config.Config.WebConfig.APIWebPort),
		Handler:        r,
		ReadTimeout:    30 * time.Second,
		WriteTimeout:   30 * time.Second,
		MaxHeaderBytes: 1 << 20}

	err := srv.ListenAndServe()
	if err != nil {
		logger.LogAppError(err)
		panic(fmt.Sprintf("Unable to start HTTP server, error: %s\n", err))
	}
}