Changeset 3799570
- Timestamp:
- 11/13/2026 02:50:05 AM (less than one hour ago)
- Location:
- tlloancy-turn-relay
- Files:
-
- 2 added
- 16 edited
- 1 copied
-
tags/1.1.0 (copied) (copied from tlloancy-turn-relay/trunk )
-
tags/1.1.0/assets/css/admin.css (modified) (1 diff)
-
tags/1.1.0/assets/js/admin.js (modified) (1 diff)
-
tags/1.1.0/assets/js/qrcode.js (added)
-
tags/1.1.0/docs/MODE-DEMPLOI.md (modified) (1 diff)
-
tags/1.1.0/docs/USER-GUIDE.md (modified) (1 diff)
-
tags/1.1.0/includes/class-tlloancy-turn-relay-admin.php (modified) (2 diffs)
-
tags/1.1.0/includes/class-tlloancy-turn-relay-rest.php (modified) (2 diffs)
-
tags/1.1.0/readme.txt (modified) (3 diffs)
-
tags/1.1.0/tlloancy-turn-relay.php (modified) (2 diffs)
-
trunk/assets/css/admin.css (modified) (1 diff)
-
trunk/assets/js/admin.js (modified) (1 diff)
-
trunk/assets/js/qrcode.js (added)
-
trunk/docs/MODE-DEMPLOI.md (modified) (1 diff)
-
trunk/docs/USER-GUIDE.md (modified) (1 diff)
-
trunk/includes/class-tlloancy-turn-relay-admin.php (modified) (2 diffs)
-
trunk/includes/class-tlloancy-turn-relay-rest.php (modified) (2 diffs)
-
trunk/readme.txt (modified) (3 diffs)
-
trunk/tlloancy-turn-relay.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
tlloancy-turn-relay/tags/1.1.0/assets/css/admin.css
r3798744 r3799570 21 21 max-width: 640px; 22 22 } 23 24 .tltr-qr { 25 display: block; 26 width: 220px; 27 height: auto; 28 margin: 12px 0; 29 background: #fff; 30 image-rendering: pixelated; 31 } -
tlloancy-turn-relay/tags/1.1.0/assets/js/admin.js
r3798744 r3799570 203 203 output.appendChild(block); 204 204 } 205 if (data.test_url && window.qrcode) { 206 var qr = window.qrcode(0, 'M'); 207 qr.addData(data.test_url); 208 qr.make(); 209 var img = document.createElement('img'); 210 img.className = 'tltr-qr'; 211 img.alt = 'Phone test'; 212 img.src = qr.createDataURL(6); 213 output.appendChild(img); 214 var link = document.createElement('p'); 215 var anchor = document.createElement('a'); 216 anchor.href = data.test_url; 217 anchor.textContent = data.test_url; 218 link.appendChild(anchor); 219 output.appendChild(link); 220 } 205 221 }).catch(function () { 206 222 output.textContent = 'The check did not complete.'; -
tlloancy-turn-relay/tags/1.1.0/docs/MODE-DEMPLOI.md
r3798744 r3799570 18 18 ## Produire un identifiant d’essai 19 19 20 **Diagnostics → Issue a test credential** affiche un nom et un mot de passe dans la page. Ils expirent au bout de cinq minutes. Les copier dans un testeur TURN. La page n’ouvre pas de site extérieur et ne place pas le mot de passe dans une URL. 20 **Diagnostics → Issue a test credential** affiche un nom, un mot de passe et un QR code. Ils expirent au bout de cinq minutes. Scanner le code avec un téléphone en données mobiles. Le téléphone ouvre une page de ce site qui lance le test TURN avec cet identifiant. Le secret partagé n’est pas dans le lien. Le mot de passe non plus : la page le charge pendant ces cinq minutes. 21 22 Une ligne qui commence par `Relay` et montre l’adresse publique veut dire que le téléphone a atteint coturn. `Not reachable?` veut dire que ce téléphone n’a pas eu de réponse. `Authentication failed?` veut dire que le serveur a répondu et a refusé l’identifiant. 21 23 22 24 **Resolve host** et **Open TLS port** ne partent que sur un clic. -
tlloancy-turn-relay/tags/1.1.0/docs/USER-GUIDE.md
r3798744 r3799570 18 18 ## Issue a test credential 19 19 20 **Diagnostics → Issue a test credential** shows a username and a password in the page. They expire in five minutes. Copy them into a TURN tester yourself. The page does not open an outside tester and does not put the password in a URL. 20 **Diagnostics → Issue a test credential** shows a username, a password, and a QR code. They expire in five minutes. Scan the code with a phone on mobile data. The phone opens a page on this site that runs the TURN test with that credential. The shared secret is not in the link. The password is not in the link either: the page loads it for those five minutes. 21 22 A line that starts with `Relay` and shows the public address means the phone reached coturn. `Not reachable?` means this phone got no answer. `Authentication failed?` means the server answered and rejected the credential. 21 23 22 24 **Resolve host** and **Open TLS port** run only when you click them. -
tlloancy-turn-relay/tags/1.1.0/includes/class-tlloancy-turn-relay-admin.php
r3798744 r3799570 49 49 ); 50 50 wp_enqueue_script( 51 'tlloancy-turn-relay-qr', 52 TLLOANCY_TURN_RELAY_URL . 'assets/js/qrcode.js', 53 array(), 54 '1.4.4', 55 true 56 ); 57 wp_enqueue_script( 51 58 'tlloancy-turn-relay-admin', 52 59 TLLOANCY_TURN_RELAY_URL . 'assets/js/admin.js', 53 array( ),60 array( 'tlloancy-turn-relay-qr' ), 54 61 TLLOANCY_TURN_RELAY_VERSION, 55 62 true … … 250 257 251 258 <h2><?php esc_html_e( 'Diagnostics', 'tlloancy-turn-relay' ); ?></h2> 259 <p class="description"><?php esc_html_e( 'Issue a test credential prints a QR code. Scan it from a phone that is not on this network. The link lasts five minutes. The shared secret is not in the link.', 'tlloancy-turn-relay' ); ?></p> 252 260 <p> 253 261 <button type="button" class="button" data-tltr-check="dns"><?php esc_html_e( 'Resolve host', 'tlloancy-turn-relay' ); ?></button> -
tlloancy-turn-relay/tags/1.1.0/includes/class-tlloancy-turn-relay-rest.php
r3798744 r3799570 18 18 add_action( 'rest_api_init', array( __CLASS__, 'routes' ) ); 19 19 add_action( 'parse_request', array( __CLASS__, 'well_known' ) ); 20 add_action( 'template_redirect', array( __CLASS__, 'phone_test' ) ); 20 21 } 21 22 … … 321 322 ); 322 323 } 324 $urls = isset( $turn['urls'] ) ? $turn['urls'] : array(); 325 if ( ! is_array( $urls ) ) { 326 $urls = array( $urls ); 327 } 328 $turn_urls = array(); 329 foreach ( $urls as $url ) { 330 if ( is_string( $url ) && str_starts_with( $url, 'turn' ) ) { 331 $turn_urls[] = $url; 332 } 333 } 334 if ( ! $turn_urls ) { 335 return array( 336 'ok' => false, 337 'message' => __( 'A host and a secret of at least 32 characters are required before a test credential can be issued.', 'tlloancy-turn-relay' ), 338 ); 339 } 340 $token = bin2hex( random_bytes( 16 ) ); 341 set_transient( 342 'tltr_ice_test_' . $token, 343 array( 344 'urls' => $turn_urls, 345 'username' => (string) $turn['username'], 346 'credential' => (string) $turn['credential'], 347 ), 348 300 349 ); 323 350 return array( 324 351 'ok' => true, 325 'message' => __( 'Test credential. It expires in five minutes. Copy it into your tester. It is not sent anywhere.', 'tlloancy-turn-relay' ),352 'message' => __( 'Test credential. It expires in five minutes. Scan the code from a phone that is not on this network.', 'tlloancy-turn-relay' ), 326 353 'username' => $turn['username'], 327 354 'credential' => $turn['credential'], 328 ); 355 'test_url' => add_query_arg( 'tlloancy_turn_relay_test', $token, home_url( '/' ) ), 356 ); 357 } 358 359 /** 360 * Public phone page for one five-minute test credential. 361 */ 362 public static function phone_test() { 363 if ( ! isset( $_GET['tlloancy_turn_relay_test'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The token is the access secret. 364 return; 365 } 366 nocache_headers(); 367 $token = sanitize_text_field( wp_unslash( $_GET['tlloancy_turn_relay_test'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended 368 $stored = null; 369 if ( preg_match( '/^[a-f0-9]{32}$/', $token ) ) { 370 $found = get_transient( 'tltr_ice_test_' . $token ); 371 if ( is_array( $found ) && ! empty( $found['urls'] ) && isset( $found['username'], $found['credential'] ) ) { 372 $stored = $found; 373 } 374 } 375 status_header( null === $stored ? 404 : 200 ); 376 header( 'Content-Type: text/html; charset=' . get_option( 'blog_charset' ) ); 377 header( 'X-Robots-Tag: noindex' ); 378 header( 'Referrer-Policy: no-referrer' ); 379 self::phone_test_document( $stored ); 380 exit; 381 } 382 383 /** 384 * HTML for the phone test. The credential lives in the page, not in the query string. 385 * 386 * @param array|null $stored Stored TURN server, or null when the link has expired. 387 */ 388 private static function phone_test_document( $stored ) { 389 $ice = null; 390 if ( is_array( $stored ) ) { 391 $ice = array( 392 'iceServers' => array( 393 array( 394 'urls' => array_values( $stored['urls'] ), 395 'username' => (string) $stored['username'], 396 'credential' => (string) $stored['credential'], 397 ), 398 ), 399 ); 400 } 401 ?> 402 <!DOCTYPE html> 403 <html <?php language_attributes(); ?>> 404 <head> 405 <meta charset="<?php echo esc_attr( get_option( 'blog_charset' ) ); ?>"> 406 <meta name="viewport" content="width=device-width, initial-scale=1"> 407 <meta name="robots" content="noindex"> 408 <title><?php esc_html_e( 'TURN test', 'tlloancy-turn-relay' ); ?></title> 409 <style> 410 body{margin:0;padding:24px;background:#fff;color:#1d2327;font:16px/1.4 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif} 411 h1{font-size:28px;margin:0 0 12px} 412 #result{font-size:28px;margin:16px 0} 413 table{border-collapse:collapse;width:100%;max-width:640px} 414 td,th{border-bottom:1px solid #c3c4c7;text-align:left;padding:6px 8px;font-size:14px} 415 #errors{white-space:pre-wrap} 416 </style> 417 </head> 418 <body> 419 <h1><?php esc_html_e( 'TURN test', 'tlloancy-turn-relay' ); ?></h1> 420 <?php if ( ! $ice ) : ?> 421 <p><?php esc_html_e( 'This test link has expired. Issue a new credential.', 'tlloancy-turn-relay' ); ?></p> 422 <?php else : ?> 423 <p><?php esc_html_e( 'Run this on mobile data, not on the same network as the relay.', 'tlloancy-turn-relay' ); ?></p> 424 <p id="result"><?php esc_html_e( 'Testing…', 'tlloancy-turn-relay' ); ?></p> 425 <table> 426 <thead><tr><th><?php esc_html_e( 'Type', 'tlloancy-turn-relay' ); ?></th><th><?php esc_html_e( 'Protocol', 'tlloancy-turn-relay' ); ?></th><th><?php esc_html_e( 'Address', 'tlloancy-turn-relay' ); ?></th><th><?php esc_html_e( 'Port', 'tlloancy-turn-relay' ); ?></th></tr></thead> 427 <tbody id="rows"></tbody> 428 </table> 429 <p id="errors"></p> 430 <script type="application/json" id="tltr-ice"><?php echo wp_json_encode( $ice ); ?></script> 431 <script> 432 (function () { 433 var cfg = JSON.parse(document.getElementById('tltr-ice').textContent); 434 var result = document.getElementById('result'); 435 var rows = document.getElementById('rows'); 436 var errors = document.getElementById('errors'); 437 var found = []; 438 var finished = false; 439 function finish() { 440 if (finished) return; 441 finished = true; 442 var relay = found.filter(function (c) { return c.type === 'relay'; })[0]; 443 var srflx = found.some(function (c) { return c.type === 'srflx'; }); 444 if (relay) { 445 result.textContent = 'Relay ' + (relay.address || ''); 446 } else if (srflx) { 447 result.textContent = 'Authentication failed?'; 448 } else { 449 result.textContent = 'Not reachable?'; 450 } 451 if (pc) pc.close(); 452 } 453 var pc = new RTCPeerConnection({ iceServers: cfg.iceServers, iceTransportPolicy: 'all' }); 454 pc.onicecandidate = function (event) { 455 if (!event.candidate || event.candidate.candidate === '') return; 456 var c = event.candidate; 457 found.push(c); 458 var tr = document.createElement('tr'); 459 [c.type, c.protocol, c.address, c.port].forEach(function (val) { 460 var td = document.createElement('td'); 461 td.textContent = val == null ? '' : String(val); 462 tr.appendChild(td); 463 }); 464 rows.appendChild(tr); 465 }; 466 pc.onicecandidateerror = function (event) { 467 errors.textContent += (event.url || '') + ' ' + (event.errorCode || '') + ' ' + (event.errorText || '') + '\n'; 468 }; 469 pc.onicegatheringstatechange = function () { 470 if (pc.iceGatheringState === 'complete') finish(); 471 }; 472 pc.createOffer({ offerToReceiveAudio: 1 }).then(function (desc) { 473 return pc.setLocalDescription(desc); 474 }).catch(function (err) { 475 errors.textContent = String(err); 476 finish(); 477 }); 478 setTimeout(finish, 20000); 479 })(); 480 </script> 481 <?php endif; ?> 482 </body> 483 </html> 484 <?php 329 485 } 330 486 -
tlloancy-turn-relay/tags/1.1.0/readme.txt
r3798744 r3799570 6 6 Tested up to: 7.1 7 7 Requires PHP: 8.1 8 Stable tag: 1. 08 Stable tag: 1.1.0 9 9 License: GPLv2 or later 10 10 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 60 60 It is not the shared secret. A shared card tells another site where to ask for credentials. That request must include the token in the `X-Tlloancy-Turn-Relay-Token` header. The HMAC secret never leaves this server. 61 61 62 = How do I test the relay from another network? = 63 64 Open **Settings → tlloancy Turn Relay**, click **Issue a test credential**, and scan the QR code with a phone on mobile data. The result `Relay` plus the public address means the phone reached coturn. 65 62 66 = Can I list every WordPress site that has the plugin? = 63 67 … … 74 78 The diagnostics buttons resolve your own TURN host and open a TCP connection to its TLS port. They run only when an administrator clicks them. 75 79 80 **Issue a test credential** also prints a QR code. The code opens a page on this site. That page runs the TURN test in the phone’s browser with a credential that expires in five minutes. The shared secret is not in the link. 81 76 82 == Changelog == 83 84 = 1.1.0 = 85 * A five-minute phone test: the diagnostics screen prints a QR code that opens the TURN check with the test credential already loaded. 77 86 78 87 = 1.0 = -
tlloancy-turn-relay/tags/1.1.0/tlloancy-turn-relay.php
r3798744 r3799570 3 3 * Plugin Name: tlloancy Turn Relay 4 4 * Description: Issues short-lived TURN credentials for a shared coturn server, and an optional card of backup STUN and TURN nodes. 5 * Version: 1. 05 * Version: 1.1.0 6 6 * Requires at least: 6.2 7 7 * Requires PHP: 8.1 … … 19 19 } 20 20 21 define( 'TLLOANCY_TURN_RELAY_VERSION', '1. 0' );21 define( 'TLLOANCY_TURN_RELAY_VERSION', '1.1.0' ); 22 22 define( 'TLLOANCY_TURN_RELAY_FILE', __FILE__ ); 23 23 define( 'TLLOANCY_TURN_RELAY_DIR', plugin_dir_path( __FILE__ ) ); -
tlloancy-turn-relay/trunk/assets/css/admin.css
r3798744 r3799570 21 21 max-width: 640px; 22 22 } 23 24 .tltr-qr { 25 display: block; 26 width: 220px; 27 height: auto; 28 margin: 12px 0; 29 background: #fff; 30 image-rendering: pixelated; 31 } -
tlloancy-turn-relay/trunk/assets/js/admin.js
r3798744 r3799570 203 203 output.appendChild(block); 204 204 } 205 if (data.test_url && window.qrcode) { 206 var qr = window.qrcode(0, 'M'); 207 qr.addData(data.test_url); 208 qr.make(); 209 var img = document.createElement('img'); 210 img.className = 'tltr-qr'; 211 img.alt = 'Phone test'; 212 img.src = qr.createDataURL(6); 213 output.appendChild(img); 214 var link = document.createElement('p'); 215 var anchor = document.createElement('a'); 216 anchor.href = data.test_url; 217 anchor.textContent = data.test_url; 218 link.appendChild(anchor); 219 output.appendChild(link); 220 } 205 221 }).catch(function () { 206 222 output.textContent = 'The check did not complete.'; -
tlloancy-turn-relay/trunk/docs/MODE-DEMPLOI.md
r3798744 r3799570 18 18 ## Produire un identifiant d’essai 19 19 20 **Diagnostics → Issue a test credential** affiche un nom et un mot de passe dans la page. Ils expirent au bout de cinq minutes. Les copier dans un testeur TURN. La page n’ouvre pas de site extérieur et ne place pas le mot de passe dans une URL. 20 **Diagnostics → Issue a test credential** affiche un nom, un mot de passe et un QR code. Ils expirent au bout de cinq minutes. Scanner le code avec un téléphone en données mobiles. Le téléphone ouvre une page de ce site qui lance le test TURN avec cet identifiant. Le secret partagé n’est pas dans le lien. Le mot de passe non plus : la page le charge pendant ces cinq minutes. 21 22 Une ligne qui commence par `Relay` et montre l’adresse publique veut dire que le téléphone a atteint coturn. `Not reachable?` veut dire que ce téléphone n’a pas eu de réponse. `Authentication failed?` veut dire que le serveur a répondu et a refusé l’identifiant. 21 23 22 24 **Resolve host** et **Open TLS port** ne partent que sur un clic. -
tlloancy-turn-relay/trunk/docs/USER-GUIDE.md
r3798744 r3799570 18 18 ## Issue a test credential 19 19 20 **Diagnostics → Issue a test credential** shows a username and a password in the page. They expire in five minutes. Copy them into a TURN tester yourself. The page does not open an outside tester and does not put the password in a URL. 20 **Diagnostics → Issue a test credential** shows a username, a password, and a QR code. They expire in five minutes. Scan the code with a phone on mobile data. The phone opens a page on this site that runs the TURN test with that credential. The shared secret is not in the link. The password is not in the link either: the page loads it for those five minutes. 21 22 A line that starts with `Relay` and shows the public address means the phone reached coturn. `Not reachable?` means this phone got no answer. `Authentication failed?` means the server answered and rejected the credential. 21 23 22 24 **Resolve host** and **Open TLS port** run only when you click them. -
tlloancy-turn-relay/trunk/includes/class-tlloancy-turn-relay-admin.php
r3798744 r3799570 49 49 ); 50 50 wp_enqueue_script( 51 'tlloancy-turn-relay-qr', 52 TLLOANCY_TURN_RELAY_URL . 'assets/js/qrcode.js', 53 array(), 54 '1.4.4', 55 true 56 ); 57 wp_enqueue_script( 51 58 'tlloancy-turn-relay-admin', 52 59 TLLOANCY_TURN_RELAY_URL . 'assets/js/admin.js', 53 array( ),60 array( 'tlloancy-turn-relay-qr' ), 54 61 TLLOANCY_TURN_RELAY_VERSION, 55 62 true … … 250 257 251 258 <h2><?php esc_html_e( 'Diagnostics', 'tlloancy-turn-relay' ); ?></h2> 259 <p class="description"><?php esc_html_e( 'Issue a test credential prints a QR code. Scan it from a phone that is not on this network. The link lasts five minutes. The shared secret is not in the link.', 'tlloancy-turn-relay' ); ?></p> 252 260 <p> 253 261 <button type="button" class="button" data-tltr-check="dns"><?php esc_html_e( 'Resolve host', 'tlloancy-turn-relay' ); ?></button> -
tlloancy-turn-relay/trunk/includes/class-tlloancy-turn-relay-rest.php
r3798744 r3799570 18 18 add_action( 'rest_api_init', array( __CLASS__, 'routes' ) ); 19 19 add_action( 'parse_request', array( __CLASS__, 'well_known' ) ); 20 add_action( 'template_redirect', array( __CLASS__, 'phone_test' ) ); 20 21 } 21 22 … … 321 322 ); 322 323 } 324 $urls = isset( $turn['urls'] ) ? $turn['urls'] : array(); 325 if ( ! is_array( $urls ) ) { 326 $urls = array( $urls ); 327 } 328 $turn_urls = array(); 329 foreach ( $urls as $url ) { 330 if ( is_string( $url ) && str_starts_with( $url, 'turn' ) ) { 331 $turn_urls[] = $url; 332 } 333 } 334 if ( ! $turn_urls ) { 335 return array( 336 'ok' => false, 337 'message' => __( 'A host and a secret of at least 32 characters are required before a test credential can be issued.', 'tlloancy-turn-relay' ), 338 ); 339 } 340 $token = bin2hex( random_bytes( 16 ) ); 341 set_transient( 342 'tltr_ice_test_' . $token, 343 array( 344 'urls' => $turn_urls, 345 'username' => (string) $turn['username'], 346 'credential' => (string) $turn['credential'], 347 ), 348 300 349 ); 323 350 return array( 324 351 'ok' => true, 325 'message' => __( 'Test credential. It expires in five minutes. Copy it into your tester. It is not sent anywhere.', 'tlloancy-turn-relay' ),352 'message' => __( 'Test credential. It expires in five minutes. Scan the code from a phone that is not on this network.', 'tlloancy-turn-relay' ), 326 353 'username' => $turn['username'], 327 354 'credential' => $turn['credential'], 328 ); 355 'test_url' => add_query_arg( 'tlloancy_turn_relay_test', $token, home_url( '/' ) ), 356 ); 357 } 358 359 /** 360 * Public phone page for one five-minute test credential. 361 */ 362 public static function phone_test() { 363 if ( ! isset( $_GET['tlloancy_turn_relay_test'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The token is the access secret. 364 return; 365 } 366 nocache_headers(); 367 $token = sanitize_text_field( wp_unslash( $_GET['tlloancy_turn_relay_test'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended 368 $stored = null; 369 if ( preg_match( '/^[a-f0-9]{32}$/', $token ) ) { 370 $found = get_transient( 'tltr_ice_test_' . $token ); 371 if ( is_array( $found ) && ! empty( $found['urls'] ) && isset( $found['username'], $found['credential'] ) ) { 372 $stored = $found; 373 } 374 } 375 status_header( null === $stored ? 404 : 200 ); 376 header( 'Content-Type: text/html; charset=' . get_option( 'blog_charset' ) ); 377 header( 'X-Robots-Tag: noindex' ); 378 header( 'Referrer-Policy: no-referrer' ); 379 self::phone_test_document( $stored ); 380 exit; 381 } 382 383 /** 384 * HTML for the phone test. The credential lives in the page, not in the query string. 385 * 386 * @param array|null $stored Stored TURN server, or null when the link has expired. 387 */ 388 private static function phone_test_document( $stored ) { 389 $ice = null; 390 if ( is_array( $stored ) ) { 391 $ice = array( 392 'iceServers' => array( 393 array( 394 'urls' => array_values( $stored['urls'] ), 395 'username' => (string) $stored['username'], 396 'credential' => (string) $stored['credential'], 397 ), 398 ), 399 ); 400 } 401 ?> 402 <!DOCTYPE html> 403 <html <?php language_attributes(); ?>> 404 <head> 405 <meta charset="<?php echo esc_attr( get_option( 'blog_charset' ) ); ?>"> 406 <meta name="viewport" content="width=device-width, initial-scale=1"> 407 <meta name="robots" content="noindex"> 408 <title><?php esc_html_e( 'TURN test', 'tlloancy-turn-relay' ); ?></title> 409 <style> 410 body{margin:0;padding:24px;background:#fff;color:#1d2327;font:16px/1.4 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif} 411 h1{font-size:28px;margin:0 0 12px} 412 #result{font-size:28px;margin:16px 0} 413 table{border-collapse:collapse;width:100%;max-width:640px} 414 td,th{border-bottom:1px solid #c3c4c7;text-align:left;padding:6px 8px;font-size:14px} 415 #errors{white-space:pre-wrap} 416 </style> 417 </head> 418 <body> 419 <h1><?php esc_html_e( 'TURN test', 'tlloancy-turn-relay' ); ?></h1> 420 <?php if ( ! $ice ) : ?> 421 <p><?php esc_html_e( 'This test link has expired. Issue a new credential.', 'tlloancy-turn-relay' ); ?></p> 422 <?php else : ?> 423 <p><?php esc_html_e( 'Run this on mobile data, not on the same network as the relay.', 'tlloancy-turn-relay' ); ?></p> 424 <p id="result"><?php esc_html_e( 'Testing…', 'tlloancy-turn-relay' ); ?></p> 425 <table> 426 <thead><tr><th><?php esc_html_e( 'Type', 'tlloancy-turn-relay' ); ?></th><th><?php esc_html_e( 'Protocol', 'tlloancy-turn-relay' ); ?></th><th><?php esc_html_e( 'Address', 'tlloancy-turn-relay' ); ?></th><th><?php esc_html_e( 'Port', 'tlloancy-turn-relay' ); ?></th></tr></thead> 427 <tbody id="rows"></tbody> 428 </table> 429 <p id="errors"></p> 430 <script type="application/json" id="tltr-ice"><?php echo wp_json_encode( $ice ); ?></script> 431 <script> 432 (function () { 433 var cfg = JSON.parse(document.getElementById('tltr-ice').textContent); 434 var result = document.getElementById('result'); 435 var rows = document.getElementById('rows'); 436 var errors = document.getElementById('errors'); 437 var found = []; 438 var finished = false; 439 function finish() { 440 if (finished) return; 441 finished = true; 442 var relay = found.filter(function (c) { return c.type === 'relay'; })[0]; 443 var srflx = found.some(function (c) { return c.type === 'srflx'; }); 444 if (relay) { 445 result.textContent = 'Relay ' + (relay.address || ''); 446 } else if (srflx) { 447 result.textContent = 'Authentication failed?'; 448 } else { 449 result.textContent = 'Not reachable?'; 450 } 451 if (pc) pc.close(); 452 } 453 var pc = new RTCPeerConnection({ iceServers: cfg.iceServers, iceTransportPolicy: 'all' }); 454 pc.onicecandidate = function (event) { 455 if (!event.candidate || event.candidate.candidate === '') return; 456 var c = event.candidate; 457 found.push(c); 458 var tr = document.createElement('tr'); 459 [c.type, c.protocol, c.address, c.port].forEach(function (val) { 460 var td = document.createElement('td'); 461 td.textContent = val == null ? '' : String(val); 462 tr.appendChild(td); 463 }); 464 rows.appendChild(tr); 465 }; 466 pc.onicecandidateerror = function (event) { 467 errors.textContent += (event.url || '') + ' ' + (event.errorCode || '') + ' ' + (event.errorText || '') + '\n'; 468 }; 469 pc.onicegatheringstatechange = function () { 470 if (pc.iceGatheringState === 'complete') finish(); 471 }; 472 pc.createOffer({ offerToReceiveAudio: 1 }).then(function (desc) { 473 return pc.setLocalDescription(desc); 474 }).catch(function (err) { 475 errors.textContent = String(err); 476 finish(); 477 }); 478 setTimeout(finish, 20000); 479 })(); 480 </script> 481 <?php endif; ?> 482 </body> 483 </html> 484 <?php 329 485 } 330 486 -
tlloancy-turn-relay/trunk/readme.txt
r3798744 r3799570 6 6 Tested up to: 7.1 7 7 Requires PHP: 8.1 8 Stable tag: 1. 08 Stable tag: 1.1.0 9 9 License: GPLv2 or later 10 10 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 60 60 It is not the shared secret. A shared card tells another site where to ask for credentials. That request must include the token in the `X-Tlloancy-Turn-Relay-Token` header. The HMAC secret never leaves this server. 61 61 62 = How do I test the relay from another network? = 63 64 Open **Settings → tlloancy Turn Relay**, click **Issue a test credential**, and scan the QR code with a phone on mobile data. The result `Relay` plus the public address means the phone reached coturn. 65 62 66 = Can I list every WordPress site that has the plugin? = 63 67 … … 74 78 The diagnostics buttons resolve your own TURN host and open a TCP connection to its TLS port. They run only when an administrator clicks them. 75 79 80 **Issue a test credential** also prints a QR code. The code opens a page on this site. That page runs the TURN test in the phone’s browser with a credential that expires in five minutes. The shared secret is not in the link. 81 76 82 == Changelog == 83 84 = 1.1.0 = 85 * A five-minute phone test: the diagnostics screen prints a QR code that opens the TURN check with the test credential already loaded. 77 86 78 87 = 1.0 = -
tlloancy-turn-relay/trunk/tlloancy-turn-relay.php
r3798744 r3799570 3 3 * Plugin Name: tlloancy Turn Relay 4 4 * Description: Issues short-lived TURN credentials for a shared coturn server, and an optional card of backup STUN and TURN nodes. 5 * Version: 1. 05 * Version: 1.1.0 6 6 * Requires at least: 6.2 7 7 * Requires PHP: 8.1 … … 19 19 } 20 20 21 define( 'TLLOANCY_TURN_RELAY_VERSION', '1. 0' );21 define( 'TLLOANCY_TURN_RELAY_VERSION', '1.1.0' ); 22 22 define( 'TLLOANCY_TURN_RELAY_FILE', __FILE__ ); 23 23 define( 'TLLOANCY_TURN_RELAY_DIR', plugin_dir_path( __FILE__ ) );
Note:
See TracChangeset
for help on using the changeset viewer.