diff --git a/cdn/phoneicons/Icon_Bell.png b/cdn/phoneicons/Icon_Bell.png new file mode 100644 index 0000000..bbc0cf5 Binary files /dev/null and b/cdn/phoneicons/Icon_Bell.png differ diff --git a/cdn/phoneicons/Icon_Boost.png b/cdn/phoneicons/Icon_Boost.png new file mode 100644 index 0000000..8c51e5d Binary files /dev/null and b/cdn/phoneicons/Icon_Boost.png differ diff --git a/cdn/phoneicons/Icon_Boost_Disabled.png b/cdn/phoneicons/Icon_Boost_Disabled.png new file mode 100644 index 0000000..2e44efb Binary files /dev/null and b/cdn/phoneicons/Icon_Boost_Disabled.png differ diff --git a/cdn/phoneicons/Icon_Charge_Boost.png b/cdn/phoneicons/Icon_Charge_Boost.png new file mode 100644 index 0000000..13b536b Binary files /dev/null and b/cdn/phoneicons/Icon_Charge_Boost.png differ diff --git a/cdn/phoneicons/Icon_Charge_Boost_Disabled.png b/cdn/phoneicons/Icon_Charge_Boost_Disabled.png new file mode 100644 index 0000000..499ed2f Binary files /dev/null and b/cdn/phoneicons/Icon_Charge_Boost_Disabled.png differ diff --git a/cdn/phoneicons/Icon_Elbow.png b/cdn/phoneicons/Icon_Elbow.png new file mode 100644 index 0000000..0ea60d6 Binary files /dev/null and b/cdn/phoneicons/Icon_Elbow.png differ diff --git a/cdn/phoneicons/Icon_Fence.png b/cdn/phoneicons/Icon_Fence.png new file mode 100644 index 0000000..af9698e Binary files /dev/null and b/cdn/phoneicons/Icon_Fence.png differ diff --git a/cdn/phoneicons/Icon_RideOn.png b/cdn/phoneicons/Icon_RideOn.png new file mode 100644 index 0000000..0aefbcb Binary files /dev/null and b/cdn/phoneicons/Icon_RideOn.png differ diff --git a/cdn/phoneicons/Icon_Steering_Center.png b/cdn/phoneicons/Icon_Steering_Center.png new file mode 100644 index 0000000..0274446 Binary files /dev/null and b/cdn/phoneicons/Icon_Steering_Center.png differ diff --git a/cdn/phoneicons/Icon_Trash_Aero.png b/cdn/phoneicons/Icon_Trash_Aero.png new file mode 100644 index 0000000..d785456 Binary files /dev/null and b/cdn/phoneicons/Icon_Trash_Aero.png differ diff --git a/cdn/phoneicons/Icon_Trash_Anvil.png b/cdn/phoneicons/Icon_Trash_Anvil.png new file mode 100644 index 0000000..4852e51 Binary files /dev/null and b/cdn/phoneicons/Icon_Trash_Anvil.png differ diff --git a/cdn/phoneicons/Icon_Trash_Burrito.png b/cdn/phoneicons/Icon_Trash_Burrito.png new file mode 100644 index 0000000..3709e87 Binary files /dev/null and b/cdn/phoneicons/Icon_Trash_Burrito.png differ diff --git a/cdn/phoneicons/Icon_Trash_Feather.png b/cdn/phoneicons/Icon_Trash_Feather.png new file mode 100644 index 0000000..67f3d4e Binary files /dev/null and b/cdn/phoneicons/Icon_Trash_Feather.png differ diff --git a/cdn/phoneicons/Icon_Trash_Invisibility.png b/cdn/phoneicons/Icon_Trash_Invisibility.png new file mode 100644 index 0000000..085a6bf Binary files /dev/null and b/cdn/phoneicons/Icon_Trash_Invisibility.png differ diff --git a/cdn/phoneicons/Icon_Trash_Steamroller.png b/cdn/phoneicons/Icon_Trash_Steamroller.png new file mode 100644 index 0000000..b047747 Binary files /dev/null and b/cdn/phoneicons/Icon_Trash_Steamroller.png differ diff --git a/cdn/phoneicons/Icon_Trash_Van.png b/cdn/phoneicons/Icon_Trash_Van.png new file mode 100644 index 0000000..e302fa8 Binary files /dev/null and b/cdn/phoneicons/Icon_Trash_Van.png differ diff --git a/cdn/phoneicons/Icon_Turn_Arrow_Left.png b/cdn/phoneicons/Icon_Turn_Arrow_Left.png new file mode 100644 index 0000000..af6f63e Binary files /dev/null and b/cdn/phoneicons/Icon_Turn_Arrow_Left.png differ diff --git a/cdn/phoneicons/Icon_Turn_Arrow_Right.png b/cdn/phoneicons/Icon_Turn_Arrow_Right.png new file mode 100644 index 0000000..ceb966f Binary files /dev/null and b/cdn/phoneicons/Icon_Turn_Arrow_Right.png differ diff --git a/cdn/phoneicons/Icon_Turn_Arrow_Up.png b/cdn/phoneicons/Icon_Turn_Arrow_Up.png new file mode 100644 index 0000000..fda916a Binary files /dev/null and b/cdn/phoneicons/Icon_Turn_Arrow_Up.png differ diff --git a/cdn/phoneicons/Icon_Wave.png b/cdn/phoneicons/Icon_Wave.png new file mode 100644 index 0000000..22e2e77 Binary files /dev/null and b/cdn/phoneicons/Icon_Wave.png differ diff --git a/cdn/phoneicons/Icon_Workout_Easier.png b/cdn/phoneicons/Icon_Workout_Easier.png new file mode 100644 index 0000000..3cf81c7 Binary files /dev/null and b/cdn/phoneicons/Icon_Workout_Easier.png differ diff --git a/cdn/phoneicons/Icon_Workout_FF.png b/cdn/phoneicons/Icon_Workout_FF.png new file mode 100644 index 0000000..42f39b4 Binary files /dev/null and b/cdn/phoneicons/Icon_Workout_FF.png differ diff --git a/cdn/phoneicons/Icon_Workout_Harder.png b/cdn/phoneicons/Icon_Workout_Harder.png new file mode 100644 index 0000000..66e8cc7 Binary files /dev/null and b/cdn/phoneicons/Icon_Workout_Harder.png differ diff --git a/cdn/phoneicons/Icon_Workout_Pause.png b/cdn/phoneicons/Icon_Workout_Pause.png new file mode 100644 index 0000000..8f14b1b Binary files /dev/null and b/cdn/phoneicons/Icon_Workout_Pause.png differ diff --git a/cdn/phoneicons/Icon_Workout_Play.png b/cdn/phoneicons/Icon_Workout_Play.png new file mode 100644 index 0000000..9ee9d09 Binary files /dev/null and b/cdn/phoneicons/Icon_Workout_Play.png differ diff --git a/ssl/Program.cs b/ssl/Program.cs new file mode 100644 index 0000000..03f850e --- /dev/null +++ b/ssl/Program.cs @@ -0,0 +1,213 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using System.Net.Http.Json; +using System.Net.Sockets; +using System.Threading; +using System.Security.Cryptography.X509Certificates; +using System.Net.Security; +using System.Security.Authentication; +using System.IO; + +namespace ConsoleApp1 { + public class SslMitm { + static X509Certificate serverCertificate = new X509Certificate(@"cert-zwift-com.pfx", pwd_here); + public static void Start() { + void server() { + TcpListener listener = new TcpListener(IPAddress.Any, 443); + listener.Start(); + Console.WriteLine("SSL:443. Waiting for clients to connect..."); + while (true) { + TcpClient client = listener.AcceptTcpClient(); + new Thread(o => { ProcessClient(client); }).Start(); + } + void ProcessClient(TcpClient client) { + SslStream sslStream = new SslStream(client.GetStream(), false); + TcpClient fwdClient = new TcpClient("13.33.246.64", 443); + SslStream sslForwardStream = new SslStream(fwdClient.GetStream(), false, null, null); + try { + sslForwardStream.AuthenticateAsClient("us-or-rly101.zwift.com"); + } + catch (AuthenticationException e) + { + Console.WriteLine("ProcessClient Exception: {0}", e.Message); + if (e.InnerException != null) { + Console.WriteLine("Inner exception: {0}", e.InnerException.Message); + } + fwdClient.Close(); + return; + } + var stage = "?"; + try { + sslStream.AuthenticateAsServer(serverCertificate, clientCertificateRequired: false, checkCertificateRevocation: false); + sslStream.ReadTimeout = 1500; + sslForwardStream.ReadTimeout = 1500; + sslStream.WriteTimeout = 1500; + sslForwardStream.WriteTimeout = 1500; + + byte[] buffer = new byte[65536]; + while(client.Connected && fwdClient.Connected) { + int bytes = -1; + while (client.Connected && fwdClient.Connected) { + stage = "SR"; + bytes = sslStream.Read(buffer, 0, buffer.Length); + if (bytes > 0) { + stage = "FW"; + sslForwardStream.Write(buffer, 0, bytes); + using (var s = new FileStream(path_here + Thread.CurrentThread.ManagedThreadId.ToString(), FileMode.Append)) { + s.Write(buffer, 0, bytes); + } + } + if(bytes < buffer.Length) break; + } + if(bytes != -1) + sslForwardStream.Flush(); + bytes = -1; + while (client.Connected && fwdClient.Connected) { + stage = "FR"; + bytes = sslForwardStream.Read(buffer, 0, buffer.Length); + if (bytes > 0) { + stage = "SW"; + sslStream.Write(buffer, 0, bytes); + using (var s = new FileStream(path_here + Thread.CurrentThread.ManagedThreadId.ToString(), FileMode.Append)) { + s.Write(buffer, 0, bytes); + } + } + if (bytes < buffer.Length) break; + } + if (bytes != -1) + sslStream.Flush(); + } + } catch (Exception) { + Console.WriteLine("SSL Client Exception: {0}", stage); + try { sslForwardStream.Flush(); } catch (Exception) { } + try { sslStream.Flush();} catch (Exception) { } + try { sslStream.Close();} catch (Exception) { } + try { client.Close();} catch (Exception) { } + try { sslForwardStream.Close();} catch (Exception) { } + try { fwdClient.Close();} catch (Exception) { } + return; + } finally { + try { sslStream.Close(); } catch (Exception) { } + try { client.Close(); } catch (Exception) { } + try { sslForwardStream.Close(); } catch (Exception) { } + try { fwdClient.Close(); } catch (Exception) { } + } + } + } + new Thread(server).Start(); + } + } + public class Bearer { + public string access_token { get; set; } + } + public class User { + public int id { get; set; } = your_id_here; + } + public class MobileEnv { + public int appBuild { get; set; } + public string appDisplayName { get; set; } + public string appVersion { get; set; } + public string systemHardware { get; set; } + public string systemOS { get; set; } + public string systemOSVersion { get; set; } + } + public class PhoneInfo { + public static PhoneInfo Create() { + return new PhoneInfo() { + mobileEnvironment = new MobileEnv { appBuild = 1276, appDisplayName = "Companion", appVersion = "3.29.0", systemHardware = "samsung SM-G965N", systemOS = "Android", systemOSVersion = "7.1.2 (API 25)" }, + phoneAddress = GetLocalIPAddress(), + port = 21587, + protocol = "TCP" + }; + } + public string phoneAddress { get; set; } + public string protocol { get; set; } + public MobileEnv mobileEnvironment { get; set; } + public int port { get; set; } + public static string GetLocalIPAddress() { + var host = Dns.GetHostEntry(Dns.GetHostName()); + foreach (var ip in host.AddressList) + if (ip.AddressFamily == AddressFamily.InterNetwork) + return ip.ToString(); + throw new Exception("No network adapters with an IPv4 address in the system!"); + } + } + internal class Program { + static void Main(string[] args) { + //SslMitm.Start(); return; + + Bearer bearer = Authorize(); + var me = new User(); + var pi = PhoneInfo.Create(); + using (var httpClient = NewAuthorizedHttpClient(bearer)) { + me = httpClient.GetFromJsonAsync("api/profiles/me").Result; + httpClient.PutAsJsonAsync("relay/profiles/me/phone", pi).Wait(); + //pi = httpClient.GetFromJsonAsync("relay/profiles/me/phone").Result; //почему-то тут NULL + //Console.WriteLine($"phoneAddress: {pi.phoneAddress}:{pi.port}"); + } + TcpListener server = new TcpListener(IPAddress.Any, pi.port); + Console.WriteLine($"Listening: {pi.phoneAddress}:{pi.port}"); + server.Start(); + //byte cnt = 0; + while (true) { + TcpClient client = server.AcceptTcpClient(); + NetworkStream ns = client.GetStream(); + Console.WriteLine($"New client: {client.Client.RemoteEndPoint.ToString()}"); + while (client.Connected) { + int msgSize = ((int)ns.ReadByte() << 24) + ((int)ns.ReadByte() << 16) + ((int)ns.ReadByte() << 8) + ns.ReadByte(); + if (msgSize > 0) { + Console.WriteLine($"Msg size: {msgSize}"); + byte[] payload = new byte[msgSize]; + int r = ns.Read(payload, 0, msgSize); + if (r != msgSize) + Console.WriteLine($"Wrond read: {r}"); + else + Console.WriteLine(BitConverter.ToString(payload)); + //ns.Write(new byte[] { 0, 0, 0, 13, 0x08, 0xAD, 0xfb, 0x89, 0x02, 0x12, 0x02, 0x50, 0x01 }, 0, 13); + //ns.Write(new byte[] { 0, 0, 0, 14, 0x08, 0x01, 0x12, 0x06, 0x08, cnt++, 0x10, 0x01, 0x50, 0x01 }, 0, 14); + //ns.Flush(); + } + } + } + HttpClient NewHttpClient(string baseAddr) { return new HttpClient(NewClientHandler()) { BaseAddress = new Uri(baseAddr) }; } + HttpClientHandler NewClientHandler() { return new HttpClientHandler() { + //Proxy = new WebProxy("127.0.0.1:8888", false) + }; } + HttpClient NewAuthorizedHttpClient(Bearer auth) { + var ret = NewHttpClient("https://us-or-rly101.zwift.com/"); + ret.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", auth.access_token); + ret.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json"); + ret.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "application/json"); + ret.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Encoding", "gzip"); + ret.DefaultRequestHeaders.TryAddWithoutValidation("Zwift-Api-Version", "2.6"); + ret.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "com.zwift.android.prod/3.29.0-1276 (samsung SM-G965N/Android 7.1.2)");// loggedInUserId=" + me.id.ToString()); + ret.DefaultRequestHeaders.Connection.Add("keep-alive"); + ret.DefaultRequestHeaders.ExpectContinue = false; + return ret; + } + Bearer Authorize() { + while(true) try { + using (var httpClient = NewHttpClient("https://secure.zwift.com/")) { + var pwdContent = new FormUrlEncodedContent(new[] { + new KeyValuePair("client_id", "Zwift_Mobile_Link"), + new KeyValuePair("username", your_login), + new KeyValuePair("password", your_pwd), + new KeyValuePair("grant_type", "password") + }); + using (var tokeInfo = httpClient.PostAsync("auth/realms/zwift/protocol/openid-connect/token", pwdContent).Result) { + tokeInfo.EnsureSuccessStatusCode(); + return tokeInfo.Content.ReadFromJsonAsync().Result; + } + } + } catch (Exception ex) { + Console.WriteLine("Connection failed: {0} {1}. Repeat...", ex.GetType().Name, (ex.InnerException != null) ? ex.InnerException.GetType().Name : ""); + } + } + } + } +} diff --git a/standalone.py b/standalone.py index 450cb21..8960889 100755 --- a/standalone.py +++ b/standalone.py @@ -85,8 +85,8 @@ def save_ghost(name, player_id): try: if not os.path.isdir(folder): os.makedirs(folder) - except: - return + except Exception as exc: + print('save_ghost: %s' % repr(exc)) f = '%s/%s-%s.bin' % (folder, zwift_offline.get_utc_date_time().strftime("%Y-%m-%d-%H-%M-%S"), name) with open(f, 'wb') as fd: fd.write(ghosts.rec.SerializeToString()) @@ -108,8 +108,8 @@ def organize_ghosts(player_id): try: if not os.path.isdir(dest): os.makedirs(dest) - except: - return + except Exception as exc: + print('organize_ghosts: %s' % repr(exc)) os.rename(file, os.path.join(dest, f)) def load_ghosts(player_id, state, ghosts): @@ -218,9 +218,10 @@ class CDNHandler(SimpleHTTPRequestHandler): url = 'http://{}{}'.format(hostname, self.path) req_header = self.parse_headers() resp = requests.get(url, headers=merge_two_dicts(req_header, set_header()), verify=False) - except: - self.send_error(404, 'error trying to proxy') - return + except Exception as exc: + print('Error trying to proxy: %s' % repr(exc)) + self.send_error(404, 'error trying to proxy') + return self.send_response(resp.status_code) self.send_resp_headers(resp) self.wfile.write(resp.content) @@ -261,8 +262,8 @@ class TCPHandler(socketserver.BaseRequestHandler): hello = tcp_node_msgs_pb2.TCPHello() try: hello.ParseFromString(self.data[4:-4]) #2 bytes: length, 2 bytes: unrecognised; 4 bytes: CRC? - except: - print('TCPHandler ParseFromString exception') + except Exception as exc: + print('TCPHandler ParseFromString exception: %s' % repr(exc)) return # send packet containing UDP server (127.0.0.1) # (very little investigation done into this packet while creating @@ -321,6 +322,8 @@ class TCPHandler(socketserver.BaseRequestHandler): #Check every 5 seconds for new updates tcpthreadevent.wait(timeout=5) try: + t = int(zwift_offline.get_utc_time()) + #if ZC need to be registered if player_id in zwift_offline.zc_connect_queue: # and player_id in online: zc_params = tcp_node_msgs_pb2.TCPCompanionConnect() @@ -330,6 +333,7 @@ class TCPHandler(socketserver.BaseRequestHandler): zc_params.zc_local_port = zwift_offline.zc_connect_queue[player_id][1] #21587 zc_params.kind = 2 #TCP zc_params_payload = zc_params.SerializeToString() + last_alive_check = t self.request.sendall(struct.pack('!h', len(zc_params_payload))) self.request.sendall(zc_params_payload) print("TCPHandler register_zc %d %s" % (player_id, zc_params_payload.hex())) @@ -362,8 +366,6 @@ class TCPHandler(socketserver.BaseRequestHandler): for player_update_proto in added_player_updates: player_update_queue[player_id].remove(player_update_proto) - t = int(zwift_offline.get_utc_time()) - #Check if any updates are added and should be sent to client, otherwise just keep alive every 25 seconds if len(message.updates) > 0: last_alive_check = t @@ -374,8 +376,8 @@ class TCPHandler(socketserver.BaseRequestHandler): last_alive_check = t self.request.sendall(struct.pack('!h', len(payload))) self.request.sendall(payload) - except: - print('TCPHandler loop exception') + except Exception as exc: + print('TCPHandler loop exception: %s' % repr(exc)) break class GhostsVariables: @@ -497,8 +499,8 @@ class UDPHandler(socketserver.BaseRequestHandler): try: #If no sensors connected, first byte must be skipped recv.ParseFromString(data[1:-4]) - except: - print('UDPHandler ParseFromString exception') + except Exception as exc: + print('UDPHandler ParseFromString exception: %s' % repr(exc)) return client_address = self.client_address diff --git a/zwift_offline.py b/zwift_offline.py index 45e6d9b..b0fd311 100644 --- a/zwift_offline.py +++ b/zwift_offline.py @@ -1448,6 +1448,7 @@ def api_profiles_activities_rideon(recieving_player_id): def api_empty_obj(): return '{}', 200 +@app.route('/api/profiles//campaigns/otm2020', methods=['GET']) @app.route('/api/profiles//followees', methods=['GET']) def api_profiles_followees(player_id): return '', 200