1<?php 2 3use dokuwiki\Extension\SyntaxPlugin; 4 5/** 6 * DokuWiki Plugin dig (Syntax Component) 7 * 8 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 9 * @author Andreas Gohr <andi@splitbrain.org> 10 */ 11class syntax_plugin_dig extends SyntaxPlugin 12{ 13 /** @inheritdoc */ 14 public function getType() 15 { 16 return 'substition'; 17 } 18 19 /** @inheritdoc */ 20 public function getPType() 21 { 22 return 'block'; 23 } 24 25 /** @inheritdoc */ 26 public function getSort() 27 { 28 return 333; 29 } 30 31 /** @inheritdoc */ 32 public function connectTo($mode) 33 { 34 $this->Lexer->addSpecialPattern('<dig>\n.*?\n</dig>', $mode, 'plugin_dig'); 35 } 36 37 /** @inheritdoc */ 38 public function handle($match, $state, $pos, Doku_Handler $handler) 39 { 40 $lines = explode("\n", $match); 41 array_shift($lines); // skip opening tag 42 array_pop($lines); // skip closing tag 43 44 return linesToHash($lines); 45 } 46 47 /** @inheritdoc */ 48 public function render($mode, Doku_Renderer $R, $data) 49 { 50 if ($mode == 'metadata') return false; 51 52 $R->table_open(); 53 $R->tablethead_open(); 54 $R->tablerow_open(); 55 $R->tableheader_open(); 56 $R->cdata($this->getLang('host')); 57 $R->tableheader_close(); 58 $R->tableheader_open(); 59 $R->cdata($this->getLang('ip')); 60 $R->tableheader_close(); 61 $R->tableheader_open(); 62 $R->cdata($this->getLang('ns')); 63 $R->tableheader_close(); 64 $R->tableheader_open(); 65 $R->cdata($this->getLang('ssl')); 66 $R->tableheader_close(); 67 $R->tablerow_close(); 68 $R->tablethead_close(); 69 70 $R->tabletbody_open(); 71 72 foreach ($data as $domain => $comment) { 73 $R->tablerow_open(); 74 $this->renderCell($R, [$domain, $comment]); 75 76 try { 77 $dns = $this->getDNS($domain); 78 $this->renderCell($R, $dns['ip']); 79 $this->renderCell($R, $dns['ns']); 80 } catch (Exception $e) { 81 $this->renderCell($R, $e->getMessage(), 2); 82 } 83 84 try { 85 $ssl = $this->getSSL($domain); 86 87 $this->renderCell( 88 $R, 89 [ 90 'valid' => $ssl['date_valid'] && $ssl['domain_valid'] ? '✔️' : '❌', 91 'until' => date('Y-m-d', $ssl['date_to']), 92 'days' => floor(($ssl['date_to'] - time()) / (24 * 60 * 60)), 93 'issuer' => $ssl['issuer'], 94 'domains' => count($ssl['domains']), 95 ] 96 ); 97 } catch (Exception $e) { 98 $this->renderCell($R, $e->getMessage()); 99 } 100 101 $R->tablerow_close(); 102 } 103 $R->tabletbody_close(); 104 105 $R->tabletfoot_open(); 106 $R->tablerow_open(); 107 $R->tablecell_open(4); 108 $R->cdata($this->getLang('lastupdate') . ' ' . date('Y-m-d H:i:s')); 109 $R->tablecell_close(); 110 $R->tablerow_close(); 111 $R->tabletfoot_close(); 112 113 $R->table_close(); 114 115 return true; 116 } 117 118 /** 119 * Get the DNS information for a domain 120 * 121 * @param string $domain 122 * @return array 123 * @throws Exception if the DNS lookup fails 124 */ 125 protected function getDNS($domain) 126 { 127 $info = [ 128 'ip' => [], 129 'ns' => [], 130 ]; 131 132 $record = dns_get_record($domain, DNS_ALL); 133 if (!$record) throw new Exception($this->getLang('dnsfail')); 134 135 foreach ($record as $r) { 136 switch ($r['type']) { 137 case 'A': 138 $info['ip']['IP'] = $r['ip']; 139 break; 140 case 'AAAA': 141 $info['ip']['IPv6'] = $r['ipv6']; 142 break; 143 case 'CNAME': 144 $info['ip']['CNAME'] = $r['target']; 145 break; 146 case 'SOA': 147 $info['ns']['NS'] = $r['mname']; 148 break; 149 case 'MX': 150 $info['ns']['MX'] = $r['target']; 151 break; 152 } 153 } 154 return $info; 155 } 156 157 /** 158 * Get the certificate information for a domain 159 * 160 * @param string $domain 161 * @return array 162 * @throws Exception if the SSL lookup fails 163 */ 164 protected function getSSL($domain) 165 { 166 if (!function_exists('openssl_x509_parse')) { 167 throw new Exception($this->getLang('sslfail') . ': ' . 'openssl not available in your PHP installation'); 168 } 169 170 $context = stream_context_create( 171 ["ssl" => ["capture_peer_cert" => true, 'verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true]] 172 ); 173 $read = @stream_socket_client( 174 "ssl://" . $domain . ":443", 175 $errno, 176 $errstr, 177 15, 178 STREAM_CLIENT_CONNECT, 179 $context 180 ); 181 if ($errstr) throw new Exception($this->getLang('sslfail') . ': ' . $errstr, $errno); 182 183 184 $cert = stream_context_get_params($read); 185 $certinfo = openssl_x509_parse($cert['options']['ssl']['peer_certificate']); 186 187 $issuer = $certinfo['issuer']['O'] ?? implode(' ', $certinfo['issuer']); 188 189 $domains = array_merge( 190 [$certinfo['subject']['CN']], 191 explode(',', $certinfo['extensions']['subjectAltName']) 192 ); 193 $domains = array_map(static fn($d) => preg_replace('/^DNS:/', '', trim($d)), $domains); 194 195 return [ 196 'date_from' => $certinfo['validFrom_time_t'], 197 'date_to' => $certinfo['validTo_time_t'], 198 'date_valid' => $certinfo['validFrom_time_t'] < time() && time() < $certinfo['validTo_time_t'], 199 'issuer' => $issuer, 200 'domains' => $domains, 201 'domain_valid' => in_array($domain, $domains), 202 ]; 203 } 204 205 /** 206 * @param Doku_Renderer $R 207 * @param string|array $data 208 * @param int $colspan 209 */ 210 protected function renderCell($R, $data, $colspan = 1) 211 { 212 $R->tablecell_open($colspan); 213 if (is_array($data)) { 214 foreach ($data as $key => $item) { 215 if (!is_numeric($key)) { 216 $R->cdata($key . ': '); 217 } 218 $R->cdata($item); 219 $R->linebreak(); 220 } 221 } else { 222 $R->cdata($data); 223 } 224 $R->tablecell_close(); 225 } 226} 227