PHP Conference Japan 2024

getmxrr

(PHP 4, PHP 5, PHP 7, PHP 8)

getmxrr取得與指定網際網路主機名稱對應的 MX 記錄

說明

getmxrr(字串 $hostname, 陣列 &$hosts, 陣列 &$weights = null): 布林值

在 DNS 中搜尋對應於 hostname 的 MX 記錄。

參數

hostname

網際網路主機名稱。

hosts

找到的 MX 記錄列表會放入 hosts 陣列中。

weights

如果提供了 weights 陣列,它將會被填入收集到的權重資訊。

回傳值

如果找到任何記錄,則回傳 true;如果沒有找到記錄或發生錯誤,則回傳 false

注意事項

注意:

此函式不應用於地址驗證的目的。只會回傳在 DNS 中找到的郵件交換器,然而,根據 » RFC 2821,當沒有列出郵件交換器時,應使用 hostname 本身作為唯一的郵件交換器,優先順序為 0

注意:

為了與尚未實作此功能的 Windows 版本相容,請嘗試使用 » PEAR 類別 » Net_DNS

參見

  • checkdnsrr() - 檢查與給定網際網路主機名稱或 IP 地址相對應的 DNS 記錄
  • dns_get_record() - 取得與主機名稱關聯的 DNS 資源記錄
  • gethostbyname() - 取得與給定網際網路主機名稱相對應的 IPv4 地址
  • gethostbynamel() - 取得與給定網際網路主機名稱相對應的 IPv4 地址列表
  • gethostbyaddr() - 取得與給定 IP 地址相對應的網際網路主機名稱
  • named(8) 使用手冊頁面

新增註釋

使用者貢獻的註釋 19 則註釋

19
Robert Imhoff
13 年前
我嘗試使用 getmxrr() 來驗證詢問提交表單中電子郵件地址的網域名稱部分,並且在檢查不存在的網域名稱時,某些頂級網域名稱會產生奇怪的效果。

使用 sdlkfjsdl.com,由於網域名稱不存在,getmxrr() 會按預期回傳 false,並且回傳的 mxhosts 陣列是空的。

但是使用 sdlkfjsdl.gov,getmxrr() 會回傳 true,並且回傳的 mxhosts 陣列包含一個元素:NULL

使用 sdlkfjsdl.org,getmxrr() 會回傳 true,並且回傳的 mxhosts 陣列包含一個元素:'0.0.0.0'

使用 sdlkfjsdl.co.uk,getmxrr() 會回傳 true 並提供一個 MX 記錄:uk-net-wildcard-null-mx.centralnic.net

所以,要驗證電子郵件網域,似乎必須檢查返回的 mxhosts 陣列,排除 mxhosts 返回 NULL、0.0.0.0 和萬用字元的情況……
3
Jay
17 年前
如前所述,下面列出的一些程式碼在處理多個相同權重時會出現問題,例如查詢 gmail.com 時。以下程式碼透過交換鍵/值來避免這個問題。

<?php

// 取得記錄
getmxrr("gmail.com", $mx_records, $mx_weight);

// 將記錄放入一個可以排序的陣列中
for($i=0;$i<count($mx_records);$i++){
$mxs[$mx_records[$i]] = $mx_weight[$i];
}

// 排序
asort ($mxs);

// 由於鍵實際上包含我們想要的資料,只需將它們放入一個名為 records 的陣列中
$records = array_keys($mxs);

// 簡單地輸出 records 陣列中的所有內容
for($i = 0; $i < count($records); $i++){
echo
$records[$i];
echo
'<br>';
}

?>

如果您想取得權重,可以使用 "array_values($mxs);" 而不是 "array_keys($mxs);"。

希望這對一些人有所幫助。
11
rune dot heggtveit at devzone dot progative dot com
19 年前
在 Windows 平台上執行 mx 查詢的另一種方法。
這是從我為 DNS 查詢編寫的另一個類別重寫而來的——所以它可能有點亂——但希望您能理解。

非常感謝 rfc 社群。

<?php

