作者归档:softsim

nebula vpn

https://github.com/slackhq/nebula
https://github.com/DefinedNet/mobile_nebula

Nebula is a scalable overlay networking tool with a focus on performance, simplicity and security. It lets you seamlessly connect computers anywhere in the world. Nebula is portable, and runs on Linux, OSX, Windows, iOS, and Android. It can be used to connect a small number of computers, but is also able to connect tens of thousands of computers.

Configure Nebula VPN on iOS/Android
https://willhbr.net/2021/06/22/configure-nebula-vpn-on-ios-android/

VPN mesh with Nebula
https://linuxblog.xyz/posts/slack-nebula/

在debian中安装 nebula-lighthouse-service 灯塔服务

UDP/noiseprotocol协议

NaiveProxy

浏览器 → NaiveProxy客户端 ⟶ GFW ⟶ 常用前端(Caddy/HAProxy等) → NaiveProxy服务端 ⟶ 互联网

使用Chrome的网络堆栈,GFW审查截获的流量行为与Chrome和标准前端(如 Caddy、HAProxy)之间的常规 HTTP/2 流量完全相同。前端还会将未经身份验证的用户和活动探测器重新路由到后端HTTP服务器,从而使得无法检测到代理的存在,比如像这样:探查⟶常用前端⟶网站页面。

https://github.com/klzgrad/naiveproxy

0. 安装Go开发环境

 下载
cd ~/
wget "https://go.dev/dl/$(curl https://go.dev/VERSION?m=text).linux-amd64.tar.gz"
解压缩
tar xvaf go1.19.3.linux-amd64.tar.gz
mv go golang
mkdir go
配置环境变量
export GOROOT=$HOME/golang
export GOPATH=$HOME/go
export GOBIN=$GOPATH/bin
export PATH=$GOBIN:$GOROOT/bin:$PATH

1. 安装编译caddy

go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
~/go/bin/xcaddy build --with github.com/caddyserver/forwardproxy@caddy2=github.com/klzgrad/forwardproxy@naive
或
~/go/bin/xcaddy build --with github.com/caddyserver/forwardproxy=github.com/klzgrad/forwardproxy@naive

编译成功后,可以看到当前目录中存在一个名为caddy的文件。
https://github.com/klzgrad/naiveproxy/wiki/%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87 中介绍的方法有点过时

2. 配置caddy
创建一个 config.json

:443, softs.im
tls email@softs.im
route {
        forward_proxy {
                basic_auth username passwd
                hide_ip
                hide_via
                probe_resistance
        }
        file_server {
                root /home/softsim/www
        }
}

更好一点的配置

{
        order forward_proxy before file_server
}
:443, nr5g.softs.im {
        tls /home/softsim/ssl/softsim.cer /home/softsim/ssl/softsim.key

        forward_proxy {
                basic_auth myuser mypasswd
                hide_ip
                hide_via
                probe_resistance
        }

        root * /home/softsim/nr5g
        encode gzip
        php_fastcgi unix//run/php/php8.1-fpm.sock
        file_server
}

lte.softs.im {
        tls /home/softsim/ssl/softsim.cer /home/softsim/ssl/softsim.key
        file_server {
                root /var/lib/transmission-daemon/downloads
        }
}

注意:
(1) 用您的实际电子邮件地址替换 email@softs.im 用来接收 Let’s Encrypt 通知
或者用 文件配置 已经申请好的证书
tls /path/example.com.crt /path/example.com.key

将其格式化

caddy fmt --overwrite /etc/caddy/Caddyfile

注意:不格式化,caddy运行会报错
并且,还得用 /etc/caddy/Caddyfile 这个名字, 用别的文件名会出错

如果想建立多用户版Naive,可以将 forward_proxy 模块复制。将basic_auth中的用户密码修改成另外一个就可以了。

运行

# 以后台的方式启动 caddy
caddy start --config  /etc/caddy/Caddyfile

# 以前台的方式启动 caddy
caddy run --config  /etc/caddy/Caddyfile


caddy stop

3. 编译naiveproxy客户端

git clone --depth 1 https://github.com/klzgrad/naiveproxy.git
cd naiveproxy/src/
./get-clang.sh
./build.sh

编译出的文件为 out/Release/naive

