海运的博客

php协程异步扩展swoole使用

发布时间:January 17, 2021 // 分类:PHP // No Comments

ubuntu 20.04 php7.4编译安装swoole:

apt install php-cli php-dev libcurl4-openssl-dev php-curl libc-ares-dev
wget https://github.com/swoole/swoole-src/archive/v4.8.13.tar.gz
tar zxf v4.8.13.tar.gz
cd swoole-src-4.8.13
phpize
./configure --enable-openssl --enable-http2 --enable-swoole-curl --enable-cares
make && make install
echo 'extension=swoole.so' > /etc/php/7.4/cli/conf.d/20-swoole.ini
php --ri swoole
#减小swoole.so文件的大小
strip -s /usr/lib/php/20190902/swoole.so

debian12安装swoole5:

apt install php-cli php-dev libcurl4-openssl-dev php-curl libc-ares-dev libbrotli-dev
wget https://github.com/swoole/swoole-src/archive/refs/tags/v5.1.4.tar.gz
./configure --enable-openssl --enable-swoole-curl --enable-cares

swoole原生协程http客户端:

<?php
//Co::set(['hook_flags' => SWOOLE_HOOK_ALL]);
$count = 10;
Co\run(function () use (&$result, $count) {
  $wg = new \Swoole\Coroutine\WaitGroup();
  $result = [];
  for ($i = 1; $i <= $count; $i++) {
    $wg->add();
    go(function () use ($i, $wg, &$result) {
      $cli = new Swoole\Coroutine\Http\Client('www.baidu.com', 80);
      $cli->set(['timeout' => 10]);
      $cli->setHeaders([
        'Host' => 'www.baidu.com',
        'User-Agent' => 'Mozilla/5.0 Firefox/78.0',
      ]);
      $cli->get('/');
      $result[$i] = $cli->getStatusCode();
      $cli->close();
      $wg->done();
    });
  }
  $wg->wait();
});
var_dump($result);

以hook方式协程运行php curl:

<?php
//Co::set(['hook_flags' => SWOOLE_HOOK_ALL]);
Co\run(function () {
  $chan = new Swoole\Coroutine\Channel(10);
  for ($i = 1; $i <= 10; $i++) {
    go(function () use ($i, $chan) {
      $header = array(
        'User-Agent: Mozilla/5.0 Firefox/78.0'
      );
      $ch = curl_init();
      curl_setopt($ch, CURLOPT_URL, "http://www.baidu.com");
      curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
      curl_exec($ch);
      $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
      curl_close($ch);
      $chan->push(['index' => $i, 'code' => $code]);
    });
  }
  for ($i = 1; $i <= 10; $i++) {
    $res = $chan->pop();
    var_dump($res);
  }
});

golang/php使用aes加密文件

发布时间:April 28, 2020 // 分类:PHP // 1 Comment

兼容于golang语言版本:https://github.com/eliben/code-for-blog/blob/master/2019/aes-encrypt-file/aes-file.go
使用aes-256-cbc加密,加密文件前8字节为原始文件大小数字,跟着是16字节随机字节用作iv,再跟着是先补全的明文加密内容。

