rtckit / sip
PHP 编写的 SIP 协议实现
0.7.1
2023-05-22 14:24 UTC
Requires
- php: >=7.4.0
- ext-ctype: *
Requires (Dev)
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^9.5
- symfony/yaml: ^5.3
- vimeo/psalm: ^5.11
Suggests
- ext-hash: Enables RFC 8760 authentication via SHA(-512)-256 hashing
README
PHP SIP 解析/渲染库
符合 RFC 3261 的 PHP 7.4 SIP 解析和渲染库。
快速入门
SIP 消息解析
安装后,您可以直接按照以下方式解析 SIP 消息
/* * $text holds your SIP message as a string, for example * $text = 'REGISTER sip:192.168.0.1 SIP/2.0 /.../'; */ $message = \RTCKit\SIP\Message::parse($text); /* Outputs "RTCKit\SIP\Request" */ echo get_class($message) . PHP_EOL; /* Outputs something similar to: * Protocol version: SIP/2.0 * Request method: REGISTER * Request URI: sip:192.168.0.1 * Via: 192.168.0.2:5050 * Via branch: z9hG4bK.eAV4o0nXr * From scheme: sip * From user: buzz * From host: 192.168.0.1 * From tag: SFJbQ2oWh * To scheme: sip * To user: buzz * To host: 192.168.0.1 * Sequence number: 20 * Call ID: ob0EYyuyC0 */ printf("Protocol version: %s" . PHP_EOL, $message->version); printf("Request method: %s" . PHP_EOL, $message->method); printf("Request URI: %s" . PHP_EOL, $message->uri); printf("Via: %s" . PHP_EOL, $message->via->values[0]->host); printf("Via branch: %s" . PHP_EOL, $message->via->values[0]->branch); printf("From scheme: %s" . PHP_EOL, $request->from->uri->scheme); printf("From user: %s" . PHP_EOL, $request->from->uri->user); printf("From host: %s" . PHP_EOL, $request->from->uri->host); printf("From tag: %s" . PHP_EOL, $request->from->tag); printf("To scheme: %s" . PHP_EOL, $request->to->uri->scheme); printf("To user: %s" . PHP_EOL, $request->to->uri->user); printf("To host: %s" . PHP_EOL, $request->to->uri->host); printf("Sequence number: %s" . PHP_EOL, $message->cSeq->sequence); printf("Call ID: %s" . PHP_EOL, $message->callId->value);
SIP 消息渲染
渲染是解析的反操作;例如,让我们为 REGISTER
请求准备一个 200 OK
响应
$response = new \RTCKit\SIP\Response; $response->version = 'SIP/2.0'; $response->code = 200; $response->via = new \RTCKit\SIP\Header\ViaHeader; $response->via->values[0] = new \RTCKit\SIP\Header\ViaValue; $response->via->values[0]->protocol = 'SIP'; $response->via->values[0]->version = '2.0'; $response->via->values[0]->transport = 'UDP'; $response->via->values[0]->host = '192.168.0.2:5050'; $response->via->values[0]->branch = 'z9hG4bK.eAV4o0nXr'; $response->from = new \RTCKit\SIP\Header\NameAddrHeader; $response->from->uri = new \RTCKit\SIP\URI; $response->from->uri->scheme = 'sip'; $response->from->uri->user = 'buzz'; $response->from->uri->host = '192.168.0.1'; $response->from->tag = 'SFJbQ2oWh'; $response->to = new \RTCKit\SIP\Header\NameAddrHeader; $response->to->uri = new \RTCKit\SIP\URI; $response->to->uri->scheme = 'sip'; $response->to->uri->user = 'buzz'; $response->to->uri->host = '192.168.0.1'; $response->to->tag = '8cQtUyH6N5N9K'; $response->cSeq = new \RTCKit\SIP\Header\CSeqHeader; $response->cSeq->sequence = 20; $response->cSeq->method = 'REGISTER'; $response->callId = new \RTCKit\SIP\Header\CallIdHeader; $response->callId->value = 'ob0EYyuyC0'; $response->maxForwards = new \RTCKit\SIP\Header\ScalarHeader; $response->maxForwards->value = 70; $response->contact = new \RTCKit\SIP\Header\ContactHeader; $response->contact->values[0] = new \RTCKit\SIP\Header\ContactValue; $response->contact->values[0]->uri = new \RTCKit\SIP\URI; $response->contact->values[0]->uri->scheme = 'sip'; $response->contact->values[0]->uri->user = 'buzz'; $response->contact->values[0]->uri->host = '192.168.0.2'; $response->contact->values[0]->uri->port = 5050; $response->contact->values[0]->uri->transport = 'udp'; $response->contact->values[0]->expires = 3600; $response->userAgent = new \RTCKit\SIP\Header\Header; $response->userAgent->values[0] = 'MyDeskPhone/1.0.0'; /* Outputs: * SIP/2.0 200 OK * Via: SIP/2.0/UDP 192.168.0.2:5050;branch=z9hG4bK.eAV4o0nXr * From: <sip:buzz@192.168.0.1>;tag=SFJbQ2oWh * To: <sip:buzz@192.168.0.1>;tag=8cQtUyH6N5N9K * Contact: <sip:buzz@192.168.0.2:5050;transport=udp>;expires=3600 * Call-ID: ob0EYyuyC0 * CSeq: 20 REGISTER * Max-Forwards: 70 * User-Agent: MyDeskPhone/1.0.0 */ echo $response->render();
SIP 消息流解析
如果您的用例涉及连续数据流而不是单个消息,则 StreamParser
类可以帮助您;这对于分析 SIP 跟踪文件或数据包捕获特别有用,例如解析 TCP 上的 SIP 流量等。
/* Instantiate the Stream Parser */ $parser = new \RTCKit\SIP\StreamParser; $fp = fopen(/.../); while (!feof($fp)) { $bytes = fread($fp, 256); /* The actual input string ($bytes) can be retrieved from any stream-like source */ if ($parser->process($bytes, $messages) === \RTCKit\SIP\StreamParser::SUCCESS) { foreach ($messages as $message) { /* * $message is either a Request or a Response object, using * the same structure as messages returned by Message::parse() */ } } }
最后,提供的 示例 是一个很好的起点。
要求
RTCKit\SIP 与 PHP 7.4+ 兼容,没有外部库和扩展依赖。
安装
您可以使用 Composer 将库作为项目依赖项添加
composer require rtckit/sip
如果您只在开发期间需要库,例如在测试套件中使用,则应将其添加为仅开发依赖项
composer require --dev rtckit/sip
测试
要运行测试套件,请克隆此存储库,然后使用 Composer 安装依赖项
composer install
然后,转到项目根目录并运行
php -d memory_limit=-1 ./vendor/bin/phpunit -c ./etc/phpunit.xml.dist
静态分析
为了确保高代码质量,RTCKit\SIP 使用 PHPStan 和 Psalm
php -d memory_limit=-1 ./vendor/bin/phpstan analyse -c ./etc/phpstan.neon -n -vvv --ansi --level=max src php -d memory_limit=-1 ./vendor/bin/psalm --config=./etc/psalm.xml
许可
MIT,请参阅 LICENSE 文件。
致谢
- SIP 协议贡献者/IETF Trust
- PROTOS SIP 测试材料 - 芬兰奥卢大学安全编程小组
- lioneagle/sipparser 测试材料 (MIT 许可证)
贡献
可以通过 问题跟踪器 提交错误报告(以及小型补丁)。对于重大补丁,建议您分支存储库并提交拉取请求。