配置naiveproxy 客户端

{
  "listen": "socks://127.0.0.1:1080",
  "proxy": "https://username:passwd@softs.im"
}

保存为np.json
运行

./naive np.json

4. 配置浏览器的 SmartProxy或者SwitchyOmega 来自动匹配要代理的网站
…..

5. 让caddy以systemd 的守护进程运行

mv  caddy  /usr/bin/
setcap cap_net_bind_service=+ep  /usr/bin/caddy

添加 /etc/systemd/system/caddy.service 文件,内容如下

[Unit]
Description=Caddy
Documentation=https://caddyserver.com/docs/
After=network.target network-online.target
Requires=network-online.target

[Service]
User=www-data
Group=www-data
ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/bin/caddy reload --config /etc/caddy/Caddyfile
TimeoutStopSec=5s
LimitNOFILE=1048576
LimitNPROC=512
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target

启用服务

 systemctl daemon-reload
 systemctl enable caddy
 systemctl start caddy

6. 支持一般PHP的配置

        root * /home/softsim/simpleforum
        php_fastcgi unix//run/php/php7.4-fpm.sock
        file_server

tinyfecVPN和udp2raw

https://github.com/wangyu-/tinyfecVPN

tinyfecVPN

工作在VPN方式的双边网络加速工具,可以加速全流量(TCP/UDP/ICMP)。

tinyfecVPN使用了和UDPSpeeder相同的lib,功能和UDPspeeder类似,只不过tinyfecVPN工作方式是VPN,UDPspeeder工作方式是UDP tunnel.

tinyfecVPN是无状态的点对点VPN, 收到包即连接建立成功;即使VPN断线重连,上层承载的连接不掉线。

https://github.com/wangyu-/udp2raw/blob/unified/doc/README.zh-cn.md

udp2raw tunnel,通过raw socket给UDP包加上TCP或ICMP header,进而绕过UDP屏蔽或QoS,或在UDP不稳定的环境下提升稳定性。可以有效防止在使用kcptun或者finalspeed的情况下udp端口被运营商限速。

支持心跳保活、自动重连,重连后会恢复上次连接,在底层掉线的情况下可以保持上层不掉线。同时有加密、防重放攻击、信道复用的功能。

Linux主机,有root权限或cap_net_raw capability.。可以是PC、android手机/平板、openwrt路由器、树莓派。主机上最好安装了iptables命令

最好的办法是买个能刷OpenWrt/LEDE/梅林的路由器,把udp2raw运行在路由器上。

把udp流量伪装成tcp /icmp

用raw socket给udp包加上tcp/icmp包头,可以突破udp流量限制或Udp QOS。或者在udp nat有问题的环境下,提升稳定性。 另外也支持用raw 发udp包,这样流量不会被伪装,只会被加密。
模拟TCP3次握手

模拟TCP3次握手,模拟seq ack过程。另外还模拟了一些tcp option:MSS,sackOk,TS,TS_ack,wscale,用来使流量看起来更像是由普通的linux tcp协议栈发送的。
心跳保活、自动重连,连接恢复

心跳保活、自动重连,udp2raw重连可以恢复上次的连接,重连后上层连接继续有效,底层掉线上层不掉线。有效解决上层连接断开的问题。 (功能借鉴自kcptun-raw)(就算你拔掉网线重插,或者重新拨号获得新ip,上层应用也不会断线)
================

https://github.com/wangyu-/tinyfecVPN/wiki/%E4%BD%BF%E7%94%A8%E7%BB%8F%E9%AA%8C

用udp2raw防止tinyfecVPN被限速或断流

udp2raw可以把udp流量伪装成tcp,防止udp被运营商限速或断流。即使发生断流,udp2raw可以检测到这种情况并自动帮你重连。

推荐tinyfecVPN和udp2raw一起使用。连接方式:

tinyfecVPN client—->udp2raw client—————>udp2raw server—->tinyfecVPN server
=========================

n2n和omniedge

https://github.com/ntop/n2n
https://github.com/switch-iot/hin2n/blob/dev/README_zh.md

https://github.com/omniedgeio/omniedge (类似于nebula和headscale之类的东西)

debian bookworm安装配置Kamailio