<?php
function decrypt($key, $infilename, $outfilename = "") {
  if (!$outfilename) {
    $outfilename = $infilename.'.dec';
  }
  $cipher_str = file_get_contents($infilename);
  if (!$cipher_str) {
    die("empty file".PHP_EOL);
  }
  $file_size = substr($cipher_str, 0, 8);
  $file_size = unpack("P", $file_size);
  $iv = substr($cipher_str, 8, 16);
  $cipher_str = substr($cipher_str, 24);
  $plain_str = openssl_decrypt($cipher_str, "aes-256-cbc", $key, 3, $iv);
  $plain_str = substr($plain_str, 0, $file_size[1]);
  file_put_contents($outfilename, $plain_str);
  echo "decrypted output file ".$outfilename.PHP_EOL;
}
function encrypt($key, $infilename, $outfilename) {
  if (!$outfilename) {
    $outfilename = $infilename.'.enc';
  }
  $plain_str = file_get_contents($infilename);
  if (!$plain_str) {
    die("empty file".PHP_EOL);
  }
  $file_size = strlen($plain_str);
  $file_size = pack("P", $file_size);
  $iv = random_bytes(16);
  if (strlen($plain_str) % 16 != 0) {
    $bytesToPad = 16 - (strlen($plain_str) % 16);
    $plain_str = $plain_str . random_bytes($bytesToPad);
  }
  $cipher_str = openssl_encrypt($plain_str, "aes-256-cbc", $key, 3, $iv);
  $cipher_str = $file_size.$iv.$cipher_str;
  file_put_contents($outfilename, $cipher_str);
  echo "encrypted output file ".$outfilename.PHP_EOL;
}
$arg = getopt('e::d::k:i:o:');
if (isset($arg['i']) && !empty($arg['i'])) {
  $infilename = $arg['i'];
} else {
  die("please input filename".PHP_EOL);
}
if (isset($arg['o']) && !empty($arg['o'])) {
  $outfilename = $arg['o'];
} else {
  $outfilename = "";
}
if (isset($arg['k']) && !empty($arg['k'])) {
  $key = $arg['k'];
} else {
  $key = "pass";
}
$key = hash('sha256', $key, true);
if (isset($arg['d'])) {
  decrypt($key, $infilename, $outfilename);
} elseif (isset($arg['e'])) {
  encrypt($key, $infilename, $outfilename);
}

golang cbc使用pkcs7补全版:

package main

import (
        "bytes"
        "crypto/aes"
        "crypto/cipher"
        "crypto/rand"
        "crypto/sha256"
        //"encoding/binary"
        "flag"
        "fmt"
        "io/ioutil"
        "log"
        "os"
)
func pkcs7strip(data *[]byte, blockSize int) (int, error) {
        length := len(*data)
        if length == 0 {
                return 0, fmt.Errorf("pkcs7: Data is empty")
        }
        if length%blockSize != 0 {
                return 0, fmt.Errorf("pkcs7: Data is not block-aligned")
        }
        padLen := int((*data)[length-1])
        ref := bytes.Repeat([]byte{byte(padLen)}, padLen)
        if padLen > blockSize || padLen == 0 || !bytes.HasSuffix(*data, ref) {
                return 0, fmt.Errorf("pkcs7: Invalid padding")
        }
        return length-padLen, nil
}

func pkcs7pad(data *[]byte, blockSize int) error {
        if blockSize < 0 || blockSize > 256 {
                return fmt.Errorf("pkcs7: Invalid block size %d", blockSize)
        } else {
                padLen := blockSize - len(*data) % blockSize
                padding := bytes.Repeat([]byte{byte(padLen)}, padLen)
                *data = append(*data, padding...)
                return nil
        }
}

func encryptFile(key []byte, filename string, outFilename string) (string, error) {
        if len(outFilename) == 0 {
                outFilename = filename + ".enc"
        }

        plaintext, err := ioutil.ReadFile(filename)
        if err != nil {
                return "", err
        }

        of, err := os.Create(outFilename)
        if err != nil {
                return "", err
        }
        defer of.Close()

        if  err := pkcs7pad(&plaintext, aes.BlockSize) ; err != nil {
                return "", err
        }

        iv := make([]byte, aes.BlockSize)
        if _, err := rand.Read(iv); err != nil {
                return "", err
        }
        if _, err = of.Write(iv); err != nil {
                return "", err
        }

        ciphertext := make([]byte, len(plaintext))

        block, err := aes.NewCipher(key)
        if err != nil {
                return "", err
        }
        mode := cipher.NewCBCEncrypter(block, iv)
        mode.CryptBlocks(ciphertext, plaintext)

        if _, err = of.Write(ciphertext); err != nil {
                return "", err
        }
        return outFilename, nil
}