class mxlookup
{
var
$dns_socket = NULL;
var
$QNAME = "";
var
$dns_packet= NULL;
var
$ANCOUNT = 0;
var
$cIx = 0;
var
$dns_repl_domain;
var
$arrMX = array();

function
mxlookup($domain, $dns="192.168.2.1")
{
$this->QNAME($domain);
$this->pack_dns_packet();
$dns_socket = fsockopen("udp://$dns", 53);

fwrite($dns_socket,$this->dns_packet,strlen($this->dns_packet));
$this->dns_reply = fread($dns_socket,1);
$bytes = stream_get_meta_data($dns_socket);
$this->dns_reply .= fread($dns_socket,$bytes['unread_bytes']);
fclose($dns_socket);
$this->cIx=6;
$this->ANCOUNT = $this->gord(2);
$this->cIx+=4;
$this->parse_data($this->dns_repl_domain);
$this->cIx+=7;

for(
$ic=1;$ic<=$this->ANCOUNT;$ic++)
{
$QTYPE = ord($this->gdi($this->cIx));
if(
$QTYPE!==15){print("[MX Record not returned]"); die();}
$this->cIx+=9;
$mxPref = ord($this->gdi($this->cIx));
$this->parse_data($curmx);
$this->arrMX[] = array("MX_Pref" => $mxPref, "MX" => $curmx);
$this->cIx+=3;
}
}

function
parse_data(&$retval)
{
$arName = array();
$byte = ord($this->gdi($this->cIx));
while(
$byte!==0)
{
if(
$byte==192) //compressed
{
$tmpIx = $this->cIx;
$this->cIx = ord($this->gdi($cIx));
$tmpName = $retval;
$this->parse_data($tmpName);
$retval=$retval.".".$tmpName;
$this->cIx = $tmpIx+1;
return;
}
$retval="";
$bCount = $byte;
for(
$b=0;$b<$bCount;$b++)
{
$retval .= $this->gdi($this->cIx);
}
$arName[]=$retval;
$byte = ord($this->gdi($this->cIx));
}
$retval=join(".",$arName);
}

function
gdi(&$cIx,$bytes=1)
{
$this->cIx++;
return(
substr($this->dns_reply, $this->cIx-1, $bytes));
}

function
QNAME($domain)
{
$dot_pos = 0; $temp = "";
while(
$dot_pos=strpos($domain,"."))
{
$temp = substr($domain,0,$dot_pos);
$domain = substr($domain,$dot_pos+1);
$this->QNAME .= chr(strlen($temp)).$temp;
}
$this->QNAME .= chr(strlen($domain)).$domain.chr(0);
}

function
gord($ln=1)
{
$reply="";
for(
$i=0;$i<$ln;$i++){
$reply.=ord(substr($this->dns_reply,$this->cIx,1));
$this->cIx++;
}

return
$reply;
}

function
pack_dns_packet()
{
$this->dns_packet = chr(0).chr(1).
chr(1).chr(0).
chr(0).chr(1).
chr(0).chr(0).
chr(0).chr(0).
chr(0).chr(0).
$this->QNAME.
chr(0).chr(15).
chr(0).chr(1);
}

}

?>

<?php

/* 使用範例: */
$mx = new mxlookup("php.net");

print
$mx->ANCOUNT." 個 MX 記錄\n";
print
"傳回 ".$mx->dns_repl_domain." 的記錄:\n<pre>";
print_r($mx->arrMX);

?>

返回

02 個 MX 記錄
傳回 php.net 的記錄:

陣列
(
[0] => 陣列
(
[MX_Pref] => 15
[MX] => smtp.osuosl.org
)

[1] => 陣列
(
[MX_Pref] => 5
[MX] => osu1.php.net
)

)
3
zorlac_man at hotmail dot com
20 年前
基於某些原因,這個和其他 DNS 查詢函式在我的 Linux 系統上執行速度非常慢。我已經檢查了幾個地方,但找不到任何解釋。

作為一種解決方法,我只好使用系統呼叫 dig

<?php
CheckMX
("fakedomain.org");
CheckMX("hotmail.com");

function
CheckMX($domain) {
exec("dig +short MX " . escapeshellarg($domain),$ips);
if(
$ips[0] == "") {
print
"找不到 $domain 的 MX 記錄!\n";
return
FALSE;
}
print
"已找到 $domain 的 MX 記錄\n";
return
TRUE;
}
?>