bookworm上的kamailio很新,5.6.2版本

1. 先安装MariaDB

apt install default-mysql-server

2. 切换到root用户, 执行

#  mysql -u root -p

然后在 mysql 的shell里修改 mysql数据库的root密码

ALTER USER 'root'@'localhost' IDENTIFIED BY 'new_password';

因为配置Kamailio时,要用到mysql 的root用户密码

3. 安装 Kamailio

apt install kamailio kamailio-mysql-modules

4. 配置
配置文件 在 /etc/kamailio/ 目录下,有两个
一个是 /etc/kamailio/kamctlrc, 里面的配置信息,主要供 kamctl 和 kamdbctl 这两个工具命令使用

SIP_DOMAIN=
DBENGINE=MYSQL
DBRWUSER=
DBRWPW=
CHARSET="latin1"
---上面5个必须修改----
DBROUSER=
DBROPW=
DBHOST=
DBPORT=

SIP_DOMAIN可以是域名 或者 IP地址

另外一个是 /etc/kamailio/kamailio.cfg, 是供 kamailio 主程序使用的, 里面有很多 脚本

#!define WITH_MYSQL
#!define WITH_AUTH
#!define WITH_USRLOCDB

#!define DBURL "mysql://kamailio:_NEW_DBRWPW_@localhost/kamailio"

这四个选项必须修改

如果设置了SIP_DOMAIN为域名,但这个域名没有在DNS中解析, 还必须在 kamailio.cfg 设置一个选项

alias=""

5. 初始化数据库和用户

kamdbctl create

这个时候会要求输入mysql的root密码

6. 开启停止服务

systemctl start kamailio
systemctl stop kamailio
systemctl restart kamailio

7. 添加用户

kamctl add userid password
比如
kamctl add  softsimuser  softsimpassword

8. 监听 IPV6地址

listen=udp:10.0.0.10:5060

listen=[proto]:[localip]:[lport] advertise [publicip]:[pport]

如果不设置,只监听本地 ipv4地址
或者使用

auto_bind_ipv6=1

选项

9. 常用管理命令

查看用户
/usr/sbin/kamctl db show subscriber

添加用户
/usr/sbin/kamctl add  softsimuser  softsimpassword

修改用户密码
/usr/sbin/kamctl   passwd  username  passwd

显示在线用户
/usr/sbin/kamctl    online

删除用户
/usr/sbin/kamctl  rm   username


启动Kamalio
/usr/sbin/kamctl  start  

重启Kamalio
/usr/sbin/kamctl  restart 


停止Kamalio
/usr/sbin/kamctl         stop 

STREAM_VOICE_CALL

对 AudioManager.STREAM_VOICE_CALL 音频流类型进行分析

hardware/libhardware_legacy/audio/AudioPolicyManagerBase.cpp
frameworks/base/media/java/android/media/AudioManager.java
frameworks/base/media/java/android/media/AudioService.java
frameworks/base/media/java/android/media/AudioSystem.java

通过音频流的类型获取对应的音频策略

AudioPolicyManagerBase.cpp:

AudioPolicyManagerBase::routing_strategy AudioPolicyManagerBase::getStrategy(AudioSystem::stream_type stream) {
    // stream to strategy mapping
    switch (stream) {
    case AudioSystem::VOICE_CALL:
    case AudioSystem::BLUETOOTH_SCO:
        return STRATEGY_PHONE;
    ...
    }
}

可以, voice call的音频策略是 phone

通过音频策略获取到对应的输出设备

AudioPolicyManagerBase.cpp:

