Subversion Repositories oidinfo_api

Rev

Rev 13 | Rev 16 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. <?php
  2.  
  3. /*
  4.  * UUID utils for PHP
  5.  * Copyright 2011 - 2020 Daniel Marschall, ViaThinkSoft
  6.  * Version 2020-11-14
  7.  *
  8.  * Licensed under the Apache License, Version 2.0 (the "License");
  9.  * you may not use this file except in compliance with the License.
  10.  * You may obtain a copy of the License at
  11.  *
  12.  *     http://www.apache.org/licenses/LICENSE-2.0
  13.  *
  14.  * Unless required by applicable law or agreed to in writing, software
  15.  * distributed under the License is distributed on an "AS IS" BASIS,
  16.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17.  * See the License for the specific language governing permissions and
  18.  * limitations under the License.
  19.  */
  20.  
  21. # This library requires either the GMP extension (or BCMath if gmp_supplement.inc.php is present)
  22.  
  23. if (file_exists(__DIR__ . '/mac_utils.inc.phps')) include_once __DIR__ . '/mac_utils.inc.phps'; // optionally used for uuid_info()
  24. if (file_exists(__DIR__ . '/gmp_supplement.inc.php')) include_once __DIR__ . '/gmp_supplement.inc.php';
  25.  
  26. define('UUID_NAMEBASED_NS_DNS',     '6ba7b810-9dad-11d1-80b4-00c04fd430c8');
  27. define('UUID_NAMEBASED_NS_URL',     '6ba7b811-9dad-11d1-80b4-00c04fd430c8');
  28. define('UUID_NAMEBASED_NS_OID',     '6ba7b812-9dad-11d1-80b4-00c04fd430c8');
  29. define('UUID_NAMEBASED_NS_X500_DN', '6ba7b814-9dad-11d1-80b4-00c04fd430c8');
  30.  
  31. function uuid_valid($uuid) {
  32.         $uuid = str_replace(array('-', '{', '}'), '', $uuid);
  33.         $uuid = strtoupper($uuid);
  34.  
  35.         if (strlen($uuid) != 32) return false;
  36.  
  37.         $uuid = preg_replace('@[0-9A-F]@', '', $uuid);
  38.  
  39.         return ($uuid == '');
  40. }
  41.  
  42. # TODO: Don't echo
  43. function uuid_info($uuid) {
  44.         if (!uuid_valid($uuid)) return false;
  45.  
  46.         # $uuid = str_replace(array('-', '{', '}'), '', $uuid);
  47.         $uuid = strtoupper($uuid);
  48.         $uuid = preg_replace('@[^0-9A-F]@', '', $uuid);
  49.  
  50.         $x = hexdec(substr($uuid, 16, 1));
  51.              if ($x >= 14 /* 1110 */) $variant = 3;
  52.         else if ($x >= 12 /* 1100 */) $variant = 2;
  53.         else if ($x >=  8 /* 1000 */) $variant = 1;
  54.         else if ($x >=  0 /* 0000 */) $variant = 0;
  55.  
  56.  
  57.         switch ($variant) {
  58.                 case 0:
  59.                         echo sprintf("%-24s %s\n", "Variant:", "[0xx] NCS (reserved for backward compatibility)");
  60.  
  61.                         /*
  62.                          * Internal structure of variant #0 UUIDs
  63.                          *
  64.                          * The first 6 octets are the number of 4 usec units of time that have
  65.                          * passed since 1/1/80 0000 GMT.  The next 2 octets are reserved for
  66.                          * future use.  The next octet is an address family.  The next 7 octets
  67.                          * are a host ID in the form allowed by the specified address family.
  68.                          *
  69.                          * Note that while the family field (octet 8) was originally conceived
  70.                          * of as being able to hold values in the range [0..255], only [0..13]
  71.                          * were ever used.  Thus, the 2 MSB of this field are always 0 and are
  72.                          * used to distinguish old and current UUID forms.
  73.                          *
  74.                          * +--------------------------------------------------------------+
  75.                          * |                    high 32 bits of time                      |  0-3  .time_high
  76.                          * +-------------------------------+-------------------------------
  77.                          * |     low 16 bits of time       |  4-5               .time_low
  78.                          * +-------+-----------------------+
  79.                          * |         reserved              |  6-7               .reserved
  80.                          * +---------------+---------------+
  81.                          * |    family     |   8                                .family
  82.                          * +---------------+----------...-----+
  83.                          * |            node ID               |  9-16           .node
  84.                          * +--------------------------...-----+
  85.                          *
  86.                          */
  87.  
  88.                         // Example of an UUID: 333a2276-0000-0000-0d00-00809c000000
  89.  
  90.                         # TODO: See https://github.com/cjsv/uuid/blob/master/Doc
  91.  
  92.                         # Timestamp: Count of 4us intervals since 01 Jan 1980 00:00:00 GMT
  93.                         # 1/0,000004 = 250000
  94.                         # Seconds between 1970 and 1980 : 315532800
  95.                         # 250000*315532800=78883200000000
  96.                         $timestamp = substr($uuid, 0, 12);
  97.                         $ts = gmp_init($timestamp, 16);
  98.                         $ts = gmp_add($ts, gmp_init("78883200000000"));
  99.                         $ms = gmp_mod($ts, gmp_init("250000"));
  100.                         $ts = gmp_div($ts, gmp_init("250000"));
  101.                         $ts = gmp_strval($ts);
  102.                         $ms = gmp_strval($ms);
  103.                         $ts = gmdate('Y-m-d H:i:s', $ts)."'".str_pad($ms, 7, '0', STR_PAD_LEFT).' GMT';
  104.                         echo sprintf("%-24s %s\n", "Timestamp:", "[0x$timestamp] $ts");
  105.  
  106.                         $reserved = substr($uuid, 12, 4);
  107.                         echo sprintf("%-24s %s\n", "Reserved:", "0x$reserved");
  108.  
  109.                         # Family 13 (dds) looks like node is 00 | nnnnnn 000000.
  110.                         # Family 2 is presumably (ip).
  111.                         # Not sure if anything else was used.
  112.                         $family_hex = substr($uuid, 16, 2);
  113.                         $family_dec = hexdec($family_hex);
  114.                         if ($family_dec == 2) {
  115.                                 $family_ = 'IP';
  116.                         } else if ($family_dec == 13) {
  117.                                 $family_ = 'DDS (Data Link)';
  118.                         } else {
  119.                                 $family_ = "Unknown ($family_dec)"; # There are probably no more families
  120.                         }
  121.                         echo sprintf("%-24s %s\n", "Family:", "[0x$family_hex = $family_dec] $family_");
  122.  
  123.                         $nodeid = substr($uuid, 18, 14);
  124.                         echo sprintf("%-24s %s\n", "Node ID:", "0x$nodeid");
  125.                         # TODO: interprete node id (the family specifies it)
  126.  
  127.                         break;
  128.                 case 1:
  129.                         echo sprintf("%-24s %s\n", "Variant:", "[10x] RFC 4122 (Leach-Mealling-Salz)");
  130.  
  131.                         $version = hexdec(substr($uuid, 12, 1));
  132.                         switch ($version) {
  133.                                 case 1:
  134.                                         echo sprintf("%-24s %s\n", "Version:", "[1] Time-based with unique random host identifier");
  135.  
  136.                                         # Timestamp: Count of 100ns intervals since 15 Oct 1582 00:00:00
  137.                                         # 1/0,0000001 = 10000000
  138.                                         $timestamp = substr($uuid, 13, 3).substr($uuid, 8, 4).substr($uuid, 0, 8);
  139.                                         $ts = gmp_init($timestamp, 16);
  140.                                         $ts = gmp_sub($ts, gmp_init("122192928000000000"));
  141.                                         $ms = gmp_mod($ts, gmp_init("10000000"));
  142.                                         $ts = gmp_div($ts, gmp_init("10000000"));
  143.                                         $ts = gmp_strval($ts);
  144.                                         $ms = gmp_strval($ms);
  145.                                         $ts = gmdate('Y-m-d H:i:s', $ts)."'".str_pad($ms, 7, '0', STR_PAD_LEFT).' GMT';
  146.                                         echo sprintf("%-24s %s\n", "Timestamp:", "[0x$timestamp] $ts");
  147.  
  148.                                         $x = hexdec(substr($uuid, 16, 4));
  149.                                         $dec = $x & 0x3FFF; // The highest 2 bits are used by "variant" (10x)
  150.                                         $hex = substr($uuid, 16, 4);
  151.                                         echo sprintf("%-24s %s\n", "Clock ID:", "[0x$hex] $dec");
  152.  
  153.                                         $x = substr($uuid, 20, 12);
  154.                                         $nodeid = '';
  155.                                         for ($i=0; $i<6; $i++) {
  156.                                                 $nodeid .= substr($x, $i*2, 2);
  157.                                                 if ($i != 5) $nodeid .= ':';
  158.                                         }
  159.                                         echo sprintf("%-24s %s\n", "Node ID:", "$nodeid");
  160.  
  161.                                         if (function_exists('decode_mac')) {
  162.                                                 echo "\nIn case that this Node ID is a MAC address, here is the interpretation of that MAC address:\n";
  163.                                                 echo decode_mac($nodeid);
  164.                                         }
  165.  
  166.                                         break;
  167.                                 case 2:
  168.                                         echo sprintf("%-24s %s\n", "Version:", "[2] DCE Security version");
  169.  
  170.                                         # The time_low field (which represents an integer in the range [0, 232-1]) is interpreted as a local-ID; that is, an identifier (within the domain specified by clock_seq_low) meaningful to the local host. In the particular case of a POSIX host, when combined with a POSIX UID or POSIX GID domain in the clock_seq_low field (above), the time_low field represents a POSIX UID or POSIX GID, respectively.
  171.                                         $x = substr($uuid, 0, 8);
  172.                                         echo sprintf("%-24s %s\n", "Local ID:", "0x$x");
  173.  
  174.                                         # The clock_seq_low field (which represents an integer in the range [0, 28-1]) is interpreted as a local domain (as represented by sec_rgy_domain_t; see sec_rgy_domain_t ); that is, an identifier domain meaningful to the local host. (Note that the data type sec_rgy_domain_t can potentially hold values outside the range [0, 28-1]; however, the only values currently registered are in the range [0, 2], so this type mismatch is not significant.) In the particular case of a POSIX host, the value sec_rgy_domain_person is to be interpreted as the "POSIX UID domain", and the value sec_rgy_domain_group is to be interpreted as the "POSIX GID domain".
  175.                                         $x = substr($uuid, 18, 2);
  176.                                         if ($x == '00') $domain_info = 'POSIX: User-ID / Non-POSIX: site-defined';
  177.                                         else if ($x == '01') $domain_info = 'POSIX: Group-ID / Non-POSIX: site-defined';
  178.                                         else $domain_info = 'site-defined';
  179.                                         echo sprintf("%-24s %s\n", "Local Domain:", "0x$x ($domain_info)");
  180.  
  181.                                         # Timestamp: Count of 100ns intervals since 15 Oct 1582 00:00:00
  182.                                         # 1/0,0000001 = 10000000
  183.                                         $timestamp = substr($uuid, 13, 3).substr($uuid, 8, 4).'00000000';
  184.                                         $ts = gmp_init($timestamp, 16);
  185.                                         $ts = gmp_sub($ts, gmp_init("122192928000000000"));
  186.                                         $ms = gmp_mod($ts, gmp_init("10000000"));
  187.                                         $ts = gmp_div($ts, gmp_init("10000000"));
  188.                                         $ts = gmp_strval($ts);
  189.                                         $ms = gmp_strval($ms);
  190.                                         $ts_min = gmdate('Y-m-d H:i:s', $ts)."'".str_pad($ms, 7, '0', STR_PAD_LEFT).' GMT';
  191.  
  192.                                         $timestamp = substr($uuid, 13, 3).substr($uuid, 8, 4).'FFFFFFFF';
  193.                                         $ts = gmp_init($timestamp, 16);
  194.                                         $ts = gmp_sub($ts, gmp_init("122192928000000000"));
  195.                                         $ms = gmp_mod($ts, gmp_init("10000000"));
  196.                                         $ts = gmp_div($ts, gmp_init("10000000"));
  197.                                         $ts = gmp_strval($ts);
  198.                                         $ms = gmp_strval($ms);
  199.                                         $ts_max = gmdate('Y-m-d H:i:s', $ts)."'".str_pad($ms, 7, '0', STR_PAD_LEFT).' GMT';
  200.  
  201.                                         $timestamp = substr($uuid, 13, 3).substr($uuid, 8, 4).'xxxxxxxx';
  202.                                         echo sprintf("%-24s %s\n", "Timestamp:", "[0x$timestamp] $ts_min - $ts_max");
  203.  
  204.                                         $x = hexdec(substr($uuid, 16, 2).'00');
  205.                                         $dec_min = $x & 0x3FFF; // The highest 2 bits are used by "variant" (10x)
  206.                                         $x = hexdec(substr($uuid, 16, 2).'FF');
  207.                                         $dec_max = $x & 0x3FFF; // The highest 2 bits are used by "variant" (10x)
  208.                                         $hex = substr($uuid, 16, 2).'xx';
  209.                                         echo sprintf("%-24s %s\n", "Clock ID:", "[0x$hex] $dec_min - $dec_max");
  210.  
  211.                                         $x = substr($uuid, 20, 12);
  212.                                         $nodeid = '';
  213.                                         for ($i=0; $i<6; $i++) {
  214.                                                 $nodeid .= substr($x, $i*2, 2);
  215.                                                 if ($i != 5) $nodeid .= ':';
  216.                                         }
  217.                                         echo sprintf("%-24s %s\n", "Node ID:", "$nodeid");
  218.  
  219.                                         if (function_exists('decode_mac')) {
  220.                                                 echo "\nIn case that this Node ID is a MAC address, here is the interpretation of that MAC address:\n";
  221.                                                 echo decode_mac($nodeid);
  222.                                         }
  223.  
  224.                                         break;
  225.                                 case 3:
  226.                                         echo sprintf("%-24s %s\n", "Version:", "[3] Name-based (MD5 hash)");
  227.  
  228.                                         $hash = str_replace('-', '', strtolower($uuid));
  229.                                         $hash[12] = '?'; // was overwritten by version
  230.                                         $hash[16] = '?'; // was partially overwritten by variant
  231.  
  232.                                         echo sprintf("%-24s %s\n", "MD5(Namespace+Subject):", "$hash");
  233.  
  234.                                         break;
  235.                                 case 4:
  236.                                         echo sprintf("%-24s %s\n", "Version:", "[4] Random");
  237.  
  238.                                         $rand = '';
  239.                                         for ($i=0; $i<16; $i++) {
  240.                                                 $bin = base_convert(substr($uuid, $i*2, 2), 16, 2);
  241.                                                 $bin = str_pad($bin, 8, "0", STR_PAD_LEFT);
  242.  
  243.                                                 if ($i == 6) {
  244.                                                         $bin[0] = 'x';
  245.                                                         $bin[1] = 'x';
  246.                                                 } else if ($i == 8) {
  247.                                                         $bin[0] = 'x';
  248.                                                         $bin[1] = 'x';
  249.                                                         $bin[2] = 'x';
  250.                                                         $bin[3] = 'x';
  251.                                                 }
  252.  
  253.                                                 $rand .= "$bin ";
  254.                                         }
  255.  
  256.                                         echo sprintf("%-24s %s\n", "Random bits:", trim($rand));
  257.  
  258.                                         break;
  259.                                 case 5:
  260.                                         echo sprintf("%-24s %s\n", "Version:", "[5] Name-based (SHA-1 hash)");
  261.  
  262.                                         $hash = str_replace('-', '', strtolower($uuid));
  263.                                         $hash[12] = '?'; // was overwritten by version
  264.                                         $hash[16] = '?'; // was partially overwritten by variant
  265.                                         $hash .= '????????'; // was cut off
  266.  
  267.                                         echo sprintf("%-24s %s\n", "SHA1(Namespace+Subject):", "$hash");
  268.  
  269.  
  270.                                         break;
  271.                                 default:
  272.                                         echo sprintf("%-24s %s\n", "Version:", "[$version] Unknown");
  273.                                         break;
  274.                         }
  275.  
  276.                         break;
  277.                 case 2:
  278.                         echo sprintf("%-24s %s\n", "Variant:", "[110] Reserved for Microsoft Corporation");
  279.                         break;
  280.                 case 3:
  281.                         echo sprintf("%-24s %s\n", "Variant:", "[111] Reserved for future use");
  282.                         break;
  283.         }
  284. }
  285.  
  286. function uuid_canonize($uuid) {
  287.         if (!uuid_valid($uuid)) return false;
  288.         return oid_to_uuid(uuid_to_oid($uuid));
  289. }
  290.  
  291. function oid_to_uuid($oid) {
  292.         if (!is_uuid_oid($oid)) return false;
  293.  
  294.         if ($oid[0] == '.') {
  295.                 $oid = substr($oid, 1);
  296.         }
  297.         $ary = explode('.', $oid);
  298.         $val = $ary[2];
  299.  
  300.         $x = gmp_init($val, 10);
  301.         $y = gmp_strval($x, 16);
  302.         $y = str_pad($y, 32, "0", STR_PAD_LEFT);
  303.         return substr($y,  0, 8).'-'.
  304.                substr($y,  8, 4).'-'.
  305.                substr($y, 12, 4).'-'.
  306.                substr($y, 16, 4).'-'.
  307.                substr($y, 20, 12);
  308. }
  309.  
  310. function is_uuid_oid($oid, $only_allow_root=false) {
  311.         if ($oid[0] == '.') $oid = substr($oid, 1); // remove leading dot
  312.  
  313.         $ary = explode('.', $oid);
  314.  
  315.         if ($only_allow_root) {
  316.                 if (count($ary) != 3) return false;
  317.         }
  318.  
  319.         if ($ary[0] != '2') return false;
  320.         if ($ary[1] != '25') return false;
  321.         for ($i=2; $i<count($ary); $i++) {
  322.                 $v = $ary[$i];
  323.                 if (!is_numeric($v)) return false;
  324.                 if ($i == 2) {
  325.                         // Must be in the range of 128 bit UUID
  326.                         $test = gmp_init($v, 10);
  327.                         if (strlen(gmp_strval($test, 16)) > 32) return false;
  328.                 }
  329.                 if ($v < 0) return false;
  330.         }
  331.  
  332.         return true;
  333. }
  334.  
  335. function uuid_to_oid($uuid) {
  336.         if (!uuid_valid($uuid)) return false;
  337.  
  338.         $uuid = str_replace(array('-', '{', '}'), '', $uuid);
  339.         $x = gmp_init($uuid, 16);
  340.         return '2.25.'.gmp_strval($x, 10); # TODO: parameter with or without leading dot
  341. }
  342.  
  343. function gen_uuid($prefer_timebased = true) {
  344.         $uuid = $prefer_timebased ? gen_uuid_timebased() : false;
  345.         if ($uuid === false) $uuid = gen_uuid_random();
  346.         return $uuid;
  347. }
  348.  
  349. // Version 1 (Time based) UUID
  350. function gen_uuid_timebased() {
  351.         # On Debian: apt-get install php-uuid
  352.         # extension_loaded('uuid')
  353.         if (function_exists('uuid_create')) {
  354.                 # OSSP uuid extension like seen in php5-uuid at Debian 8
  355.                 /*
  356.                 $x = uuid_create($context);
  357.                 uuid_make($context, UUID_MAKE_V1);
  358.                 uuid_export($context, UUID_FMT_STR, $uuid);
  359.                 return trim($uuid);
  360.                 */
  361.  
  362.                 # PECL uuid extension like seen in php-uuid at Debian 9
  363.                 return trim(uuid_create(UUID_TYPE_TIME));
  364.         }
  365.  
  366.         # On Debian: apt-get install uuid-runtime
  367.         if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
  368.                 $out = array();
  369.                 $ec = -1;
  370.                 exec('uuidgen -t 2>/dev/null', $out, $ec);
  371.                 if ($ec == 0) return $out[0];
  372.         }
  373.  
  374.         # If we hadn't any success yet, then implement the time based generation routine ourselves!
  375.         # Based on https://github.com/fredriklindberg/class.uuid.php/blob/master/class.uuid.php
  376.  
  377.         $uuid = array(
  378.                 'time_low' => 0,                /* 32-bit */
  379.                 'time_mid' => 0,                /* 16-bit */
  380.                 'time_hi' => 0,                 /* 16-bit */
  381.                 'clock_seq_hi' => 0,            /*  8-bit */
  382.                 'clock_seq_low' => 0,           /*  8-bit */
  383.                 'node' => array()               /* 48-bit */
  384.         );
  385.  
  386.         /*
  387.          * Get current time in 100 ns intervals. The magic value
  388.          * is the offset between UNIX epoch and the UUID UTC
  389.          * time base October 15, 1582.
  390.          */
  391.         $tp = gettimeofday();
  392.         $time = ($tp['sec'] * 10000000) + ($tp['usec'] * 10) + 0x01B21DD213814000;
  393.  
  394.         $uuid['time_low'] = $time & 0xffffffff;
  395.         /* Work around PHP 32-bit bit-operation limits */
  396.         $high = intval($time / 0xffffffff);
  397.         $uuid['time_mid'] = $high & 0xffff;
  398.         $uuid['time_hi'] = (($high >> 16) & 0xfff) | (1/*TimeBased*/ << 12);
  399.  
  400.         /*
  401.          * We don't support saved state information and generate
  402.          * a random clock sequence each time.
  403.          */
  404.         $uuid['clock_seq_hi'] = 0x80 | mt_rand(0, 64);
  405.         $uuid['clock_seq_low'] = mt_rand(0, 255);
  406.  
  407.         /*
  408.          * Node should be set to the 48-bit IEEE node identifier
  409.          */
  410.         $mac = get_mac_address();
  411.         if ($mac) {
  412.                 $node = str_replace(':','',$mac);
  413.                 for ($i = 0; $i < 6; $i++) {
  414.                         $uuid['node'][$i] = hexdec(substr($node, $i*2, 2));
  415.                 }
  416.  
  417.                 /*
  418.                  * Now output the UUID
  419.                  */
  420.                 return sprintf(
  421.                         '%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x',
  422.                         ($uuid['time_low']), ($uuid['time_mid']), ($uuid['time_hi']),
  423.                         $uuid['clock_seq_hi'], $uuid['clock_seq_low'],
  424.                         $uuid['node'][0], $uuid['node'][1], $uuid['node'][2],
  425.                         $uuid['node'][3], $uuid['node'][4], $uuid['node'][5]);
  426.         }
  427.  
  428.         # We cannot generate the timebased UUID!
  429.         return false;
  430. }
  431.  
  432. function get_mac_address() {
  433.         static $detected_mac = false;
  434.  
  435.         if ($detected_mac !== false) { // false NOT null!
  436.                 return $detected_mac;
  437.         }
  438.  
  439.         // TODO: This should actually be part of mac_utils.inc.php, but we need it
  440.         //       here, and mac_utils.inc.php shall only be optional. What to do?
  441.         if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
  442.                 // Windows
  443.                 $cmds = array(
  444.                         "ipconfig /all", // faster
  445.                         "getmac"
  446.                 );
  447.                 foreach ($cmds as $cmd) {
  448.                         $out = array();
  449.                         $ec = -1;
  450.                         exec($cmd, $out, $ec);
  451.                         if ($ec == 0) {
  452.                                 $out = implode("\n",$out);
  453.                                 $m = array();
  454.                                 if (preg_match("/([0-9a-f]{2}\\-[0-9a-f]{2}\\-[0-9a-f]{2}\\-[0-9a-f]{2}\\-[0-9a-f]{2}\\-[0-9a-f]{2})/ismU", $out, $m)) {
  455.                                         $detected_mac = str_replace('-', ':', strtolower($m[1]));
  456.                                         return $detected_mac;
  457.                                 }
  458.                         }
  459.                 }
  460.         } else if (strtoupper(PHP_OS) == 'DARWIN') {
  461.                 // Mac OS X
  462.                 $cmds = array(
  463.                         "networksetup -listallhardwareports 2>/dev/null",
  464.                         "netstat -i 2>/dev/null"
  465.                 );
  466.                 foreach ($cmds as $cmd) {
  467.                         $out = array();
  468.                         $ec = -1;
  469.                         exec($cmd, $out, $ec);
  470.                         if ($ec == 0) {
  471.                                 $out = implode("\n",$out);
  472.                                 $m = array();
  473.                                 if (preg_match("/([0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2})/ismU", $out, $m)) {
  474.                                         $detected_mac = $m[1];
  475.                                         return $detected_mac;
  476.                                 }
  477.                         }
  478.                 }
  479.         } else {
  480.                 // Linux
  481.                 foreach (glob('/sys/class/net/'.'*'.'/address') as $x) {
  482.                         if (!strstr($x,'/lo/')) {
  483.                                 $detected_mac = trim(file_get_contents($x));
  484.                                 return $detected_mac;
  485.                         }
  486.                 }
  487.                 $cmds = array(
  488.                         "netstat -ie 2>/dev/null",
  489.                         "ifconfig 2>/dev/null" // only available for root (because it is in sbin)
  490.                 );
  491.                 foreach ($cmds as $cmd) {
  492.                         $out = array();
  493.                         $ec = -1;
  494.                         exec($cmd, $out, $ec);
  495.                         if ($ec == 0) {
  496.                                 $out = implode("\n",$out);
  497.                                 $m = array();
  498.                                 if (preg_match("/([0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2})/ismU", $out, $m)) {
  499.                                         $detected_mac = $m[1];
  500.                                         return $detected_mac;
  501.                                 }
  502.                         }
  503.                 }
  504.         }
  505.  
  506.         $detected_mac = null;
  507.         return $detected_mac;
  508. }
  509.  
  510. // Version 2 (DCE Security) UUID
  511. function gen_uuid_dce($domain, $id) {
  512.         # Start with a version 1 UUID
  513.         $uuid = gen_uuid_timebased();
  514.  
  515.         # Add ID
  516.         $uuid = str_pad(dechex($id), 8, '0', STR_PAD_LEFT) . substr($uuid, 8);
  517.  
  518.         # Add domain
  519.         $uuid = substr($uuid,0,21) . str_pad(dechex($domain), 2, '0', STR_PAD_LEFT) . substr($uuid, 23);
  520.  
  521.         # Change version to 2
  522.         $uuid[14] = '2';
  523.  
  524.         return $uuid;
  525. }
  526.  
  527. // Version 3 (MD5 name based) UUID
  528. function gen_uuid_md5_namebased($namespace_uuid, $name) {
  529.         if (!uuid_valid($namespace_uuid)) return false;
  530.         $namespace_uuid = uuid_canonize($namespace_uuid);
  531.         $namespace_uuid = str_replace('-', '', $namespace_uuid);
  532.         $namespace_uuid = hex2bin($namespace_uuid);
  533.  
  534.         $hash = md5($namespace_uuid.$name);
  535.         $hash[12] = '3'; // Set version: 3 = MD5
  536.         $hash[16] = dechex(hexdec($hash[16]) & 0x3 | 0x8); // Set variant to "10xx" (RFC4122)
  537.  
  538.         return substr($hash,  0, 8).'-'.
  539.                substr($hash,  8, 4).'-'.
  540.                substr($hash, 12, 4).'-'.
  541.                substr($hash, 16, 4).'-'.
  542.                substr($hash, 20, 12);
  543. }
  544.  
  545. // Version 4 (Random) UUID
  546. function gen_uuid_random() {
  547.         # On Debian: apt-get install php-uuid
  548.         # extension_loaded('uuid')
  549.         if (function_exists('uuid_create')) {
  550.                 # OSSP uuid extension like seen in php5-uuid at Debian 8
  551.                 /*
  552.                 $x = uuid_create($context);
  553.                 uuid_make($context, UUID_MAKE_V4);
  554.                 uuid_export($context, UUID_FMT_STR, $uuid);
  555.                 return trim($uuid);
  556.                 */
  557.  
  558.                 # PECL uuid extension like seen in php-uuid at Debian 9
  559.                 return trim(uuid_create(UUID_TYPE_RANDOM));
  560.         }
  561.  
  562.         if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
  563.                 # On Debian: apt-get install uuid-runtime
  564.                 $out = array();
  565.                 $ec = -1;
  566.                 exec('uuidgen -r 2>/dev/null', $out, $ec);
  567.                 if ($ec == 0) return $out[0];
  568.  
  569.                 # On Debian Jessie: UUID V4 (Random)
  570.                 if (file_exists('/proc/sys/kernel/random/uuid')) {
  571.                         return file_get_contents('/proc/sys/kernel/random/uuid');
  572.                 }
  573.         }
  574.  
  575.         # Make the UUID by ourselves
  576.         # Source: http://rogerstringer.com/2013/11/15/generate-uuids-php
  577.         return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
  578.                 mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
  579.                 mt_rand( 0, 0xffff ),
  580.                 mt_rand( 0, 0x0fff ) | 0x4000,
  581.                 mt_rand( 0, 0x3fff ) | 0x8000,
  582.                 mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff )
  583.         );
  584. }
  585.  
  586. // Version 5 (SHA1 name based) UUID
  587. function gen_uuid_sha1_namebased($namespace_uuid, $name) {
  588.         $namespace_uuid = str_replace('-', '', $namespace_uuid);
  589.         $namespace_uuid = hex2bin($namespace_uuid);
  590.  
  591.         $hash = sha1($namespace_uuid.$name);
  592.         $hash[12] = '5'; // Set version: 5 = SHA1
  593.         $hash[16] = dechex(hexdec($hash[16]) & 0x3 | 0x8); // Set variant to "10xx" (RFC4122)
  594.  
  595.         return substr($hash,  0, 8).'-'.
  596.                substr($hash,  8, 4).'-'.
  597.                substr($hash, 12, 4).'-'.
  598.                substr($hash, 16, 4).'-'.
  599.                substr($hash, 20, 12);
  600. }
  601.  
  602. function uuid_numeric_value($uuid) {
  603.         $oid = uuid_to_oid($uuid);
  604.         if (!$oid) return false;
  605.         return substr($oid, strlen('2.25.'));
  606. }
  607.  
  608. function uuid_c_syntax($uuid) {
  609.         $uuid = str_replace('{', '', $uuid);
  610.         return '{ 0x' . substr($uuid, 0, 8) .
  611.                 ', 0x' . substr($uuid, 9, 4) .
  612.                 ', 0x' . substr($uuid, 14, 4) .
  613.                 ', { 0x' . substr($uuid, 19, 2).
  614.                 ', 0x' . substr($uuid, 21, 2) .
  615.                 ', 0x' . substr($uuid, 24, 2) .
  616.                 ', 0x' . substr($uuid, 26, 2) .
  617.                 ', 0x' . substr($uuid, 28, 2) .
  618.                 ', 0x' . substr($uuid, 30, 2) .
  619.                 ', 0x' . substr($uuid, 32, 2) .
  620.                 ', 0x' . substr($uuid, 34, 2) . ' } }';
  621. }
  622.  
  623. # ---
  624.  
  625. // http://php.net/manual/de/function.hex2bin.php#113057
  626. if ( !function_exists( 'hex2bin' ) ) {
  627.     function hex2bin( $str ) {
  628.         $sbin = "";
  629.         $len = strlen( $str );
  630.         for ( $i = 0; $i < $len; $i += 2 ) {
  631.             $sbin .= pack( "H*", substr( $str, $i, 2 ) );
  632.         }
  633.  
  634.         return $sbin;
  635.     }
  636. }
  637.