輸出

找不到 fakedomain.org 的 MX 記錄!
已找到 hotmail.com 的 MX 記錄

正如其他人所指出的,如果找不到 MX 記錄,檢查 TLD 是否有 IP 位址是明智之舉。
1
php [spat] hm2k.org
15 年前
在做了一些研究之後,我決定要嘗試一下這個問題...

<?php

// getmxrr() support for Windows by HM2K <php [spat] hm2k.org>
function win_getmxrr($hostname, &$mxhosts, &$mxweight=false) {
if (
strtoupper(substr(PHP_OS, 0, 3)) != 'WIN') return;
if (!
is_array ($mxhosts) ) $mxhosts = array();
if (empty(
$hostname)) return;
$exec='nslookup -type=MX '.escapeshellarg($hostname);
@
exec($exec, $output);
if (empty(
$output)) return;
$i=-1;
foreach (
$output as $line) {
$i++;
if (
preg_match("/^$hostname\tMX preference = ([0-9]+), mail exchanger = (.+)$/i", $line, $parts)) {
$mxweight[$i] = trim($parts[1]);
$mxhosts[$i] = trim($parts[2]);
}
if (
preg_match('/responsible mail addr = (.+)$/i', $line, $parts)) {
$mxweight[$i] = $i;
$mxhosts[$i] = trim($parts[1]);
}
}
return (
$i!=-1);
}

// Define
if (!function_exists('getmxrr')) {
function
getmxrr($hostname, &$mxhosts, &$mxweight=false) {
return
win_getmxrr($hostname, $mxhosts, $mxweight);
}
}

/* example */

$domain='php.net';
echo
"<pre>";
getmxrr($domain,$mxhosts,$mxweight);
print_r($mxhosts);
print_r($mxweight);

?>
0
匿名
8 年前
getmxrr 函式的一個小型 polyfill(即使在 Linux 系統中缺少此函式時)

function getmxrr($hostname, &$mxhosts, array &$mxweight=null)
{
$mxhosts = array();
exec('nslookup -type=mx '.$hostname, $result_arr);
foreach($result_arr as $line)
{
if (preg_match("/.*mail exchanger = (.*)/", $line, $matches))
{
$s_xo=explode(' ', $matches[1]);
$mxhosts[] = $s_xo[1];
$mxweight[] = $s_xo[0];
}
}
return( count($mxhosts) > 0 );
}
1
geoffbrisbine A T y a h o o DOT c o m
22 年前
我對 Win32 版本的 PHP 沒有包含 getmxrr 函式感到非常失望,因此,由於我是一個菜鳥新手,我決定自己拼湊一個(我強調是拼湊)。這已在 Win 2000 和 Win XP 上進行過測試。沒有理由它不能在 Win NT 上運行,但它不能在 Win 9x 上運行(你需要 nslookup 命令)。它將以陣列 $mx 結束,這將是一個包含 MX 優先順序、主機名稱和 IP 位址的多維陣列。您可以執行 print_r ( $mx ) 來查看它的外觀。

-----------------------------------------------

<?php
$command
= "nslookup -type=mx yahoo.com";
exec ( $command, $result );

$i = 0;
while ( list (
$key, $value ) = each ( $result ) ) {
if (
strstr ( $value, "mail exchanger" ) ) { $nslookup[$i] = $value; $i++; }
}

while ( list (
$key, $value ) = each ( $nslookup ) ) {
$temp = explode ( " ", $value );
$mx[$key][0] = $temp[3];
$mx[$key][1] = $temp[7];
$mx[$key][2] = gethostbyname ( $temp[7] );
}

array_multisort ( $mx );
?>
0
tomhutter at web dot de
18 年前
Leonardt 的程式碼在處理多個權重相同的 MX 記錄時會失敗。您可以透過交換 mxs 陣列中的鍵和值來輕鬆更改此問題

for($i=0;$i<count($mx_records);$i++){
$mxs[$mx_records[$i]] = $mx_weight[$i];
}

arsort ($mxs );
reset ($mxs);