audio_devices_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, bool fromCache) {
    uint32_t device = AUDIO_DEVICE_NONE;
    ... 
    switch (strategy) {
    ... 
    case STRATEGY_PHONE:
        // for phone strategy, we first consider the forced use and then the available devices by order
        // of priority
        switch (mForceUse[AudioSystem::FOR_COMMUNICATION]) { // 判断是否有设置在通讯过程中, 强制使用某种输出设备
        case AudioSystem::FORCE_BT_SCO: // 强制使用蓝牙, 作为输出设备
            if (!isInCall() || strategy != STRATEGY_DTMF) {
                device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
                if (device) break;
            }
            device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
            if (device) break;
            device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_SCO;
            if (device) break;
            // if SCO device is requested but no SCO device is available, fall back to default case
            // FALL THROUGH

        default:    // FORCE_NONE 没有设置通讯过程中, 强制使用某种输出设备
            // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP
            if (mHasA2dp && !isInCall() &&
                    (mForceUse[AudioSystem::FOR_MEDIA] != AudioSystem::FORCE_NO_BT_A2DP) &&
                    (getA2dpOutput() != 0) && !mA2dpSuspended) {
                device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP;
                if (device) break;
                device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
                if (device) break;
            }
            device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
            if (device) break;
            device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_WIRED_HEADSET;
            if (device) break;
            if (mPhoneState != AudioSystem::MODE_IN_CALL) {
                device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_ACCESSORY;
                if (device) break;
                device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_DEVICE;
                if (device) break;
                device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET;
                if (device) break;
                device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_AUX_DIGITAL;
                if (device) break;
                device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET;
                if (device) break;
            }
			// 没有设置通讯过程中, 音频的输出设备, 则默认使用听筒作为输出设备
            device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_EARPIECE;
            if (device) break;
            device = mDefaultOutputDevice;
            if (device == AUDIO_DEVICE_NONE) {
                ALOGE("getDeviceForStrategy() no device found for STRATEGY_PHONE");
            }
            break;

        case AudioSystem::FORCE_SPEAKER: // 强制使用扬声器, 作为输出设备
            // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to
            // A2DP speaker when forcing to speaker output
            if (mHasA2dp && !isInCall() &&
                    (mForceUse[AudioSystem::FOR_MEDIA] != AudioSystem::FORCE_NO_BT_A2DP) &&
                    (getA2dpOutput() != 0) && !mA2dpSuspended) {
                device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
                if (device) break;
            }
            if (mPhoneState != AudioSystem::MODE_IN_CALL) {
                device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_ACCESSORY;
                if (device) break;
                device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_DEVICE;
                if (device) break;
                device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET;
                if (device) break;
                device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_AUX_DIGITAL;
                if (device) break;
                device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET;
                if (device) break;
            }
            device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_SPEAKER;
            if (device) break;
            device = mDefaultOutputDevice;
            if (device == AUDIO_DEVICE_NONE) {
                ALOGE("getDeviceForStrategy() no device found for STRATEGY_PHONE, FORCE_SPEAKER");
            }
            break;
        }
    break;
	// 关于上面default位置的问题, 如果每个case选择(包括default)最终都有break语句, 则default的位置不影响执行顺序
	// 当每个case选择(包括default)不一定有break语句时, 如果输入不满足其他选择, 最终执行default. 程序会从default处从上向下执行, 直到遇到break语句停止
	// 此处的default位置并不影响执行顺序
	... // 省略
    }
    return device;
}

speech codec

G.711 (也称PCM), 用脉冲编码调制对音频采样,采样率为8k每秒。它利用一个 64Kbps 未压缩通道传输语音讯号。 起压缩率为1:2, 即把16位数据压缩成8位。
G.711 标准下主要有两种压缩算法。一种是µ-law, 北美和日本。另一种是A-law, 欧洲等其他区域

1972 年,为了将 模拟通信转向数字通信执行制定的话音编码标准

这个在通信原理的本科课程中都有介绍过。G.711采用一种称为分组丢失隐藏(PLC)的技术来减少丢包带来的实际影响。有效的信号带宽在静默期间通过语音活动检测(VAD)这一过程被减小。

延迟 0.125 ms
mos 4.1


G.729
也是ITU-T定义的音频编码算法,frame 只有10ms,G.729点到点的时延为25ms,G.729基于CELP模式,用CS-ACELP(Conjugate-Structure Algebraic Code Excited Linear Prediction)方法以8Kbps的波特率对语音进行编码。

G.729采用的是共轭结构的代数码激励线性预测算法(Conjugate Structure Algebraic Code Excited Linear Prediction,CS-ACELP),这是一种基于CELP编码模型的算法。由于G.729编码器能够实现很高的语音质量(MOS分3.9, 这个语音质量算很不错了)和很低的算法延时,被广泛地应用于数据通信的各个领域。