func decryptFile(key []byte, filename string, outFilename string) (string, error) {
        if len(outFilename) == 0 {
                outFilename = filename + ".dec"
        }

        ciphertext, err := ioutil.ReadFile(filename)
        if err != nil {
                return "", err
        }

        of, err := os.Create(outFilename)
        if err != nil {
                return "", err
        }
        defer of.Close()

        buf := bytes.NewReader(ciphertext)
        iv := make([]byte, aes.BlockSize)
        if _, err = buf.Read(iv); err != nil {
                return "", err
        }

        paddedSize := len(ciphertext) - aes.BlockSize
        if paddedSize%aes.BlockSize != 0 {
                return "", fmt.Errorf("want padded plaintext size to be aligned to block size")
        }
        plaintext := make([]byte, paddedSize)

        block, err := aes.NewCipher(key)
        if err != nil {
                return "", err
        }
        mode := cipher.NewCBCDecrypter(block, iv)
        mode.CryptBlocks(plaintext, ciphertext[aes.BlockSize:])

        origSize, err := pkcs7strip(&plaintext, aes.BlockSize)
        if err != nil {
          return "", err
        }
        if _, err := of.Write(plaintext[:origSize]); err != nil {
                return "", err
        }
        return outFilename, nil
}

func main() {
        var keyFlag string
        encFlag := flag.Bool("e", false, "encrypt")
        decFlag := flag.Bool("d", false, "decrypt")
        flag.StringVar(&keyFlag, "k", "password", "encrypt password")

        flag.Parse()
        filename := flag.Arg(0)

        key := sha256.Sum256([]byte(keyFlag))
        fmt.Println("use password", keyFlag)
        //fmt.Println(key)
        if *encFlag {
                outFilename, err := encryptFile(key[:], filename, "")
                if err != nil {
                        log.Fatal(err)
                }
                fmt.Println("Encrypted output file:", outFilename)
        } else if *decFlag {
                outFilename, err := decryptFile(key[:], filename, "")
                if err != nil {
                        log.Fatal(err)
                }
                fmt.Println("Decrypted output file:", outFilename)
        } else {
                fmt.Println(flag.Usage)
                os.Exit(1)
        }
}

golang使用aes cfb:

package main

import (
        //"bytes"
        "crypto/aes"
        "crypto/cipher"
        "crypto/rand"
        "crypto/sha256"
        //"encoding/binary"
        "flag"
        "fmt"
        "io"
        "io/ioutil"
        "log"
        "os"
)

func encryptFile(key []byte, filename string, outFilename string) (string, error) {
        if len(outFilename) == 0 {
                outFilename = filename + ".enc"
        }

        plaintext, err := ioutil.ReadFile(filename)
        if err != nil {
                return "", err
        }

        of, err := os.Create(outFilename)
        if err != nil {
                return "", err
        }
        defer of.Close()

        block, err := aes.NewCipher(key)
        if err != nil {
                panic(err)
        }

        ciphertext := make([]byte, aes.BlockSize+len(plaintext))
        iv := ciphertext[:aes.BlockSize]
        if _, err := io.ReadFull(rand.Reader, iv); err != nil {
                panic(err)
        }

        stream := cipher.NewCFBEncrypter(block, iv)
        stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)

        fmt.Printf("iv:\t%x\n", iv)
        fmt.Printf("cipher text:\t%x\n", ciphertext)
        if _, err = of.Write(ciphertext); err != nil {
                return "", err
        }
        return outFilename, nil
}

func decryptFile(key []byte, filename string, outFilename string) (string, error) {
        if len(outFilename) == 0 {
                outFilename = filename + ".dec"
        }

        ciphertext, err := ioutil.ReadFile(filename)
        fmt.Printf("cipher text:\t%x\n", ciphertext)
        if err != nil {
                return "", err
        }

        of, err := os.Create(outFilename)
        if err != nil {
                return "", err
        }
        defer of.Close()

        block, err := aes.NewCipher(key)
        if err != nil {
                panic(err)
        }

        iv := ciphertext[:aes.BlockSize]
        ciphertext = ciphertext[aes.BlockSize:]
        fmt.Printf("cipher2 text:\t%x\n", ciphertext)
        fmt.Printf("iv:\t%x\n", iv)

        stream := cipher.NewCFBDecrypter(block, iv)

        stream.XORKeyStream(ciphertext, ciphertext)

        if _, err := of.Write(ciphertext); err != nil {
                return "", err
        }
        return outFilename, nil
}

