Plugin Directory

Changeset 3799570


Ignore:
Timestamp:
11/13/2026 02:50:05 AM (less than one hour ago)
Author:
tlloancy
Message:

Version 1.1.0

Location:
tlloancy-turn-relay
Files:
2 added
16 edited
1 copied

Legend:

Unmodified
Added
Removed
  • tlloancy-turn-relay/tags/1.1.0/assets/css/admin.css

    r3798744 r3799570  
    2121        max-width: 640px;
    2222}
     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  
    203203        output.appendChild(block);
    204204      }
     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      }
    205221    }).catch(function () {
    206222      output.textContent = 'The check did not complete.';
  • tlloancy-turn-relay/tags/1.1.0/docs/MODE-DEMPLOI.md

    r3798744 r3799570  
    1818## Produire un identifiant d’essai
    1919
    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
     22Une 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.
    2123
    2224**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  
    1818## Issue a test credential
    1919
    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
     22A 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.
    2123
    2224**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  
    4949                );
    5050                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(
    5158                        'tlloancy-turn-relay-admin',
    5259                        TLLOANCY_TURN_RELAY_URL . 'assets/js/admin.js',
    53                         array(),
     60                        array( 'tlloancy-turn-relay-qr' ),
    5461                        TLLOANCY_TURN_RELAY_VERSION,
    5562                        true
    … …  
    250257
    251258                        <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>
    252260                        <p>
    253261                                <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  
    1818                add_action( 'rest_api_init', array( __CLASS__, 'routes' ) );
    1919                add_action( 'parse_request', array( __CLASS__, 'well_known' ) );
     20                add_action( 'template_redirect', array( __CLASS__, 'phone_test' ) );
    2021        }
    2122
    … …  
    321322                        );
    322323                }
     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                );
    323350                return array(
    324351                        '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' ),
    326353                        'username'   => $turn['username'],
    327354                        '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>
     410body{margin:0;padding:24px;background:#fff;color:#1d2327;font:16px/1.4 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}
     411h1{font-size:28px;margin:0 0 12px}
     412#result{font-size:28px;margin:16px 0}
     413table{border-collapse:collapse;width:100%;max-width:640px}
     414td,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
    329485        }
    330486
  • tlloancy-turn-relay/tags/1.1.0/readme.txt

    r3798744 r3799570  
    66Tested up to: 7.1
    77Requires PHP: 8.1
    8 Stable tag: 1.0
     8Stable tag: 1.1.0
    99License: GPLv2 or later
    1010License URI: https://www.gnu.org/licenses/gpl-2.0.html
    … …  
    6060It 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.
    6161
     62= How do I test the relay from another network? =
     63
     64Open **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
    6266= Can I list every WordPress site that has the plugin? =
    6367
    … …  
    7478The diagnostics buttons resolve your own TURN host and open a TCP connection to its TLS port. They run only when an administrator clicks them.
    7579
     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
    7682== 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.
    7786
    7887= 1.0 =
  • tlloancy-turn-relay/tags/1.1.0/tlloancy-turn-relay.php

    r3798744 r3799570  
    33 * Plugin Name:       tlloancy Turn Relay
    44 * Description:       Issues short-lived TURN credentials for a shared coturn server, and an optional card of backup STUN and TURN nodes.
    5  * Version:           1.0
     5 * Version:           1.1.0
    66 * Requires at least: 6.2
    77 * Requires PHP:      8.1
    … …  
    1919}
    2020
    21 define( 'TLLOANCY_TURN_RELAY_VERSION', '1.0' );
     21define( 'TLLOANCY_TURN_RELAY_VERSION', '1.1.0' );
    2222define( 'TLLOANCY_TURN_RELAY_FILE', __FILE__ );
    2323define( 'TLLOANCY_TURN_RELAY_DIR', plugin_dir_path( __FILE__ ) );
  • tlloancy-turn-relay/trunk/assets/css/admin.css

    r3798744 r3799570  
    2121        max-width: 640px;
    2222}
     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  
    203203        output.appendChild(block);
    204204      }
     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      }
    205221    }).catch(function () {
    206222      output.textContent = 'The check did not complete.';
  • tlloancy-turn-relay/trunk/docs/MODE-DEMPLOI.md

    r3798744 r3799570  
    1818## Produire un identifiant d’essai
    1919
    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
     22Une 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.
    2123
    2224**Resolve host** et **Open TLS port** ne partent que sur un clic.
  • tlloancy-turn-relay/trunk/docs/USER-GUIDE.md

    r3798744 r3799570  
    1818## Issue a test credential
    1919
    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
     22A 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.
    2123
    2224**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  
    4949                );
    5050                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(
    5158                        'tlloancy-turn-relay-admin',
    5259                        TLLOANCY_TURN_RELAY_URL . 'assets/js/admin.js',
    53                         array(),
     60                        array( 'tlloancy-turn-relay-qr' ),
    5461                        TLLOANCY_TURN_RELAY_VERSION,
    5562                        true
    … …  
    250257
    251258                        <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>
    252260                        <p>
    253261                                <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  
    1818                add_action( 'rest_api_init', array( __CLASS__, 'routes' ) );
    1919                add_action( 'parse_request', array( __CLASS__, 'well_known' ) );
     20                add_action( 'template_redirect', array( __CLASS__, 'phone_test' ) );
    2021        }
    2122
    … …  
    321322                        );
    322323                }
     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                );
    323350                return array(
    324351                        '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' ),
    326353                        'username'   => $turn['username'],
    327354                        '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>
     410body{margin:0;padding:24px;background:#fff;color:#1d2327;font:16px/1.4 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}
     411h1{font-size:28px;margin:0 0 12px}
     412#result{font-size:28px;margin:16px 0}
     413table{border-collapse:collapse;width:100%;max-width:640px}
     414td,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
    329485        }
    330486
  • tlloancy-turn-relay/trunk/readme.txt

    r3798744 r3799570  
    66Tested up to: 7.1
    77Requires PHP: 8.1
    8 Stable tag: 1.0
     8Stable tag: 1.1.0
    99License: GPLv2 or later
    1010License URI: https://www.gnu.org/licenses/gpl-2.0.html
    … …  
    6060It 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.
    6161
     62= How do I test the relay from another network? =
     63
     64Open **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
    6266= Can I list every WordPress site that has the plugin? =
    6367
    … …  
    7478The diagnostics buttons resolve your own TURN host and open a TCP connection to its TLS port. They run only when an administrator clicks them.
    7579
     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
    7682== 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.
    7786
    7887= 1.0 =
  • tlloancy-turn-relay/trunk/tlloancy-turn-relay.php

    r3798744 r3799570  
    33 * Plugin Name:       tlloancy Turn Relay
    44 * Description:       Issues short-lived TURN credentials for a shared coturn server, and an optional card of backup STUN and TURN nodes.
    5  * Version:           1.0
     5 * Version:           1.1.0
    66 * Requires at least: 6.2
    77 * Requires PHP:      8.1
    … …  
    1919}
    2020
    21 define( 'TLLOANCY_TURN_RELAY_VERSION', '1.0' );
     21define( 'TLLOANCY_TURN_RELAY_VERSION', '1.1.0' );
    2222define( 'TLLOANCY_TURN_RELAY_FILE', __FILE__ );
    2323define( 'TLLOANCY_TURN_RELAY_DIR', plugin_dir_path( __FILE__ ) );
Note: See TracChangeset for help on using the changeset viewer.