G.729是对8KHz采样16bit量化的线性PCM语音信号进行编码,压缩后数据速率为8Kbps,具备16:1的高压缩率。

延迟 15ms
MOS 3.9

G.729 CS-ACELP 8 3.92


G.723
G.723是ITU-T在1996年制订成型的一种多媒体语音编解码标准。G.723编码器采用LPC合成-分析法和感觉加权误差最小化原理编码。G.723标准可在6.3kbps和5.3kbps两种码率下工作。对激励信号进行量化时,高速率(6.3kbps)编码器的激励信号采用多脉冲最大似然量化(MP­-MLQ),低速率(5.3kbps)编码器的激励信号采用代数码本激励线性预测(ACELP)。其中,高码率算法(6.3kbps)具有较高的重建语音质量,而低码率算法(5.3kbps)的计算复杂度则较低。与一般的低码率语音编码算法一样,这里的G.723标准采用的线性预测的合成分析法也就是我们通常所说的Analysis-by-Synthesis。

G.723.1是一个双速率的语音编码器.
编码器首先对语音信号进行传统电话带宽的滤波(基于G.712),再对语音信号用传统8000-Hz速率进行抽样(基于G.711),并变换成16 bit线性PCM码作为该编码器的输入;
在解码器中对输出进行逆操作来重构语音信号;高速率编码器使用多脉冲最大似然量化(MP-MLQ),低速率编码器使用代数码激励线性预测(ACELP)方法,编码器和解码器都必须支持此两种速率,并能够在帧间对两种速率进行转换;
此系统同样能够对音乐和其他音频信号进行压缩和解压缩,但它对语音信号来说是最优的;采用了执行不连续传输的静音压缩,这就意味着在静音期间的比特流中加入了人为的噪声。除了预留带宽之外,这种技术使发信机的调制解调器保持连续工作,并且避免了载波信号的时通时断。

G.723.1 MP-MLQ 6.3kbps 3.9 MOS
G.723.1 ACELP 5.3kbps 3.65 MOS


G.726

Adaptive Differential Pulse Code Modulation (ADPCM),最为常用的方式是 32 kbit/s,但由于其只是 G.711速率的一半,所以可将网络的可利用空间增加了一倍。G.726具体规定了一个 64 kbpsA-law 或 µ-law PCM 信号是如何被转化为40, 32, 24或16 kbps 的 ADPCM 通道的。在这些通道中,24和16 kbps 的通道被用于数字电路倍增设备(DCME)中的语音传输,而40 kbps 通道则被用于 DCME 中的数据解调信号(尤其是4800 kbps 或更高的调制解调器)。
G.726 encoder 输入一般都是G.711 encoder的输出:64kbps A-law or u-law.其算法实质就是一个ADPCM, 自适应量化算法。


Speex 2.15 /44
Minimizes bandwidth usage by using variable bit rate. It is one of the most preferred codecs used in many VoIP apps.


SILK/ Opus


AMR


GSM


Codec2是一个开源的 语音编解码器, 最低450 bit/s
https://github.com/drowe67/codec2

Codec2的主要开发者是David Rowe ,他同时也是另一个开源语音项目Speex 的开发者。Codec2项目允许在只占用极少带宽的情况下提供通话服务,其算法是基于1980年代的论文,并避开了未过期的专利。


VoLTE使用两种语音编码标准:AMR(或AMR-NB)、AMR-WB。


VoNR使用 EVS


twinkle支持的 codec

G.711 A-law (64 kbps payload, 8 kHz sampling rate)
G.711 μ-law (64 kbps payload, 8 kHz sampling rate)
GSM (13 kbps payload, 8 kHz sampling rate)
Speex narrow band (15.2 kbps payload, 8 kHz sampling rate)
Speex wide band (28 kbps payload, 16 kHz sampling rate)
Speex ultra wide band (36 kbps payload, 32 kHz sampling rate)
iLBC (13.3 or 15.2 kbps payload, 8 kHz sampling rate)
G.726 (16, 24, 32 or 40 kbps payload, 8 kHz sampling rate)


Linphone 自己实现了一个 G.729 编解码器

Bcg729 is an open source implementation of both an encoder and decoder for the ITU G729 Annex A/B speech codec. This library was developed by Belledonne Communications, the company supporting the Linphone project. It was written from scratch and is NOT a derivative work of the ITU reference source code of any kind.

