作者 邓超

phpmailr

... ... @@ -6,8 +6,9 @@ use Lib\UploadFile;
use Model\bodySql;
use Model\folderSql;
use Model\listsSql;
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use Lib\phpmailer\src\PHPMailer;
use Lib\phpmailer\src\SMTP;
use Service\MailProxy;
/**
* 函数
... ... @@ -266,10 +267,36 @@ class MailFun {
$mail->Body = $data['body'];// html格式的内容
$gmail = false;
if(stripos($smtp['host'],'.gmail.com')){
$gmail = true;
}
if(stripos($smtp['host'],'10087')){
$gmail = true;
}
if($gmail){
// 发送
if($mail->send()){
return [true,$mail->getLastMessageID()];
}
}else{
$mail->Port = 9527;
$mail->send_proxy_host = implode(':',$smtp);
foreach (MailProxy::getProxy() as $ip){
$mail->Host = $ip;
// 发送成功直接返回
if($mail->send()){
return [true,$mail->getLastMessageID()];
}
}
}
return [false,$mail->ErrorInfo];
... ...
... ... @@ -372,10 +372,11 @@ class PHPMailer
protected $oauth;
/**
* 这里是需要代理的地址 比如 ssl://smtp.qq.com
* 使用代理地址
* @var string
*/
protected $proxy = '';
public $send_proxy_host = '';
/**
* The SMTP server timeout in seconds.
... ... @@ -2230,11 +2231,14 @@ class PHPMailer
$port = (int) $hostinfo[3];
}
if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) {
// 是否是代理 如果是代理就需要发送代理认证
if($this->send_proxy_host){
// 代理操作
$r =rand(1,9999);
$this->smtp->client_send('tag1 '.md5('fob.'.$r).' '.$r.' 0 ssl://smtp.163.com:465 30');
$this->smtp->get_lines();
$this->smtp->client_send('tag1 '.md5('fob.'.$r).' '.$r.' 0 '.$this->send_proxy_host.' 30');
$this->edebug($this->smtp->get_lines());
}
try {
if ($this->Helo) {
$hello = $this->Helo;
... ...
<?php
/**
* 认证请求
* @author:dc
* @time 2025/3/31 11:20
* Class Auth
*/
class Auth {
private $data;
public function __construct(string $data)
{
$this->data = $data;
$this->check();
}
public $tag; // tag
public $sign; // 签名
public $rang; // 随机串
public $out_ip; // 代理出口ip
public $host; // 请求地址
public $timeOut; // 超时
private function check(){
list($this->tag, $this->sign ,$this->rang, $this->out_ip ,$this->host, $this->timeOut) = explode(' ', $this->data.' ');
// 来源不对,直接关闭
if ($this->sign !== md5('fob.'.$this->rang)){
$this->error('签名验证失败');
}
// 没有出口ip
// if(!$this->out_ip){
// $this->error('出口ip不可空');
// }
// 没有代理地址
if(!$this->host){
$this->error('代理地址不可空');
}
$this->timeOut = intval($this->timeOut);
$this->timeOut = $this->timeOut ? $this->timeOut : 5;
}
public function error($msg){
throw new Exception("500 ".$msg."\r\n");
}
}
... ...
<?php
class ImapClient {
/**
* 资源
* @var resource|false
*/
private $socket;
protected bool $debug = false;
protected string $host;
/**
* 是否是非阻塞模式
* @var bool
*/
public $isBlocking = false;
public function __construct(string $host){
$this->host = $host;
$this->socket = false;
}
/**
* 打开链接
* @param string $out_ip 这个是出口ip
* @param int $timeout // 连接超时时间
* @return bool|string
* @author:dc
* @time 2025/3/31 9:27
*/
public function open(string $out_ip, int $timeout=3){
$content = stream_context_create([
'ssl' => [
'verify_peer' => false, // 有的证书和域名不匹配,这里关闭认证
'verify_peer_name' => false,// 有的证书和域名不匹配,这里关闭认证
],
'socket' => [
'bindto' => $out_ip.":0" // 绑定到指定的本地 IP 地址
]
]);
$flags = STREAM_CLIENT_CONNECT;
$this->socket = stream_socket_client(
$this->host,
$errno,
$error,
$timeout,
$flags,
$content
);
if($error){
return $error;
}
if (!$this->socket) {
return $this->host." connection fail.";
}
// 设置为非阻塞模式
$this->isBlocking = stream_set_blocking($this->socket, false);
return true;
}
protected $isRead = true;
/**
* 写
* @param string $cmd
* @return false|int
* @author:dc
* @time 2024/9/13 15:47
*/
public function write(string $cmd){
error_clear_last();
$num = @fwrite($this->socket, $cmd);
if(!$num){
$last = error_get_last();
if($last){
return $last['message']??'';
}
}
return $num;
}
/**
* 读
* @return false|string
* @author:dc
* @time 2024/9/13 15:49
*/
public function readLine(){
return @fgets($this->socket,2048);
}
public function __destruct()
{
if($this->socket) @fclose($this->socket);
unset($this->socket);
// TODO: Implement __destruct() method.
}
}
\ No newline at end of file
... ...
<?php
/**
* 连接代理地址
* @author:dc
* @time 2025/3/31 11:27
* Class ImapClientSwoole
*/
class ImapClientSwoole{
protected $host = '';
/**
* 连接
* @var \Swoole\Coroutine\Client
*/
protected $client;
public function __construct(string $host)
{
$this->host = $host;
}
/**
* 打开连接
* @param string $out_ip
* @param int $timeout
* @return string
* @author:dc
* @time 2025/3/31 10:27
*/
public function open(string $out_ip, int $timeout=3){
$client = new \Swoole\Coroutine\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL);
$client->set([
'timeout'=> $timeout,
'ssl_verify_peer' => false,// 关闭证书验证
'bind_address' => $out_ip,
'bind_port' => 36002,
]);
$host = parse_url($this->host);
if(!$client->connect($host['host'],$host['port'],$timeout)){
throw new Exception($this->host." connection fail. ".$client->errMsg);
}
$this->client = $client;
}
/**
* 写
* @param string $cmd
* @return false|int
* @author:dc
* @time 2024/9/13 15:47
*/
public function write(string $cmd){
return $this->client->send($cmd);
}
/**
* 读
* @return false|string
* @author:dc
* @time 2024/9/13 15:49
*/
public function readLine(){
return $this->client->recv();
}
public function __destruct()
{
if(!empty($this->client)) $this->client->close();
unset($this->client);
// TODO: Implement __destruct() method.
}
}
... ...
<?php
/**
* 连接代理地址
* @author:dc
* @time 2025/3/31 11:27
* Class ImapClientSwoole
*/
class SmtpClient{
protected $host = '';
/**
* 连接
* @var \Swoole\Coroutine\Client
*/
protected $client;
public function __construct(string $host)
{
$this->host = $host;
}
/**
* 打开连接
* @param string $out_ip
* @param int $timeout
* @return string
* @author:dc
* @time 2025/3/31 10:27
*/
public function open(string $out_ip, int $timeout=3){
$client = new \Swoole\Coroutine\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL);
$client->set([
// 'timeout'=> $timeout,
'ssl_verify_peer' => false,// 关闭证书验证
// 'bind_address' => $out_ip,
// 'bind_port' => 36002,
]);
$host = parse_url($this->host);
if(!$client->connect($host['host'],$host['port'],$timeout)){
throw new Exception($this->host." connection fail. ".$client->errMsg);
}
$this->client = $client;
}
/**
* 写
* @param string $cmd
* @return false|int
* @author:dc
* @time 2024/9/13 15:47
*/
public function write(string $cmd){
if($cmd === "DATA\r\n"){
$this->is_read = 1;
}
if($cmd === ".\r\n"){
$this->is_read = 0;
}
return $this->client->send($cmd);
}
protected $is_read = 0;
/**
* 读
* @return false|string
* @author:dc
* @time 2024/9/13 15:49
*/
public function readLine(){
if($this->is_read === 2){
return false;
}
if($this->is_read === 1){
$this->is_read = 2;
}
return $this->client->recv();
}
public function __destruct()
{
if(!empty($this->client)) $this->client->close();
unset($this->client);
// TODO: Implement __destruct() method.
}
}
... ...
CidrBlock,Protocol,Port,Action,FirewallRuleDescription
0.0.0.0/0,TCP,10000-10200,ACCEPT,邮件代理端口
0.0.0.0/0,TCP,20053,ACCEPT,bt面板
0.0.0.0/0,TCP,8888,ACCEPT,宝塔Linux面板默认端口
0.0.0.0/0,TCP,8015,ACCEPT,网站
0.0.0.0/0,TCP,22,ACCEPT,Linux SSH登录
\ No newline at end of file
... ...
<?php
error_reporting(E_ALL);
ini_set("display_errors","off");
/**
* TODO:: 这个文件请用root执行
* 这个文件请加ip白名单访问
*/
function sign(){
return md5('fob.guaidong007'.date('ym').$_REQUEST['rand_str'].'=');
}
if($_SERVER['AUTH_VERIFY'] != sign()){
http_response_code(500);
echo 'auth error';
exit(1);
}
$body = file_get_contents("php://input");
$body = base64_decode($body);
$body = json_decode($body, true);
define('CONF_FILNAME','/www/server/panel/vhost/nginx/tcp/mail.conf');
/**
* 保存配置
* @param $body
* @return int
* @author:dc
* @time 2025/4/14 22:54
*/
//server {
// listen 10087; # 监听的端口
// proxy_pass smtp.gmail.com:465;
//}
function saveconfig($body){
$config = [];
foreach ($body['proxys'] as $port=>$domain){
$config[] = "server {
listen {$port};
proxy_pass {$domain};
}";
}
$config = implode("\n",$config);
if(@file_put_contents(CONF_FILNAME,$config)){
return 200;
}
return 500;
}
/**
* 重启nginx服务
* @return int
* @author:dc
* @time 2025/4/14 22:54
*/
function reload(){
exec("nginx -s reload",$ret,$code);
return $code===0?200:500;
}
/**
* 检查端口监听状态
* @param $body
* @author:dc
* @time 2025/4/14 22:53
*/
function check_prot($body){
$ports = $body['prots'];
foreach ($ports as $port){
exec("netstat -ano | grep :$port",$ret,$code);
echo $port;
echo ":";
if(count($ret)>=1){
echo "1";
}else{
echo "0";
}
echo "\n";
}
}
echo call_user_func($body['action'],$body);
return 0;
\ No newline at end of file
... ...
<?php
require_once "Auth.php";
require_once "ImapClientSwoole.php";
class ProxyService
{
/**
* 连接数
* @var ImapClient[]
*/
protected static $clients = [];
/**
* @author:dc
* @time 2025/3/29 14:34
*/
public function run()
{
//创建Server对象,监听 127.0.0.1:9501 端口。
$server = new Swoole\Server('0.0.0.0', 9527, SWOOLE_PROCESS, SWOOLE_SOCK_TCP);
//监听连接进入事件。
$server->on('Connect', function ($server, $fd) {});
//监听数据接收事件。
$server->on('Receive', function ($server, $fd, $reactor_id, $data) {
// 建立连接
if (empty(self::$clients[$fd])) {
try {
$auth = new Auth($data);
}catch (Throwable $e){
$server->send($fd, $e->getMessage());
$server->close($fd,true);
return;
}
// 创建一个客户端
self::$clients[$fd] = new ImapClientSwoole($auth->host);
// 连接客户端
try {
self::$clients[$fd]->open($auth->out_ip, $auth->timeOut);
}catch (Throwable $e){
$server->send($fd, $auth->tag . ' BAD ' . $e->getMessage()."\r\n");
$server->close($fd,true);
return;
}
// 发送成功消息
$server->send($fd, $auth->tag . " OK The proxy server is successfully connected.\r\n");
// 读取数据
while (1){
if (empty(self::$clients[$fd])){
break;
}
$line = self::$clients[$fd]->readLine();
if($line){
echo '读取到 => '.$line;
$server->send($fd,$line);
}else{
co::sleep(0.1);
}
}
} // 正式请求转发
else {
// 没有连接成功
if(empty(self::$clients[$fd])){
$server->send($fd, " No proxy server.\r\n");
$server->close($fd,true);
return;
}
// 请求数据
$writeNumber = self::$clients[$fd]->write($data);
list($tag,$cmd) = explode(' ',$data,2);
// 退出命令就不等待服务器了
if(trim($cmd) == 'LOGOUT'){
$server->send($fd, $tag." OK bye.\r\n");
$server->close($fd,true);
return;
}
if($writeNumber){
echo '成功写入 => '.$data;
}else{
echo '写入失败 => '.$data;
$server->send($fd,$tag.' BAD '.$writeNumber."\r\n");
}
}
});
//监听连接关闭事件。
$server->on('Close', function ($server, $fd) {
echo '连接关闭了 => '.$fd."\n";
// 关闭并释放资源
self::$clients[$fd] = null;
unset(self::$clients[$fd]);
});
//启动服务器
$server->start();
}
}
(new ProxyService())->run();
\ No newline at end of file
... ...
<?php
require_once "Auth.php";
require_once "SmtpClient.php";
class ProxyService
{
/**
* 连接数
* @var SmtpClient[]
*/
protected static $clients = [];
protected function push(...$params){
echo co::getCid()." => ";
echo implode(' => ',$params);
return $params;
}
/**
* @author:dc
* @time 2025/3/29 14:34
*/
public function run()
{
//创建Server对象,监听 127.0.0.1:9501 端口。
$server = new Swoole\Server(
'0.0.0.0', 9527,
SWOOLE_BASE,
SWOOLE_SOCK_TCP//|SWOOLE_SSL
);
//监听连接进入事件。
$server->on('Connect', function ($server, $fd) {
$server->send($fd, "220 proxy client ok\r\n");
});
//监听数据接收事件。
$server->on('Receive', function ($server, $fd, $reactor_id, $data) {
// echo "in ".co::getCid()." ==> ".$data."\n";
// 建立连接
if (empty(self::$clients[$fd])) {
try {
$auth = new Auth($data);
}catch (Throwable $e){
$server->send($fd, $e->getMessage());
$server->close($fd,true);
return;
}
// 创建一个客户端
self::$clients[$fd] = new SmtpClient($auth->host);
// 连接客户端
try {
self::$clients[$fd]->open($auth->out_ip, $auth->timeOut);
}catch (Throwable $e){
$server->send($fd, '500 ' . $e->getMessage()."\r\n");
$server->close($fd,true);
return;
}
$line = self::$clients[$fd]->readLine();
if($line) $server->send($fd,$line);
// 发送成功消息
// $server->send($fd, "200 OK The proxy server is successfully connected.\r\n");
} // 正式请求转发
else {
// 没有连接成功
if(empty(self::$clients[$fd])){
$server->send($fd, "500 No proxy server.\r\n");
$server->close($fd,true);
return;
}
// 请求数据
self::$clients[$fd]->write($data);
$line = self::$clients[$fd]->readLine();
// echo 'out '.co::getCid()." => ".$line;
if($line!==false) $server->send($fd,$line);
}
});
//监听连接关闭事件。
$server->on('Close', function ($server, $fd) {
// echo '连接关闭了 => '.$fd."\n";
// 关闭并释放资源
self::$clients[$fd] = null;
unset(self::$clients[$fd]);
});
//启动服务器
$server->start();
}
}
(new ProxyService())->run();
\ No newline at end of file
... ...
<?php
require_once "../vendor/autoload.php";
print_r(\Lib\Mail\MailFun::sendEmail([
'tos' => [['email'=>'200582249@qq.com']],
'subject' => 'xt',
'body' => '测试'
],[
'smtp' => 'test.mailhagro.com:25',
// 'proxy' => '64.34.217.96:51395',
'email' => 'ma@mailhagro.com',
'password' => base64_encode('aB12345678'),
],true));
exit();
$a = \Lib\Imap\ImapPool::get(
(new \Lib\Imap\ImapConfig())
->setEmail('200582249@qq.com')
->setPassword('ztqxrjckdzmccahc')
->setHost('imap.qq.com')
->proxy('tcp://104.221.245.130:9527')
->debug()
);
$a->login()->isOk();
\ No newline at end of file
... ...
... ... @@ -39,33 +39,41 @@ class MailProxy {
'smtp.gmail.com:465' => '10087',
'imap.gmail.com:993' => '10086',
'imap-mail.outlook.com:993' => '10088',
'smtp-mail.outlook.com:465' => '10089',
'imap.mail.yahoo.com:993' => '10092',
'smtp.mail.yahoo.com:465' => '10093',
'imaphz.qiye.163.com:993' => '10080',
'smtphz.qiye.163.com:465' => '10081',
'imap.qiye.aliyun.com:993' => '10094',
'smtp.qiye.aliyun.com:465' => '10095',
// 'imap.qq.com:993' => '10096',
// 'smtp.qq.com:465' => '10097',
];
/**
* 代理服务器 地址
* @var string[]
*/
protected $proxyService = [
public static $proxyService = [
'43.134.162.250', // 这个是新加坡服务器 代理
'119.28.113.113', // 这个是新加坡服务器 代理02
'43.154.117.107', // 这个是 shopk的那台服务器
];
/**
* 读取代理ip 随机打乱
* @return string[]
* @author:dc
* @time 2025/4/15 15:49
*/
public static function getProxy(){
$host = self::$proxyService;
foreach ($host as $k => $v){
if($v =='43.154.117.107'){
unset($host[$k]);
}
}
shuffle($host);
return $host;
}
/**
* 分配到的ip服务
* @var string
... ... @@ -96,8 +104,13 @@ class MailProxy {
$this->defaultIp = $default;
}
// 先分配
// 只分配gmail
if(stripos($smtp,".gmail.com")){
$this->assignEmail();
}else{
$this->assignIp = false;
}
$this->originImapHost = $imap;
... ... @@ -169,7 +182,7 @@ class MailProxy {
$serve = strtolower(empty($host['host']) ? $host['path'] : $host['host']);
// 传入的是代理地址 并且是数据库里面已分配的ip
if(in_array($serve,$this->proxyService)){
if(in_array($serve,$this::$proxyService)){
// 传入的服务器 和 分配的不一致 更新分配的
if($serve != $this->assignIp){
$this->assignIp = $serve;
... ... @@ -186,7 +199,7 @@ class MailProxy {
// 返回不代理
$this->assignIp = $serve;
}else{
if(in_array($this->assignIp,$this->proxyService)){
if(in_array($this->assignIp,$this::$proxyService)){
$port = $this->config[$serve.':'.$port]; // 代理服务器端口
}
}
... ... @@ -208,7 +221,7 @@ class MailProxy {
private function assignEmail(){
$data = db()->throw()->first('select * from mail_proxy where email = "'.$this->email.'"');
// 存在记录 并且 还在服务器列表
if($data && in_array($data['ip'],$this->proxyService)){
if($data && in_array($data['ip'],$this::$proxyService)){
$this->assignIp = $data['ip'];
return ;
}
... ... @@ -238,12 +251,12 @@ class MailProxy {
// 一个月活跃用户
$t = date('Y-m-d H:i:s',strtotime("-30 day"));
foreach ($this->proxyService as $ip){
foreach ($this::$proxyService as $ip){
// shopk的不分配
if($ip=='43.154.117.107'){
continue;
}
$num = db()->count("select count(*) from `mail_proxy` where `status` = 1 and `time` > '{$t}' and `ip` = '{$ip}'");
$num = db()->count("select count(*) from `mail_proxy` where `status` = 1 and `ip` = '{$ip}'");
// 每个ip分配1000个
if($num<1000){
$this->assignIp = $ip;
... ... @@ -301,6 +314,14 @@ class MailProxy {
}
public function toArray(){
return [
'smtp' => $this->getOriginSmtpHost(),
'imap' => $this->getOriginImapHost(),
'proxy_smtp' => $this->getSmtpProxy(),
'proxy_imap' => $this->getImapProxy(),
];
}
}
... ...