PHP中的原始套接字DNS请求

时间:2014-07-25 21:58:55

标签: php sockets dns

<?php

    $domain = 'www.google.com';
    $dns = '8.8.8.8';
    $timeout = 2;

    $data = rand(10, 77) . "\0\0\0\0\1\0\0\0\0\0";
    $data .= pack('n', '1') . pack('n', '0') . pack('n', '0') . pack('n', '0');

    foreach (explode('.', $domain) as $bit) {
        $l = strlen($bit);
        $data .= chr($l) . $bit;
    }

    $data .= "\0\0" . pack('n', '2') . pack('n', '1');

    $errno = $errstr = 0;
    $fp = fsockopen('udp://' . $dns, 53, $errno, $errstr, $timeout);
    if (!$fp || !is_resource($fp)) return $errno;

    socket_set_timeout($fp, $timeout);
    fwrite($fp, $data);

    var_dump ( fread($fp, 8192) );
    fclose($fp);

这是我一直试图开始工作的代码。以下是dns查找http://www.binarytides.com/dns-query-code-in-c-with-linux-sockets/的C实现的片段。我无法看到我在PHP中错过了什么。

至于使用dns_get_records,它可以工作,但它没有超时,这对我来说非常糟糕,因为我将要处理许多查找/秒。这是我的解决方案$lookup = shell_exec('dig "' . $cleaned_host . '." -t ANY +nomultiline +nocomments +nonssearch +tcp +nostats +qr +time=1');,但即使有了这样的改进,性能也是dns_get_records速度的两倍以上,这对我的情况非常不利。因此,我尝试使用DNS服务器进行原始套接字连接,假设速度将是dns_get_records的速度。


另一种方法是通过ini_set限制dns_get_records函数,例如default_socket_timeout,但我找不到任何可以使用的函数。

更好的解决方案(可以与脚本的其余部分一起使用)是获取cURL dns缓存。

2 个答案:

答案 0 :(得分:1)

大多数情况下,您未能发现的是C代码使用九个单独的位域将DNS标头标记打包成一个单独的16位字段,而您尝试追加9单独的字节。

您应该在随机查询ID之后添加pack('n', 0x0100),以便设置RD(所需的递归)位。

此外,您只应在域名后添加一个\0,以表示尾随的“根”标签,或者确保您提供的每个域名都有一个尾随点,以便{{1这个功能对你有用。此代码有效:

explode

不要忘记参考RFC 1035来了解如何正确解码响应!

答案 1 :(得分:1)

我需要一个代码来检查某个DNS服务器是否将DNS委托给另一台服务器,我已经从上面的示例开始,我的最终代码是:

function has_ns_records( $domain, $dns = 'dns1.nic.blog' ) {
   $timeout = 2;

    // Create a question packet
   $data = pack( 'n6', rand(10, 77), 0x0100, 1, 0, 0, 0 );

   foreach ( explode('.', $domain ) as $part ) {
       $length = strlen( $part );
       $data .= chr( $length ) . $part;
   }

   $data .= pack( 'n2' , 2, 1 );  // QTYPE=NS, QCLASS=IN

   $errno = $errstr = 0;
   $fp = fsockopen( 'udp://' . $dns, 53, $errno, $errstr, $timeout );
   if (!$fp || !is_resource($fp)) return $errno;

   socket_set_timeout( $fp, $timeout );
   fwrite( $fp, $data );

   $response_data = fread( $fp, 8192 );
   fclose( $fp );

   // read answer header
   $ans_header = unpack( "nid/nspec/nqdcount/nancount/nnscount/narcount", substr( $response_data, 0, 12 ) );

   // skip question part
   $offset = strlen( $domain ) + 4 + 2 + 1; // 4 => QTYPE + QCLASS, 2 => len, 1 => null terminator

   $record_header = unpack("ntype/nclass/Nttl/nlength", substr( $response_data, 12 + $offset, 10 ) );

   // Expect type NS and class IN, when domain not exist it returns SOA (6)
   return $record_header[ 'type' ] === 2 && $record_header[ 'class' ] === 1;
}


var_dump( has_ns_records( 'get.blog.' ) );