func main() {
        var keyFlag string
        encFlag := flag.Bool("e", false, "encrypt")
        decFlag := flag.Bool("d", false, "decrypt")
        flag.StringVar(&keyFlag, "k", "password", "encrypt password")

        flag.Parse()
        filename := flag.Arg(0)

        key := sha256.Sum256([]byte(keyFlag))
        fmt.Println("use password", keyFlag)
        //fmt.Println(key)
        if *encFlag {
                outFilename, err := encryptFile(key[:], filename, "")
                if err != nil {
                        log.Fatal(err)
                }
                fmt.Println("Encrypted output file:", outFilename)
        } else if *decFlag {
                outFilename, err := decryptFile(key[:], filename, "")
                if err != nil {
                        log.Fatal(err)
                }
                fmt.Println("Decrypted output file:", outFilename)
        } else {
                fmt.Println(flag.Usage)
                os.Exit(1)
        }
}

php ssh/expect登录服务器执行命令

发布时间:April 21, 2018 // 分类:PHP // No Comments

<?php
$conn = ssh2_connect('1.1.1.1', 22);
if (!$conn) {
  die("conn fail\n");
}
if (ssh2_auth_password($conn, 'root', 'password')) {
  echo "auth sus\n";
} else {
  die("auth fail\n");
}
$stream = ssh2_exec($conn, "df  --output=avail /|tail -n 1");  
stream_set_blocking($stream, true);  
$res = trim(stream_get_contents($stream));
var_dump($res);

php使用ssh交互式执行命令:

<?php
$host = '192.168.1.1';
$port = 2222;
$pass = 'xxxx';
if (!($conn = ssh2_connect($host, $port, array('hostkey'=>'ssh-rsa')))) {
  die("conn fail\n");
}
//注意路径不要使用~/.ssh/id_rsa.pub,会遇到段错误和其它莫名其妙的问题
if (ssh2_auth_pubkey_file($conn, 'root', '/root/.ssh/id_rsa.pub', '/root/.ssh/id_rsa')) {
  echo "auth sus\n";
} else {
  die("auth fail\n");
}
function expect($stream, $match) {
  $time = time();
  $res = '';
  while(!feof($stream)){
    //if (($buffer = fgets($stream, 4096)) !== false) {
    if (($buffer = fread($stream, 4096)) !== false) {
      $res .= $buffer;
    }
    if (stristr($res, $match)) {
      return 'sus';
    }
    $now = time();
    if (($now - $time) >= 10) {
      return 'timeout';
    }
    usleep(100);
  }
  return 'disconnect';
}

$shell=ssh2_shell($conn, 'xterm');
fwrite($shell, "/usr/bin/cryptroot-unlock\n");
$res = expect($shell, 'Please unlock disk');
if ($res == 'sus') {
  fwrite($shell, "{$pass}\n");
  $res = expect($shell, 'set up successfully');
  if ($res == 'sus') {
  }
  var_dump($res);
}

php也可安装expect扩展调用ssh命令交互式执行命令:

apt install php-dev tcl-dev tcl-expect-dev
wget https://pecl.php.net/get/expect-0.4.0.tgz
tar zxvf expect-0.4.0.tgz 
cd expect-0.4.0/
 phpize
./configure
make && make install
echo 'extension=expect.so' > /etc/php/7.4/cli/conf.d/20-expect.ini
php -m|grep expect

make时如果出现错误php_expect.h:34:10: fatal error: expect_tcl.h: 没有那个文件或目录:

sed -i 's/^INCLUDES =/INCLUDES = -I\/usr\/include\/tcl8.6/' Makefile

php使用expect连接ssh执行命令:

<?php
ini_set("expect.timeout", 2);
ini_set("expect.loguser", "off");

$stream = expect_popen("ssh -o StrictHostKeyChecking=no -p 22 root@www.haiyun.me");
$cases = array(
  array("password:", "password"),
  array("Last login", "shell"),
  array("yes/no)?",  "yes/no")
);