while (list ($mx_host, $mx_weight) = each ($mxs) ) {

致敬

Tom
0
php dot net at oitc dot com
18 年前
這個函式在處理別名時會有一些奇怪的副作用...

我的函式

if (getmxrr($fqdn, $mx_records, $mx_weight)) {
// 將 MX 記錄和權重複製到陣列 $mxs 中
// 忽略相同權重的多個 MX
for ($i = 0; $i < count($mx_records); $i++) {
$mxs[$mx_weight[$i]] = $mx_records[$i];
}
// 排序陣列 mxs 以取得優先順序最高的伺服器
ksort ($mxs, SORT_NUMERIC);
reset ($mxs);
} else {
// 沒有 MX,所以使用 A
$mxs[0]= $fqdn;
}

會失敗,因為包含別名的 $fqdn 會返回 true,但在返回時 $mx_records 和 $mx_weight 都沒有包含任何內容!

在修復此問題之前,解決方案是將 if (getmxrr($fqdn, $mx_records, $mx_weight)) 替換為

// 處理別名等等。
if ($result = getmxrr($fqdn, $mx_records, $mx_weight)) {
if(!isset($mx_records) || (count($mx_records) == 0)) $result = false;
}
// 處理 MX 記錄
if ($result) {

希望這對其他人有幫助.... Tom
0
MagicalTux at ooKoo dot org
18 年前
如果您要在 Windows 上使用 getmxrr,請小心,因為 choward AT fast DOT net DOT NO SPAM PLZ 的函式存在安全漏洞。

它會將參數傳遞給外部指令,而沒有進行跳脫處理。如果您沒有驗證輸入,有人可能會在您的系統上執行惡意程式碼。

這是修正後的版本(僅添加了 escapeshellarg())

<?php
function getmxrr($hostname, &$mxhosts)
{
$mxhosts = array();
exec('%SYSTEMDIRECTORY%\\nslookup.exe -q=mx '.escapeshellarg($hostname), $result_arr);
foreach(
$result_arr as $line)
{
if (
preg_match("/.*mail exchanger = (.*)/", $line, $matches))
$mxhosts[] = $matches[1];
}
return(
count($mxhosts) > 0 );
}
//--解決方法結束

//測試..
getmxrr('yahoo.com', $mxhosts);
print_r($mxhosts);
?>

這樣可以避免很多不好的事情 ;)
0
off at NOSPAM dot abwesend dot de
18 年前
關於 'rolf at rowi dot net' 的訊息(檢查包含子網域的地址),我們可以使用

$email = 'abc@etpc01.trier.fh-rpl.de';

$strDot = '.';
$strAfterAt = substr(strstr($email, '@'), 1);
$chunks = explode($strDot, $strAfterAt);
$cntChunks = count($chunks) - 1;

$strDomain = $chunks[($cntChunks-1)] . $strDot . $chunks[$cntChunks];

if (!getmxrr( $strDomain, $mxhosts )) {
echo '找不到郵件伺服器';
}

// $strDomain 設定為 'fh-rpl.de';
0
richard dot quadling at bandvulc dot co dot uk
19 年前
Windows 系統上 getmxrr 的替代方案,無需使用 PEAR。

<?php
define
('DEFAULT_GATEWAY', 'nnn.nnn.nnn.nnn');
if (!
function_exists('getmxrr'))
{
/*
這個函式是用來取代 Windows 系統缺少的 getmxrr 函式。

參數與正常的 getmxrr 函式相同。

這個函式的步驟如下:

1 - 使用 NSLOOKUP.EXE 取得指定主機的 MX 記錄。
2 - 使用正規表示式擷取郵件伺服器和優先順序。
3 - 根據優先順序排序結果。
4 - 設定返回陣列。
5 - 返回 true 或 false。
*/
function getmxrr($s_HostName, array &$a_MXHosts = NULL, array &$a_Weights = NULL)
{
// 透過執行 Windows 的 NSLOOKUP 模擬所有必要的網路活動。
$s_NSLookup = shell_exec("nslookup -q=mx {$s_HostName} 2>nul");
preg_match_all("'^.*MX preference = (\d{1,10}), mail exchanger = (.*)$'simU", $s_NSLookup, $a_MXMatches);

// 如果有東西要返回...
if (count($a_MXMatches[2]) > 0)
{
// 如果已請求,則產生輸出陣列。
$i_ArgCount = func_num_args();
if (
$i_ArgCount > 1)
{
array_multisort($a_MXMatches[1], $a_MXMatches[2]);
switch (
$i_ArgCount)
{
case
3 :
$a_Weights = $a_MXMatches[1];
case
2 :
$a_MXHosts = $a_MXMatches[2];
}
}
return
True;
}
else
{
return
False;
}
}
}
?>

您需要知道您的預設閘道器(IP 位址或名稱)。

為此,請在 cmd 命令提示字元下執行 IPCONFIG /ALL 程式,並查找預設閘道器。

然後將 'nnn.nnn.nnn.nnn' 替換為該地址。

Richard.
-1
ng4rrjanbiah at rediffmail dot com
20 年前
這是在 Windows 平台上更好的解決方法。已在 Windows XP 上測試過。深受 "geoffbrisbine A T y a h o o DOT c o m" 使用 nslookup 的想法啟發。

<?php
函數 getmxrr($hostname, &$mxhosts)
{
$mxhosts = 陣列();
exec('nslookup -type=mx '.$hostname, $result_arr);
foreach(
$result_arr as $line)
{
if (
preg_match("/.*mail exchanger = (.*)/", $line, $matches))
$mxhosts[] = $matches[1];
}
return(
count($mxhosts) > 0 );
}
// -- 解決方案結束

// 測試..
echo getmxrr('yahoo.com', $mxhosts);
print_r($mxhosts);
?>

希望有幫助,
R. Rajesh Jeba Anbiah
-1
paul at start dot co dot uk
23 年前
透過在模式匹配和 MX 檢查中都附加一個尾點到網域,防止您的 DNS 伺服器將本地網域附加到不完整的電子郵件地址,從而「建立」有效的網域名稱。

<?php
if (eregi("^[0-9a-z_]([-_.]?[0-9a-z])*@[0-9a-z][-.0-9a-z]*\\.[a-z]{2,3}[.]?$", $string, $check)) {
$host = substr(strstr($check[0], '@'), 1).".";
if (
getmxrr($host, $validate_email_temp) )
return
TRUE;
// 這將會捕捉到非 MX 的 DNS。
if(checkdnsrr($host,"ANY"))
return
TRUE;
}
?>
-2
siclawrence at gmail dot com
19 年前
這段程式碼複製了線上工具的功能,可以檢查電子郵件地址是否有效。 首先,它會檢查電子郵件格式是否正確,然後查詢並印出 MX 記錄。 所有內容都經過精心格式化,並使用華麗的詞藻,最終會印出電子郵件地址是否有效。

<?php
$email
= "email@domain.com";

print(
"檢查: $email<br>");

if (
preg_match(, $email)) {

print(
"格式測試:通過<br>");
print(
"線上主機驗證測試...<br><br>");
print(
"MX 記錄: $email<br>");

list(
$alias, $domain) = explode("@", $email);

if (
checkdnsrr($domain, "MX")) {

getmxrr($domain, $mxhosts);

foreach(
$mxhosts as $mxKey => $mxValue){
print(
"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$mxValue<br>");
}

print(
"線上主機驗證測試:通過<br><br>");
print(
"電子郵件狀態:有效");

} else {

print(
"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;找不到記錄。<br>");
print(
"線上主機驗證測試:失敗<br><br>");
print(
"電子郵件狀態:無效");

}

} else {

print(
"格式測試:失敗<br><br>");
print(
"提供的電子郵件地址無效。<br><br>");
print(
"電子郵件狀態:無效");

}
?>
-1
Lennart Poot(www.twing.nl)
18 年前
此腳本使用 getmxrr 和 fsockopen 驗證電子郵件地址

1. 驗證地址的語法。
2. 透過主機名稱取得 MX 記錄
3. 連線到郵件伺服器並驗證信箱(使用 smtp 指令 RCTP TO:<email>)

當函式 "validate_email([email])" 無法連線到 MX 記錄中優先順序最高的郵件伺服器時,它會繼續連線到第二個郵件伺服器,依此類推。

當函式 "validate_email([email])" 在上述 3 個步驟中任何一個失敗時,它會返回 0,否則返回 1

Grtz Lennart Poot

<?
function validate_email($email){
$mailparts=explode("@",$email);
$hostname = $mailparts[1];

// 驗證電子郵件地址語法
$exp = "^[a-z\'0-9]+([._-][a-z\'0-9]+)*@([a-z0-9]+([._-][a-z0-9]+))+$";
$b_valid_syntax=preg_match("/$exp/i", $email);

// 使用 getmxrr 取得 mx 地址
$b_mx_avail=getmxrr( $hostname, $mx_records, $mx_weight );
$b_server_found=0;

if($b_valid_syntax && $b_mx_avail){
// 將 MX 記錄和權重複製到陣列 $mxs 中
$mxs=array();

for($i=0;$i<count($mx_records);$i++){
$mxs[$mx_weight[$i]]=$mx_records[$i];
}

// 排序陣列 mxs 以取得優先順序最高的伺服器
ksort ($mxs, SORT_NUMERIC );
reset ($mxs);

while (list ($mx_weight, $mx_host) = each ($mxs) ) {
if($b_server_found == 0){

//嘗試連線埠 25
$fp = @fsockopen($mx_host,25, $errno, $errstr, 2);
if($fp){
$ms_resp="";
// 向郵件伺服器問好 (HELO)
$ms_resp.=send_command($fp, "HELO microsoft.com");

// 初始化寄送郵件
$ms_resp.=send_command($fp, "MAIL FROM:<support@microsoft.com>");

// 嘗試收件者地址,如果正常,將返回 250..
$rcpt_text = send_command($fp, "RCPT TO:<".$email.">");
$ms_resp .= $rcpt_text;

if (substr($rcpt_text, 0, 3) == "250")
$b_server_found = 1;

// 中斷郵件伺服器連線
$ms_resp .= send_command($fp, "QUIT");

fclose($fp);

}

}
}
}
return $b_server_found;
}

function send_command($fp, $out) {

fwrite($fp, $out . "\r\n");
return get_data($fp);
}

function get_data($fp) {
$s = "";
stream_set_timeout($fp, 2);

for ($i = 0; $i < 2; $i++)
$s .= fgets($fp, 1024);

return $s;
}

// 支援 Windows 平台
if (!function_exists('getmxrr')) {
function getmxrr($hostname, &$mxhosts, &$mxweight) {
if (!is_array($mxhosts)) {
$mxhosts = array();
}

if (!empty($hostname)) {
$output = "";
@exec("nslookup.exe -type=MX $hostname.", $output);
$imx = -1;

foreach ($output as $line) {
$imx++;
$parts = "";
if (preg_match("/^$hostname\tMX preference = ([0-9]+), mail exchanger = (.*)$/", $line, $parts)) {
$mxweight[$imx] = $parts[1];
$mxhosts[$imx] = $parts[2];
}
}
return ($imx != -1);
}
return false;
}
}

?>
-1
jeff at pzenix dot com
18 年前
我必須指出,以下範例不適用於某些網域(例如 .co.uk、.org.uk、.net.uk),因為它假設(可能不正確)格式為 [網域].[延伸名稱]。
-2
TZ at inpetho dot net
16 年前
如果 MX 主機上安裝了灰名單,它會傳送「451 4.7.1 請稍後再試」。

我的程式碼片段

<?php
//…
foreach ($mx_records as $mx_host) {
$code = CheckMX($mx_host, $eMail);
if (
$code == 0) continue; // 找不到主機
if ($code == 451) $code = CheckMX($mx_host, $eMail); // 灰名單
if ($code == 250) {
$ok = true;
break;
}
}
//…

function CheckMX($mx_host, $eMail)
{
$code = 0;
$fp = @fsockopen($mx_host, 25, $errno, $errstr, 2);
if (
$fp) {
send_command($fp, 'HELO microsoft.com');
send_command($fp, 'MAIL FROM:<support@microsoft.com>');
$erg = send_command($fp, 'RCPT TO:<'.$eMail.'>');
fclose($fp);
$code = intval(substr($erg, 0, 3));
}
return
$code;
}
//…
?>
-2
rolf at rowi dot net
19 年前
要注意的是,不僅 user@example.com 是有效的地址,user@subnet.example.com 也是有效的(但可能比較少見)。我剛才就因為這個檢查而遇到問題…

Rolf
To Top