Skip to content

Commit

Permalink
cdn: re-add get_cdn_auth_token in CDNClient
Browse files Browse the repository at this point in the history
fixed get_manifest in area where cdn auth token required
  • Loading branch information
detiam committed Oct 22, 2024
1 parent 7467498 commit 42aec98
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 8 deletions.
12 changes: 12 additions & 0 deletions protobufs/steammessages_contentsystem.proto
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,23 @@ message CContentServerDirectory_GetManifestRequestCode_Response {
optional uint64 manifest_request_code = 1;
}

message CContentServerDirectory_GetCDNAuthToken_Request {
optional uint32 depot_id = 1;
optional string host_name = 2;
optional uint32 app_id = 3;
}

message CContentServerDirectory_GetCDNAuthToken_Response {
optional string token = 1;
optional uint32 expiration_time = 2;
}

service ContentServerDirectory {
option (service_description) = "Content Server and CDN directory";

rpc GetServersForSteamPipe (.CContentServerDirectory_GetServersForSteamPipe_Request) returns (.CContentServerDirectory_GetServersForSteamPipe_Response);
rpc GetDepotPatchInfo (.CContentServerDirectory_GetDepotPatchInfo_Request) returns (.CContentServerDirectory_GetDepotPatchInfo_Response);
rpc GetClientUpdateHosts (.CContentServerDirectory_GetClientUpdateHosts_Request) returns (.CContentServerDirectory_GetClientUpdateHosts_Response);
rpc GetManifestRequestCode (.CContentServerDirectory_GetManifestRequestCode_Request) returns (.CContentServerDirectory_GetManifestRequestCode_Response);
rpc GetCDNAuthToken (.CContentServerDirectory_GetCDNAuthToken_Request) returns (.CContentServerDirectory_GetCDNAuthToken_Response);
}
61 changes: 56 additions & 5 deletions steam/client/cdn.py
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,7 @@ def __init__(self, client):
self.cell_id = self.steam.cell_id

self.web = make_requests_session()
self.cdn_auth_tokens = {} #: CDN authentication token
self.depot_keys = {} #: depot decryption keys
self.manifests = {} #: CDNDepotManifest instances
self.app_depots = {} #: app depot info
Expand Down Expand Up @@ -526,6 +527,51 @@ def get_content_server(self, rotate=False):
self.servers.rotate(-1)
return self.servers[0]

def get_cdn_auth_token(self, app_id, depot_id, hostname):
"""Get CDN authentication token
:param app_id: app id
:type app_id: :class:`int`
:param depot_id: depot id
:type depot_id: :class:`int`
:param hostname: cdn hostname
:type hostname: :class:`str`
:return: CDN authentication token
:rtype: str
"""
def update_cdn_auth_tokens():
resp = self.steam.send_um_and_wait('ContentServerDirectory.GetCDNAuthToken#1', {
'app_id': app_id,
'depot_id': depot_id,
'host_name': hostname
}, timeout=10)

if resp is None or resp.header.eresult != EResult.OK:
if resp.header.eresult == EResult.Fail:
# no need authtoken?
pass
else:
raise SteamError(f"Failed to get CDNAuthToken for {app_id}, {depot_id}, {hostname}",
EResult.Timeout if resp is None else EResult(resp.header.eresult))

self.cdn_auth_tokens.update({app_id:{depot_id:{hostname: {
'eresult': resp.header.eresult,
'token': resp.body.token or '',
'expiration_time': resp.body.expiration_time or 0
}}}})

if app_id not in self.cdn_auth_tokens or \
depot_id not in self.cdn_auth_tokens[app_id] or \
hostname not in self.cdn_auth_tokens[app_id][depot_id]:
update_cdn_auth_tokens()
else:
if self.cdn_auth_tokens[app_id][depot_id][hostname]['eresult'] != EResult.OK:
pass
elif datetime.fromtimestamp(self.cdn_auth_tokens[app_id][depot_id][hostname]['expiration_time'] - 60) < datetime.now():
update_cdn_auth_tokens()

return self.cdn_auth_tokens[app_id][depot_id][hostname]['token']

def get_depot_key(self, app_id, depot_id):
"""Get depot key, which is needed to decrypt files
Expand All @@ -548,26 +594,31 @@ def get_depot_key(self, app_id, depot_id):

return self.depot_keys[depot_id]

def cdn_cmd(self, command, args):
def cdn_cmd(self, command, args, app_id=None, depot_id=None):
"""Run CDN command request
:param command: command name
:type command: str
:param args: args
:type args: str
:param args: app_id: (optional) required for CDN authentication token
:type args: int
:param args: depot_id: (optional) required for CDN authentication token
:type args: int
:returns: requests response
:rtype: :class:`requests.Response`
:raises SteamError: on error
"""
server = self.get_content_server()

while True:
url = "{}://{}:{}/{}/{}".format(
url = "{}://{}:{}/{}/{}{}".format(
'https' if server.https else 'http',
server.host,
server.port,
command,
args,
self.get_cdn_auth_token(app_id, depot_id, str(server.host))
)

try:
Expand Down Expand Up @@ -598,7 +649,7 @@ def get_chunk(self, app_id, depot_id, chunk_id):
:raises SteamError: error message
"""
if (depot_id, chunk_id) not in self._chunk_cache:
resp = self.cdn_cmd('depot', f'{depot_id}/chunk/{chunk_id}')
resp = self.cdn_cmd('depot', f'{depot_id}/chunk/{chunk_id}', app_id, depot_id)

data = symmetric_decrypt(resp.content, self.get_depot_key(app_id, depot_id))

Expand Down Expand Up @@ -684,9 +735,9 @@ def get_manifest(self, app_id, depot_id, manifest_gid, decrypt=True, manifest_re
"""
if (app_id, depot_id, manifest_gid) not in self.manifests:
if manifest_request_code:
resp = self.cdn_cmd('depot', f'{depot_id}/manifest/{manifest_gid}/5/{manifest_request_code}')
resp = self.cdn_cmd('depot', f'{depot_id}/manifest/{manifest_gid}/5/{manifest_request_code}', app_id, depot_id)
else:
resp = self.cdn_cmd('depot', f'{depot_id}/manifest/{manifest_gid}/5')
resp = self.cdn_cmd('depot', f'{depot_id}/manifest/{manifest_gid}/5', app_id, depot_id)

if resp.ok:
manifest = self.DepotManifestClass(self, app_id, resp.content)
Expand Down
117 changes: 114 additions & 3 deletions steam/protobufs/steammessages_contentsystem_pb2.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 42aec98

Please sign in to comment.