while (true) {
  switch (expect_expectl($stream, $cases)) {
  case "password":
    fwrite($stream, "password\n");
    break;
  case "yes/no":
    fwrite($stream, "yes\n");
    break;
  case "shell":
    fwrite($stream, "uptime\n");
    break;
  case EXP_TIMEOUT:
  case EXP_EOF:
    break 2; 
  default:
    die("Error has occurred!");
  }
}
fclose ($stream);

PHP多多线程pthreads数据共享

发布时间:June 19, 2015 // 分类:PHP // No Comments

<?php
class sharr extends Stackable {
  public function run(){}
}

class shvar extends Stackable  {
  public $num = 1;

}

class my extends Thread {

  public function __construct($sharr, $shvar) {
    $this->sharr = $sharr; 
    $this->shvar = $shvar; 
  }

  public function run(){
    $i = 0;
    while(++$i < 10) {
      $this->sharr[]=rand(0,1000);
      $this->shvar->num++;
    }
    var_dump($this->sharr);
    var_dump($this->shvar->num);
  } 
}

$sharr = new sharr();
$shvar = new shvar();
$my = new my($sharr, $shvar);
$my->start();
$my->join();

var_dump($sharr);
var_dump($shvar->num);
?>

PHP重新动态编译Curl扩展添加异步DNS支持c-ares

发布时间:January 30, 2015 // 分类: // No Comments

pycurl支持异步DNS支持c-ares类似,请先确定Libcurl是否已支持异步DNS解析c-ares,如不支持可升级libcurl支持异步DNS解析c-ares
理论上的libcurl更新添加支持异步DNS解析后,将库文件通过ldconfig添加到系统动态库,如果大版本号和之前版本相同,可以不用重新编译Php curl扩展已支持异步DNS,因为PHP curl依赖libcurl会自动选择版本较高的lib。
如查看系统共享库中的Libcurl:

ldconfig -p|grep curl
        libcurl.so.4 (libc6,x86-64) => /usr/local/curl/lib/libcurl.so.4
        libcurl.so.4 (libc6,x86-64) => /usr/lib64/libcurl.so.4
        libcurl.so (libc6,x86-64) => /usr/local/curl/lib/libcurl.so
        libcurl.so (libc6,x86-64) => /usr/lib64/libcurl.so

然后查看php curl扩展的共享库依赖,可见已自动选择新编译的libcurl.so.4:

ldd /usr/lib64/php/modules/curl.so|grep curl
        libcurl.so.4 => /usr/local/curl/lib/libcurl.so.4 (0x00007f406f0f9000)

不过为了稳定可以重新编译下php curl扩展,本文PHPyum安装,如果是源码安装的可参考:PHP动态编译添加IMAP模块支持
查看已安装的PHP版本,并下

php -v
PHP 5.5.21 (cli) (built: Jan 21 2015 15:35:14) 
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2014 Zend Technologies

下载相应版本的源码并重新编译curl扩展:

yum install php-devel
wget https://github.com/php/php-src/archive/php-5.5.21.tar.gz
tar zxvf php-5.5.21.tar.gz 
cd php-src-php-5.5.21/ext/curl/
phpize 
./configure --with-curl=/usr/local/curl

查看当前PHP CURL是否已支持异步DNS支持:

php -i|grep AsynchDNS
AsynchDNS => Yes
分类
最新文章
最近回复
  • opnfense: 谢谢博主!!!解决问题了!!!我之前一直以为内置的odhcp6就是唯一管理ipv6的方式
  • liyk: 这个方法获取的IPv6大概20分钟之后就会失效,默认路由先消失,然后Global IPV6再消失
  • 海运: 不好意思,没有。
  • zongboa: 您好,請問一下有immortalwrt設定guest Wi-Fi的GUI教學嗎?感謝您。
  • 海运: 恩山有很多。
  • swsend: 大佬可以分享一下固件吗,谢谢。
  • Jimmy: 方法一 nghtp3步骤需要改成如下才能编译成功: git clone https://git...
  • 海运: 地址格式和udpxy一样,udpxy和msd_lite能用这个就能用。
  • 1: 怎么用 编译后的程序在家里路由器内任意一台设备上运行就可以吗?比如笔记本电脑 m参数是笔记本的...
  • 孤狼: ups_status_set: seems that UPS [BK650M2-CH] is ...