The library is written in C 99, is fully portable, and can be executed on many platforms, including ARM and x86 processors. libbcg729 supports concurrent channel encoding/decoding for multi-call applications such as conferencing.

opus
speex 32k/16k/8k
pcmu
pcma
gsm
g722
g726 40/32/24/16
aal2-g726 32/24/16


参考资料:
https://www.gl.com/voice-codecs.html

https://en.wikipedia.org/wiki/Category:Speech_codecs

Android电话API

接听电话

mTelecomManager =  getSystemService("telecom");
mTelecomManager.acceptRingingCall();  

静音

mAudioManager =  getSystemService("audio");
mAudioManager.setParameters("incall_music_mute=true");

联发科

耳机插上时
Set_SpeechCall_UL_Mute=1
未插耳机时
Set_SpeechCall_UL_Mute=1;rcv_mux=off

对应地,取消静音则是

Set_SpeechCall_UL_Mute=0
或
Set_SpeechCall_UL_Mute=0;rcv_mux=on

请求音频焦点

.requestAudioFocus()

录音

int buf_len = AudioRecord.getMinBufferSize(16000, 16, 2);
audioRecord = new AudioRecord(3, 16000, 16, 2, buf_len);  //  inputSource=3 (AUDIO_SOURCE_VOICE_DOWNLINK)
audioRecord.startRecording();
short[] buf = new short[buf_len];
audioRecord.read(buf, 0, buf_len);

音频源定义

    AUDIO_SOURCE_VOICE_UPLINK = 2,
    AUDIO_SOURCE_VOICE_DOWNLINK = 3,
    AUDIO_SOURCE_VOICE_CALL = 4,

流媒体类型定义

    AUDIO_STREAM_VOICE_CALL       = 0,
    AUDIO_STREAM_SYSTEM           = 1,
    AUDIO_STREAM_RING             = 2,
    AUDIO_STREAM_MUSIC            = 3,

Digest access authentication

https://en.wikipedia.org/wiki/Digest_access_authentication
https://www.w3.org/Protocols/rfc2069/rfc2069

HA1 = MD5(username:realm:password)
HA2 = MD5(method:digestURI)
response = MD5(HA1:nonce:HA2)

或者

HA1 = MD5(username:realm:password)
HA2 = MD5(method:digestURI)
response = MD5(HA1:nonce:nonceCount:cnonce:qop:HA2)

ha1 和 ha1 都是hex string, 作为 response的输入参数

https://raw.githubusercontent.com/jmurley/sip_md5/main/sip_md5.py

SIP基本概念

SIP(Session Initiation Protocol,会话初始协议)是一个用于建立、更改和终止多媒体会话的应用层控制协议,其中的会话可以是IP电话、多媒体会话或多媒体会议。SIP是IETF多媒体数据和控制体系结构的核心协议(最新RFC文档是RFC 3261), SIP用于发起会话,它能控制多个参与者参加的多媒体会话的建立和终结,并能动态调整和修改会话属性,如会话带宽要求、传输的媒体类型(语音、视频和数据等)、媒体的编解码格式、对组播和单播的
支持等。SIP协议基于文本编码,大量借鉴了成熟的HTTP协议。

1. 多媒体会话
多媒体会话(Multimedia Session)是指一组多媒体发送者和接受者,以及从发送者到接受者的数据流。例如一个多媒体会议就是一个多媒体会话。一个会话由一组用户名称、会话ID、网络类型、地址类型以及各个单元的地址来确定

2. 用戶代理
用户代理(UA,User Agent)也称SIP终端,是指支持SIP协议的多媒体会话终端。

UA包括用户代理客户端(UAC,User Agent Client)和用户代理服务器(UAS,User Agent Server)。
一般说的UA均是指二者的总称,因为在一次呼叫中,一个SIP终端既要处理SIP请求,又要发起SIP请求。

用户代理客户端是指在SIP会话建立过程中主动发送会话请求的设备。例如,主叫SIP终端。当代理服务器向被叫终端发送会话请求时, 代理服务器也就扮演 用户代理客户端的角色。

