function gen_http_request($url) { (yield 'notdone' => [$url, 'Just Loaded']); // Always yield to start with to help with queueing up requests $url = trim($url); if (!$url) { (yield 'error' => [$url, 'No URL was provided']); } $started_at = time(); $parsed_url = parse_url($url); $parsed_url['path'] = $parsed_url['path'] ?: '/'; $request = "GET {$parsed_url['path']} HTTP/1.0\r\n" . "Host: {$parsed_url['host']}\r\n" . "User-Agent: Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10136\r\n" . "Connection: close\r\n" . "Accept: */*\r\n" . "pragma: no-cache\r\n" . "dnt: 1\r\n" . "\r\n"; // GET THE IP $ip = false; $ip_gen = gen_get_ip($parsed_url['host']); while (!$ip && $ip_gen->key()) { $ip_gen->next(); list($domain, $result) = $ip_gen->current(); switch ($ip_gen->key()) { case 'notdone': (yield 'notdone' => [$url, 'Waiting on DNS']); continue; case 'error': (yield 'error' => [$url, 'DNS resolution failed']); continue; case 'result': $ip = $result; break; } } //var_dump( "Found the IP for $url it's $ip" ); $connect_str = 'https' == $parsed_url['scheme'] ? "ssl://{$ip}:443" : "tcp://{$ip}:80"; $timeout = 15; $context = stream_context_create(array('ssl' => array('verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true))); $socket = new D_AsyncSocket($connect_str, $timeout, $context); if ($socket->error()) { (yield 'error' => [$url, "Connect failed with " . $socket->error()]); } do { if (time() - $started_at > 15) { //echo "Giving up $url"; unset($socket); (yield 'error' => [$url, 'Giving Up']); } if ($socket->error()) { //echo "Error on $url\n"; (yield 'error' => [$url, 'URL Hung up on us']); } if (!$socket->ready_for_write()) { //echo "[$url] waiting for socket connect\n"; (yield 'notdone' => [$url, 'waiting for socket connect']); continue; } $wrote = $socket->write($request, strlen($request)); $request = substr($request, $wrote); } while (!$socket->error() && $request); $data = ''; while (!$socket->error() && !$socket->eof()) { if (time() - $started_at > 15) { //echo "Giving up $url"; (yield 'error' => [$url, 'Giving Up']); } if (!$socket->ready_for_read()) { //echo "[$url] waiting for data\n"; (yield 'notdone' => [$url, 'waiting for data']); continue; } $data .= $socket->read(8192); } unset($socket); if (!$data) { //echo "[$url] No data\n"; (yield 'error' => [$url, 'no data']); } list($headers, $body) = parse_http_response($data); $result = ['headers' => $headers, 'body' => $body, 'resolved_ip' => $ip]; (yield 'result' => [$url, $result]); }
function gen_get_ip($domain) { $domain = "{$domain}."; $dns = DNS_SERVER; $timeout = 30; $started_at = time(); /* Message ID, ( QR, OPCODE, AA, TC ), Recursion Desired, Recursion Avail, Response Code, Entries in Question?? */ $data = pack('n6', rand(10, 77), 0x100, 1, 0, 0, 0); /* Question */ foreach (explode('.', $domain) as $bit) { $l = strlen($bit); $data .= chr($l) . $bit; } $data .= pack('n2', 1, 1); // QTYPE=A, QCLASS=IN $socket = new D_AsyncSocket("udp://{$dns}:53", $timeout); if ($socket->error()) { (yield 'error' => [$domain, "failed to connect"]); } do { if (time() - $started_at > $timeout) { //echo "Giving up $domain"; unset($socket); (yield 'error' => [$domain, 'Giving Up']); } if ($socket->error()) { //echo "Error on $domain\n"; (yield 'error' => [$domain, 'DNS Hung up on us']); } if (!$socket->ready_for_write()) { //echo "[$domain] waiting for socket connect\n"; (yield 'notdone' => [$domain, 'waiting for socket connect']); continue; } $wrote = $socket->write($data, strlen($data)); $data = substr($data, $wrote); } while ($data); $data = ''; $ip = false; while (!$ip && $socket && !$socket->error() && !$socket->eof()) { if (time() - $started_at > $timeout) { //echo "Giving up $domain"; (yield 'error' => [$domain, 'Giving Up']); } if (!$socket->ready_for_read()) { //echo "[$domain] waiting for data\n"; (yield 'notdone' => [$domain, 'waiting for data']); continue; } $data .= $socket->read(8192); $data_length = strlen($data); $i = 0; if ($data_length >= 12) { list(, $message_id, $QR_OPCODE_AA_TC_RD_RA_RCODE, $QDCOUNT, $ANCOUNT, $NSCOUNT, $ARCOUNT) = unpack('n6', substr($data, 0, 12)); $QR_OPCODE_AA_TC_RD_RA_RCODE = decbin($QR_OPCODE_AA_TC_RD_RA_RCODE); // Format of $QR_OPCODE_AA_TC_RD_RA_RCODE uint16 in binary // 1 0000 0 0 1 1 0 0 0 0000 // QR OPCODE AA TC RD RA res1 res2 res3 RCODE $QR = bindec(substr($QR_OPCODE_AA_TC_RD_RA_RCODE, 0, 1)); $OPCODE = bindec(substr($QR_OPCODE_AA_TC_RD_RA_RCODE, 1, 4)); $AA = bindec(substr($QR_OPCODE_AA_TC_RD_RA_RCODE, 5, 1)); $TC = bindec(substr($QR_OPCODE_AA_TC_RD_RA_RCODE, 6, 1)); $RD = bindec(substr($QR_OPCODE_AA_TC_RD_RA_RCODE, 7, 1)); $RA = bindec(substr($QR_OPCODE_AA_TC_RD_RA_RCODE, 8, 1)); // $res1, $res2, $res2 = 9, 10, and 11 $RCODE = bindec(substr($QR_OPCODE_AA_TC_RD_RA_RCODE, 12, 4)); unset($QR_OPCODE_AA_TC_RD_RA_RCODE); // var_dump( compact( 'message_id', 'QR', 'OPCODE', 'AA', 'TC', 'RD', 'RA', 'RCODE', 'QDCOUNT', 'ANCOUNT', 'NSCOUNT', 'ARCOUNT' ) ); if ($RCODE > 0) { unset($socket); (yield 'error' => [$domain, 'Error code raised: ' . $RCODE]); } $i = 12; } if ($data_length > $i) { $answer = ''; while ("" != substr($data, $i, 1)) { $field_length = ord(substr($data, $i, 1)); $answer .= substr($data, $i + 1, $field_length) . '.'; $i += $field_length + 1; } // var_dump( compact( 'answer' ) ); } if ($data_length >= $i + 5) { $i++; list(, $qtype, $qclass) = unpack('n2', substr($data, $i, 4)); // var_dump( compact( 'qtype', 'qclass' ) ); $i += 4; } if ($data_length > $i) { for ($answer_i = 1; $answer_i <= $ANCOUNT; $answer_i++) { list(, $NAME, $TYPE, $CLASS) = unpack('n3', substr($data, $i, 6)); $i += 6; list(, $TTL) = unpack('N', substr($data, $i, 4)); $i += 4; list(, $RDLENGTH) = unpack('n', substr($data, $i, 2)); $i += 2; // IPv4 is always $RDLENGTH = 4, others 2 if (1 == $TYPE && 4 == $RDLENGTH) { list(, $RDATA) = unpack('N', substr($data, $i, $RDLENGTH)); $ip = long2ip($RDATA); } elseif ($RDLENGTH > 4) { // We don't actually need to know this field. // Assume FQDN. $_RDATA = substr($data, $i, $RDLENGTH); $_i = 0; $RDATA = ''; while ("" != substr($_RDATA, $_i, 1) && $_i < $RDLENGTH) { $field_length = ord(substr($_RDATA, $_i, 1)); $RDATA .= substr($_RDATA, $_i + 1, $field_length) . '.'; $_i += $field_length + 1; } unset($_RDATA, $_i, $field_length); } else { // We don't really care about this anyway... $RDATA = substr($data, $i, $RDLENGTH); } $i += $RDLENGTH; // var_dump( compact( 'NAME', 'TYPE', 'CLASS', 'TTL', 'RDLENGTH', 'RDATA' ) ); } } } unset($socket); (yield 'result' => [$answer, $ip]); }