用户代理服务器是指在SIP会话建立过程中接收会话请求的设备。例如,被叫SIP终端。当代理服务器接收主叫终端发送会话请求时, 也就扮演了 用户代理服务器的角色。

3. 代理服务器

————————————————————————————
Agent和Proxy在中文都翻译成代理,在SIP语境,要仔细区分。

把 proxy 翻译成代理,agent 则根据语境翻译成助手(代办)之类会好理解一些(比如office里面的“大眼夹”就是agent,中文叫“office小助手”;苹果的siri, 微软的小娜等也是agent)。

agent 拥有独立思考/行动能力,可以代替你做一些决定。 比如,这是我的管家/律师,合同的事你和他谈。他的决定就是我的决定。 这个得到了授权、可以独立以你的名义完成某个任务、必要时允许他根据情况做出自主决定的,就是 agent

proxy一般原样转发,办理用户交代的事情。

在 翻墙流量 外网web的过程中, 浏览器(browser)就是agent, 它会帮你翻译 html和css, 以非常友好的形式展现内容。它也会理解你的双击,键盘操作,滑动等操作,并将这些操作转换为 http请求 发送给服务器。 它能理解主人的意图,并做出适当的转换。

而 外网的某台 中间服务器,则担当了 proxy的角色, 它基本上原样转发你的 http 请求给 目标服务器 (某些匿名代理服务器,可以让目标服务器
无法觉察你是通过proxy访问的), 再将 http响应原样 返回给你。纯粹的跑腿的角色, 不会帮你转换,思考,甚至代替做出决定。
——————————————————————————————-

代理服务器(Proxy Server)的作用就是传递主叫UA发送的会话请求到被叫UA,然后将被叫UA的响应传递回主叫UA,它相当于主叫UA和被叫UA之间传递会话消息的一座桥梁。

代理服务器在接收到主叫UA的会话请求后,首先要向位置服务器请求查找被叫UA的位置以及主、被叫UA的呼叫策略信息。只有找到被叫UA并且此呼叫是允许的,代理服务器才会向被叫UA发送会话请求。代理服务器一般在SIP会话中都是需要的。

4. 重定向服务器
重定向服务器(Redirect Server)用来指示客户端连接的新地址。比如,主叫UA呼叫被叫UA,当重定向服务器收到主叫UA发送的会话请求消息后,查找被叫UA的位置信息,然后将其返回给主叫UA,使主叫UA重新向该位置发起会话请求。此位置可以是被叫UA的位置,也可以是一个代理服务器的位置。接下来主叫UA如同直接呼叫被叫UA或者向代理服务器呼叫的流程一样。

5. 位置服务器
位置服务器(Location Server)是为代理服务器和重定向服务器等提供UA信息的设备。位置服务器记录了注册服务器接收到的UA的信息。二者可以独立存在,也可以作为逻辑组件存在于同一台服务器上

6. 注册服务器
注册服务器(Registrar Server)接受用户注册,注册的内容(如本地号码等信息)一般是存储在位置服务器上,供后续查询使用。二者都是逻辑组件,一般存在于同一台服务器上。


功能:
1. 确定用户位置:主要利用 SIP终端在注册服务器上的注册信息实现用户定位

2. 确定用户可用性: 确定被叫会话终端是否可以参加此会话。SIP支持多种地址描述和寻址方式,包括SIP-URI(比如sip:123456@192.168.1.11)、Tel-URL(比如tel:+1312000)和SIPS-URI(sips:123456@192.168.1.11)。这样,SIP主叫根据被叫地址,就可以识别出被叫是否在传统电话网上,然后通过一个与传统电话网相连的网关向被叫发起并建立呼叫

3. 确定用户能力: 确定被叫终端可用于参加会话的媒体类型及媒体参数。SIP终端在消息交互过程中携带自身的媒体类型和媒体参数,这使得会话双方都可以明确对方的会话能力

4. 建立会话: 建立主被叫双方的会话。SIP会话双方通过协商媒体类型和媒体参数,最终选择双方都具有的能力建立起会话。

5. 管理会话: 可以更改会话参数或中止会话


SIP消息
SIP消息采用文本方式编码,包括请求消息与响应消息两类。
SIP请求消息包括INVITE ACK OPTIONS BYE CANCEL和REGISTER等。RFC 3261定义的请求消息有以下六种。
INVITE: 用于邀请用户加入一个呼叫。
ACK: 用于对消息进行确认。
OPTIONS: 用于请求协商能力信息。
BYE: 用于释放已建立的呼叫。
CANCEL: 用于释放尚未建立的呼叫。
REGISTER: 用于向SIP注册服务器登记用户位置等信息。

SIP响应消息用于对请求消息进行响应,指示呼叫或注册的成功或失败状态。不同类的响应消息由状态码来区分,状态码包含三位整数,状态码的第一位用于定义响应类型,另外两位用于进一步对响应进行更加详细的说明。


工作流程

1. 登记注册
在完整的SIP系统中,所有的SIP终端作为User Agent都应该向注册服务器登记注册,以告知其位置、会话能力、呼叫策略等信息。
通常,SIP终端开机启动或者配置管理员执行注册操作时,就向注册服务器发送注册请求消息(REGISTER),该消息中携带了所有需要登记的信息。注册服务器收到注册请求消息后向终端发送回应消息,以告知其请求消息已收到。如果注册成功,就再向终端发送“200 OK”消息。

2. 建立呼叫
SIP协议采用Client/Server模型,主要通过UA与代理(proxy)服务器之间的通信来完成用户呼叫的建立过程

3. 重定向呼叫
SIP重定向服务器收到会话请求消息后,不是转发会话请求消息,而是在回应消息中告知被叫SIP终端的地址。主叫终端从而重新直接向被叫终端发送会话请求消息。被叫终端也将直接向主叫终端发送应答消息。


SIP支持的传输协议
SIP是一个应用层的会话协议,可以同时支持各种传输协议:
1) 支持UDP传输: UDP是一个无连接的协议,且不提供可靠性。在UDP上建立SIP连接存在不可靠性。
2) 支持TCP传输: 增加了SIP报文和语音传输的可靠性,通过TCP协议自身的特点为基于SIP的VoIP通信提供了面向连接和可靠的传输。SIP使用TCP传输协议后,SIP协议可以不需要考虑报文丢失和重传问题。
3) SIP支持TLS(Transport Layer Security,传输层安全)传输:SIP支持TCP传输仅仅保证了SIP报文和语音传输的可靠性,而SIP支持TLS传输则保证了SIP报文传输的安全性

TLS本身运行于TCP之上,并且提供了通信双方身份识别和加密SIP报文的一整套安全方案,为应用层协议服务。建立TLS连接时需要认证双方用户的身份,因此用户双方必须有各自合法的数字证书,只有通过认证的用户才可以进行后续的通信。使用TLS传输SIP报文,SIP报文在整个传输过程全部被加密


支持的媒体流协议包括:RTP(Real-time Transport Protocol,实时传输协议)和RTCP(Real-Time Transport Control Protocol,实时传输控制协议)。RTP用于为具有实时特征的数据(如交互音频和视频)提供端到端网络的实时传输服务;RTCP用于实时监控传输质量,并提供拥塞控制和流控制。
RTP和RTCP配合使用,就能以有效的反馈和最小的开销使传输效率更佳。在网络中媒体流是明文传输的,为了保证通信内容的安全性,SRTP协议应运而生。

SRTP(Secure Real-time Transport Protocol,安全RTP协议)通过对RTP/RTCP报文的有效负载进行加密,实现对RTP/RTCP报文的加密、认证以及报文重传保护。

采用SRTP加密媒体流,首先需要协商加密信息,目前仅支持在SDP(Session Description Protocol,会话描述协议)中通过crypto头域进行加密协商。发起方将本端支持的加密属性信息发送给接收方进行协商,协商成功后,接收方返回相应的加密属性信息。会话建立后,双方各自使用自己的密钥加密
RTP/RTCP报文,使用对端的密钥解密对方发送的RTP/RTCP报文。

SDP协商主要包括以下参数信息:
Tag: 加密属性信息标识符,在不同的加密属性必选协商参数行中的tag值必须不同,接收方根据Tag值来决定使用哪一组加密属性
Crypto-Suite : 加密套件,定义加密和认证使用的算法。
Key Parameters : 密钥参数 定义密钥信息,主要包括密钥生成方法和密钥值