2018年7月29日星期日

用 strongSwan 配一个支持 AEAD 的远程访问 IKEv2 VPN

前言

IKEv2/IPsec 是一套网络层数据加密方案,它可以配置为点到点、站到站、网状网(Cisco DMVPN)、远程访问。IKEv2/IPsec 远程访问 VPN 十分适合于远程办公的场景。
IKEv2/IPsec 有两个组成部分,IKEv2 负责对通信双方进行鉴权并协商临时密钥,IPsec 负责匹配给定的流量规则并加密数据。
IPsec 有两种模式,AH 和 ESP。AH 只进行真实性校验,不加密;ESP 既做真实性校验又加密。实际应用中,AH 因为不支持 NAT 穿隧而并没有什么用处。如果真的有不加密的需求,可以将 ESP 的加密模式设置成 null。
其中,ESP 也有两种模式,transport 和 tunnel。Transport 模式下,加密载荷里直接就是传输层段或者报文,因而只能加密两个端点之间的连接;tunnel 模式下,加密载荷里有第二层 IP 报头,不仅可以进行路由,还可以给 NAT 后的客户端分配虚拟 IP 地址。
综上所述,我们要配置的 IPsec 模式应为 ESP tunnel。

关于认证阶段

IKEv2 的认证分为两个阶段。
Phase 1:双方鉴权和协商 IKE_SA,使用 UDP 500 端口。对于远程访问 VPN 来说,客户端验证服务端的 PKI 证书,服务端验证客户端的用户名密码。
Phase 2:建立隧道和协商 CHILD_SA,使用 IP 50 协议或 UDP 4500 端口(若启用 NAT 穿隧)。双方根据配置好的规则来决定哪些流量需要加密。对于远程访问 VPN 来说,客户端会请求加密全部流量(0.0.0.0/0::/0)。
随后,双方配置操作系统的 IPsec Policy 来把流量加密转发到 IP 50 协议或 UDP 4500 端口上。
为了保证同一个临时密钥不使用太长时间,IKEv2 会每隔一段时间 rekey 一次。在本例,我们关闭服务端的 rekey,将 rekey 的任务交给客户端,否则会因为双方同时 rekey 而掉线。

软件环境

服务端:Debian testing (10.0pre),strongSwan 5.6.3,systemd 239,AppArmor 2.12。
客户端:Windows 10 Pro 1803、macOS High Sierra 10.13.6、iOS 11.4.1。
如果你在用旧版的 Debian 或者 Ubuntu,强烈建议你打开 updatesbackports 源。其中 updates 包含版本内功能更新,backports 包含跨版本功能更新。

步骤一:安装依赖

sudo apt update
sudo apt install certbot charon-systemd iptables-persistent libcharon-extra-plugins libstrongswan-extra-plugins ndppd
本文使用了新版 strongSwan 的部分新功能,所以请务必确认 strongSwan 的版本高于 5.7.1。

步骤二:申请 Let's Encrypt 证书

如果你已经有在开 HTTPS 业务,那么直接把 TLS 证书拿来用就好。
没有的话用 certbot 申请一个 Let's Encrypt 的证书:
sudo certbot certonly --standalone -d example.com
sudo ln -s /etc/letsencrypt/live/example.com/privkey.pem /etc/swanctl/private/example.com.key
一切顺利的话证书已经躺在 /etc/letsencrypt/live 里了。证书需要每隔 90 天续期一次,幸运的是 systemd 的 certbot.timer 会自动启动 certbot 来续期。不过续期之后 strongSwan 并不会自动重启,你可以在 /etc/letsencrypt/renewal-hooks 里写个脚本。

步骤三:配置 AppArmor

Debian 会默认给 strongSwan 打开 AppArmor。这样的话 strongSwan 就读取不到我们的证书文件了。在 /etc/apparmor.d/local/usr.sbin.swanctl 里加入:
/etc/letsencrypt/       r,
/etc/letsencrypt/**     r,

步骤四:配置日志

在遇到问题的时候有日志看是很重要的,因此我们要先打开必要的日志。在 /etc/strongswan.d/charon-systemd.conf 里写上:
charon-systemd {

    # Section to configure native systemd journal logger, very similar to the
    # syslog logger as described in LOGGER CONFIGURATION in strongswan.conf(5).
    journal {

        # Loglevel for a specific subsystem.
        # <subsystem> = <default>

        # Default loglevel.
        # default = 1

        cfg = 2
        ike = 2

    }

}

步骤五:配置 swanctl

strongSwan 有两套配置系统:旧的一套是 ipsec.conf,使用 stroke 插件来启动;新的一套是 swanctl.conf,使用 vici 插件来启动。我们将使用新的 swanctl.conf 系统。虽然网络上关于 swantl.conf 的文档和资料还比较少,但是配置起来更加灵活。
/etc/swanctl/swanctl.conf 里加入:
authorities {
    lets-encrypt {
        file = /etc/letsencrypt/live/example.com/chain.pem
    }
}

connections {
    ikev2 {
        version = 2
        local_addrs = %any
        remote_addrs = %any
        # 这是 Windows 10 和 macOS High Sierra 兼容的最优 Phase 1 加密套件。如果你的客户端支持更好的加密套件,可以在这里写上。
        proposals = aes256gcm16-prfsha384-curve25519-ecp384,aes256-sha384-prfsha384-ecp384,aes256-sha256-prfsha256-ecp256,aes256-sha384-prfsha384-modp1024
        dpd_delay = 30s
        send_certreq = no
        send_cert = always
        rekey_time = 0s
        pools = pool-ipv4,pool-ipv6
        local {
            certs = /etc/letsencrypt/live/example.com/cert.pem
            id = example.com
        }
        remote {
            eap_id = %any
            auth = eap-mschapv2
        }
        children {
            ikev2 {
                # 这是 Windows 10 和 macOS High Sierra 兼容的最优 Phase 2 加密套件。如果你的客户端支持更好的加密套件,可以在这里写上。
                esp_proposals = aes256gcm16-curve25519-ecp384,aes256gcm16,aes256-sha256-sha1
                local_ts = 0.0.0.0/0,::/0
                rekey_time = 0s
                hw_offload = auto
            }
        }
    }
}

secrets {
    eap-1 {
        # 两个客户端不可以共用同一个用户名,否则会发生 IP 地址冲突。如果有多个客户端,请分配多个用户名。
        secret = 用户1和用户2共用的密码
        id-user1 = 用户名1
        id-user2 = 用户名2
    }
    eap-2 {
        secret = 用户3的密码
        id-user3 = 用户名3
    }
    private-1 {
        file = example.com.key
    }
}

pools {
    pool-ipv4 {
        # 这里也可以写 100.100.100.129-100.100.100.254 的格式,或者换成你想要的 IPv4 地址段。
        addrs = 100.100.100.128/25
        dns = 8.8.8.8,8.8.4.4
    }
    pool-ipv6 {
        # 改成上游 ISP 分配的 IPv6 子网的一小段。
        addrs = 2001:db8::/97
        dns = 2001:4860:4860::8888,2001:4860:4860::8844
    }
}

步骤六:配置 IPv4 NAT

/etc/iptables/rules.v4 加入:
*nat
:PREROUTING ACCEPT [31:5739]
:INPUT ACCEPT [31:5739]
:OUTPUT ACCEPT [28:3794]
:POSTROUTING ACCEPT [11:1486]
-A POSTROUTING -m policy --pol ipsec --dir out -j ACCEPT
-A POSTROUTING -s 100.100.100.128/25 -o eth0 -j MASQUERADE
COMMIT

步骤七:配置 IPv6 NDP Proxy

/etc/ndppd.conf 加入:
# 改成你的出站设备名
proxy eth0 {
    # 改成上游 ISP 分配的 IPv6 子网的一小段。
    rule 2001:db8::/97 {
        static
    }
}

步骤八:启用 IP 转发

/etc/sysctl.d/50-ip-forwarding.conf 里写上:
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
注意当开启 IPv6 转发的时候,SLAAC 会被默认关闭。如果你通过 SLAAC 从上游 ISP 收取 IPv6 地址,那么需要将其手动打开:(可选)
net.ipv6.conf.eth0.accept_ra = 2
或者,如果你使用 /etc/network/interfaces、NetworkManager、systemd-networkd 来配置网络,只需要打开 SLAAC 开关即可。

步骤九:启动服务

sudo systemctl restart apparmor
sudo systemctl restart netfilter-persistent ndppd strongswan-swanctl systemd-sysctl
sudo systemctl enable certbot.timer netfilter-persistent ndppd strongswan-swanctl
# 禁用旧版 ipsec.conf 配置服务
sudo systemctl disable strongswan

步骤十:连接客户端

在开始之前,我强烈建议在一台空闲的电脑上连接 VPN 网关,输入:
sudo journalctl -u strongswan-swanctl -f
来查看实时滚动日志。

Windows

在 Windows 设置里,进入 Network & Internet → VPN → Add a VPN connection。
VPN provider:Windows (built-in)
Connection name:随意
Server name or address:服务器的域名
VPN type:IKEv2
User name:用户名
Password:密码
如果提示 Error 13801: IKE authentication credentials are unacceptable,那么请用 certmgr.msc 导入 Let's Encrypt Authority X3 中间证书。
如果还是不行,就导入这条注册表项:
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\RasMan\Parameters]
"DisableIKENameEkuCheck"=dword:00000001

macOS

在 macOS 系统偏好设置里,进入 Network,点击左边栏底部的「+」。
Interface:VPN
VPN Type:IKEv2
Service Name:随意
Server Address:服务器的域名或 IP 地址(如果填域名连接超时就填 IP 地址)
Remote ID:服务器的域名,不能填 IP 地址
Local ID:留空
Authentication Settings:Username
Username:用户名
Password:密码

iOS

在 iOS 的设置里,进入 VPN → Add VPN Configuration。
Type:IKEv2
Description:随意
Server:服务器的域名或 IP 地址(如果填域名连接超时就填 IP 地址)
Remote ID:服务器的域名,不能填 IP 地址
Local ID:留空
Uesr Authentication:Username
Username:用户名
Password:密码

Linux 和 Android

Linux 用户只需要将 swanctl.conf 的内容反过来填就成了客户端配置。
对于 Android 用户,可以去 Google Play 下载 strongSwan 客户端

如何配置默认网关

Windows 默认不把所有流量都发送到 VPN,macOS 和 iOS 则相反。如果要改变这个设置,可以根据以下步骤来操作。

Windows

IPv4:在 Windows 设置里,进入 Network & Internet → Status → Network and Sharing Center → Change adapter settings → VPN (IKEv2) → 右键属性 → Networking → TCP/IPv4 → Properties → Advanced → IP Settings → Use default gateway on remote network。
IPv6:虽然 TCP/IPv6 里也有这个名字的选项,但是实际上无效。要想设置 IPv6 的默认网关,请打开 PowerShell,输入:
Add-VpnConnectionRoute "连接名" ::/1
Add-VpnConnectionRoute "连接名" 8000::/1

macOS

VPN 已断开的状态下,在 macOS 系统偏好设置里,进入 Network,选择 VPN (IKEv2),点击 Advanced,在 Options 选项卡内,勾选 Send all traffic over VPN connection。

如何打开 AEAD(仅 Windows 10 支持)

AEAD 可以在同一个算法内实现加密和校验,能够提高连接的安全性。因为 Windows 10 的 Phase 1 AEAD 实现有 bug,所以只打开 Phase 2 的 AEAD。
请打开 PowerShell,输入:
Set-VpnConnectionIPsecConfiguration -ConnectionName "连接名" -AuthenticationTransformConstants GCMAES256 -CipherTransformConstants GCMAES256 -DHGroup ECP384 -EncryptionMethod AES256 -IntegrityCheckMethod SHA384 -PfsGroup ECP384 -Force
很不幸,Debian 发行的 strongSwan 并未开启 ChaCha20-Poly1305 加密算法。所以我们暂时只有 AES-256-GCM 可以用。

备用:旧版 ipsec.conf 配置

这是过时的 ipsec.conf 配置文件,仅供参考,不要使用。
config setup
    charondebug = "cfg 2, ike 2"

ca lets-encrypt
    cacert = /etc/letsencrypt/live/example.com/chain.pem
    auto = add

conn ikev2
    left = %any
    leftauth = pubkey
    leftcert = /etc/letsencrypt/live/example.com/cert.pem
    leftid = @example.com
    leftsendcert = always
    leftsubnet = 0.0.0.0/0,::/0
    right = %any
    rightauth = eap-mschapv2
    rightdns = 8.8.8.8,8.8.4.4,2001:4860:4860::8888,2001:4860:4860::8844
    rightsendcert = never
    rightsourceip = 100.100.100.128/25,2001:db8::/97
    auto = add
    dpdaction = clear
    eap_identity = %any
    esp = aes256gcm16-curve25519-ecp384,aes256gcm16,aes256-sha256-sha1!
    ike = aes256gcm16-prfsha384-curve25519-ecp384,aes256-sha384-prfsha384-ecp384,aes256-sha256-prfsha256-ecp256,aes256-sha384-prfsha384-modp1024!
    keyexchange = ikev2
    rekey = no
/etc/ipsec.secrets
# ln -s /etc/letsencrypt/live/example.com/privkey.pem /etc/ipsec.d/private/example.com.key
: RSA "example.com.key"

用户名1 : EAP "用户1的密码"
用户名2 : EAP "用户2的密码"
用户名3 : EAP "用户3的密码"

2016年7月26日星期二

用 tun2socks 配置全局 SOCKS5 代理转发

笔者要在安装了 ArchLinuxARM 的 Raspberry Pi 上配置 SOCKS5 代理转发,并把局域网内其它计算机的网关、DNS 服务器设置成 Raspberry Pi,做到全局 SOCKS5 代理转发。
ArchLinuxARM 的系统控制服务是 Systemd。如果你不用 Systemd,可能需要调整某些步骤。
在开始之前,安装这些软件包:
badvpn-tun2socks iproute2 iptables pdnsd
首先配置 pdnsd 的 DNS 转发:
/etc/pdnsd.conf
 
global {
max_ttl=300;
min_ttl=0;
neg_domain_pol=off;
query_method=tcp_only;
run_as=pdnsd;
timeout=10;
}
 
server {
ip=8.8.8.8,8.8.4.4;
}
然后写启动和终止脚本:
/usr/local/bin/socksfwd
 
#!/bin/bash
 
SOCKS_SERVER=xxx.xxx.xxx.xxx # SOCKS 服务器的 IP 地址
SOCKS_PORT=1080 # SOCKS 服务器的端口
GATEWAY_IP=192.168.1.1 # 家用网关(路由器)的 IP 地址
BYPASS_IPS=(yyy.yyy.yyy.yyy zzz.zzz.zzz.zzz) # 不需要代理的 IP 地址
TUN_NETWORK_DEV=tun0 # 选一个不冲突的 tun 设备号
TUN_NETWORK_PREFIX=10.210.0 # 选一个不冲突的内网 IP 段的前缀
 
start_fwd() {
ip tuntap add dev "$TUN_NETWORK_DEV" mode tun
ip addr change "$TUN_NETWORK_PREFIX.1/30" dev "$TUN_NETWORK_DEV"
ip link set "$TUN_NETWORK_DEV" up
ip route del default via "$GATEWAY_IP"
ip route add default via "$GATEWAY_IP" metric 600
for i in "${BYPASS_IPS[@]}"
do
ip route add "$i" via "$GATEWAY_IP" metric 5
done
ip route add default via "$TUN_NETWORK_PREFIX.2" metric 6
badvpn-tun2socks --tundev "$TUN_NETWORK_DEV" --netif-ipaddr "$TUN_NETWORK_PREFIX.2" --netif-netmask 255.255.255.252 --socks-server-addr "$SOCKS_SERVER:$SOCKS_PORT" &
TUN2SOCKS_PID="$!"
}
 
stop_fwd() {
ip route del default via "$TUN_NETWORK_PREFIX.2" metric 6
for i in "${BYPASS_IPS[@]}"
do
ip route del "$i" via "$GATEWAY_IP" metric 5
done
ip tuntap del dev "$TUN_NETWORK_DEV" mode tun
}
 
start_fwd
trap stop_fwd INT TERM
wait "$TUN2SOCKS_PID"
写 Systemd 服务:
/etc/systemd/system/socksfwd.service
 
[Unit]
Description=Transparent SOCKS5 forwarding
After=network-online.target
 
[Service]
Type=simple
ExecStart=/usr/local/bin/socksfwd
LimitNOFILE=1048576
 
[Install]
WantedBy=multi-user.target
打开流量转发:
/etc/sysctl.d/sysctl.conf
 
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
net.ipv4.tcp_congestion_control=westwood
net.ipv4.tcp_syn_retries = 5
net.ipv4.tcp_synack_retries = 5
/etc/iptables/iptables.rules
 
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [1:1500]
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING ! -s 127.0.0.0/8 -j MASQUERADE
COMMIT
最后设置 DNS 为本机 pdnsd。如果用 systemd-network,可以这么写,其中 eth0 改成对外的网卡名称:
/etc/systemd/network/eth0.network
 
[Match]
Name=eth0
 
[Network]
DHCP=yes
DNS=127.0.0.1
IPForward=yes
 
[DHCP]
UseDNS=no
 
[Route]
Metric=100
如果不用 systemd-network,也可以改 /etc/resolv.conf
/etc/resolv.conf
 
nameserver 127.0.0.1
最后启动服务并重启:
sudo systemctl enable socksfwd iptables
systemctl reboot

P.S. 这个方案也可以在桌面 Linux 或者 ChromeOS 上运行,只需要安装依赖并设法运行 socksfwd 脚本即可,其它步骤可以不做。
可以不使用 pdnsd 来转发 DNS,但是 DNS 服务器就需要加到 BYPASS_IPS 中。
在 ChromeOS 平台,创建的 tun 设备会被 shill 服务自动删掉,需要在执行 socksfwd 之前执行:
stop shill
start shill BLACKLISTED_DEVICES=tun0

2015年1月18日星期日

MinGW-w64 的 libwinpthread-1.dll 依赖问题

用 MinGW-w64 工具链编译程序默认是动态链接 libgcclibstdc++。这样客户机上必须有这两个 DLL 才能运行目标程序。
可以用 -static-libgcc -static-libstdc++ 来设置成静态链接。
但是 libstdc++ 有一个依赖是 libwinpthread-1.dll,它无法用 -static-* 选项来关掉。
虽然很多人建议用 -static,但是它有副作用。
编过研究,我找到了解决方案:
-static-libgcc -static-libstdc++ -Wl,-Bstatic -lstdc++ -lpthread -Wl,-Bdynamic
将它加在 g++ 命令行的最末尾即可。注意 -lstdc++-lpthread 之前。

去 StackOverflow 为我的这个答案投票:[问题 1] [问题 2]

2014年12月14日星期日

GCC LTO 连接时优化的几点注意

GCC 4.7 开始支持 Link Time Optimization (LTO),即连接时优化,可以使跨不同源文件的函数之间的关系得到进一步的优化。
它的做法是先把源代码编译成 GIMPLE 中间码,再在连接的时候生成真正的机器码。
在 Linux 上编译本机程序时一直正常工作,但是在使用 MingW-w64 工具链的时候,发现有时连接静态库会报错提示找不到符号。
在 Google 和 StackOverflow 一段时间之后,发现需要注意以下几点:
  1. arnmranlib 换成 gcc-argcc-nmgcc-ranlib
    因为原生的 arnmranlib 无法处理含有 GIMPLE 中间码的目标文件。
  2. 使用 gcc -fuse-linker-plugin 参数。
    它开启连接器插件功能,GCC 可以在连接时做更多的优化。
  3. 如果各种方法都不行,使用 gcc -ffat-lto-objects
    这会让 GCC 生成一个“胖的(fat)”目标文件,它同时含有非 LTO 的机器码和 GIMPLE 中间码。工具链中如果有不支持 LTO 的程序如(ar),会使用非 LTO 的机器码副本。
总结一下,编译的时候加上以下的 CFLAGS
gcc -flto -fuse-linker-plugin
如果 Makefile 是别人写好的,可以使用我的 LTOWrapper 来注入 -flto 参数。

注:在 Linux 下使用 LLVM/Clang 的全程序优化遇到困难的时候可以试试 http://nochair.net/posts/2011/07-01-whole-program-llvm-bitcode.html
Linux 下,Clang 需要使用 Gold 连接器。配置好之后 -flto 就可以使用。

2014年10月24日星期五

用命令关闭 Gnome Shell 的 Looking Glass

Gnome 3 的 Gnome Shell 有一个很方便的调试器,叫 Looking Glass。
启动方式是 Alt-F2 输入 lg 回车,退出方式是按 Esc 键。
但是我笔记本电脑的 Esc 键坏了,所以找到了一个用命令关闭 Looking Glass 的方法。
输入 Main.createLookingGlass().toggle() 即可。

P.S. 用 global.get_window_actors().forEach(function (w) { w.set_scale(0.8, 0.8) }) 可以把所有窗口缩放到 80% 大小。

2014年8月22日星期五

宣布 JKSN,又一个 JSON 兼容二进制压缩格式

本来只是想可不可以用二进制来储存 JSON 并且做一些优化之类的,结果造出来 JKSN 这货。
比较了一下文件大小,发现没有经过压缩的 JKSN 数据比 gzip -9 JSON 还要小。于是把 JKSN 的目标定为让 JSON 序列更小。

JKSN 适用于任何网络带宽比处理时间更重要的场合,如移动即时通讯。

项目地址:https://github.com/m13253/JKSN。Specification 和参考实现均以 BSD 协议发布。

为什么不用 GZIP 呢?
JKSN 和 GZIP 不冲突。JKSN 编码后的数据可以被 GZIP 压缩得更小。
由于 GZIP 的 32 KiB 滑动窗口的设计,相似的数据只有互相间隔不超过 32 KiB 才能得到有效压缩。JKSN 会将数据重新组织,使相似的数据尽可能靠近排列,更有利于 GZIP 的压缩。

下面,用项目主页上举的例子,做个比较,:
格式文件大小
JSON150 字节
JSON gzip -9112 字节
JKSN109 字节
JKSN gzip -9107 字节
再来比较同类二进制 JSON 实现:
格式文件大小
BSON172 字节
Universal Binary Json150 字节
MessagePack120 字节
JSONH116 字节
BJSON(内置自适应哈夫曼压缩)115 字节
JKSN109 字节

如果各位读者对此项目感兴趣,且有空余时间,不妨让 JKSN 变得更好:
目前 JKSN 已经有 Python 3、Python 2、浏览器 JavaScript、Node.JS、C 五种环境的实现了。如果你愿意,请帮助我为 JKSN 补上 PHP 5、Java 的实现。
追求模范性和稳定性的实现可以直接 fork JKSN repository 并发给我 Pull Request;追求速度的实现可以另开新 repository 并通知我。
如果你知道怎么重新组织 JSON 结构让其更易于压缩,请联系我。我希望和你探讨问题。

2014年8月16日星期六

中文普通话参考音素表

Rocaloid 语音合成引擎正在 Sleepwalking 的领导下紧张地开发中,制作中文音源需要哪些音素呢?
Rocaloid 现在版本是以单音节(monophone)为合成单位,所以录音数量大大降低了,有条件录制某些特殊情况下的发音变形。
以此我整理了一份参考音素表,只做参考,如有遗漏或错误请留言通知我。
标有 (p) 的是弱读和快读时的发音变化,相对的是 (f) 标记。

辅音 Consonants


汉语拼音参考IPA 音标参考X-SAMPA 音标参考备注
bpp
pp_h
mmm
fff
dtt
tt_h
nnn
lll
gkk
kk_h
hxx
jts\
qtɕʰts\_h
xɕs\
zhʈʂts`
chʈʂʰts`_h
shʂs`
rʐz`
ztsts
ctsʰts_h
sss
szzz用来拼写外来语、少数民族语言和方言
vvv
y (p)jj快读时 i u 变为 j w
w (p)ww
0ʔ?声门塞音

单元音 Mono-vowels

汉语拼音参考IPA 音标参考X-SAMPA 音标参考备注
aaa
o (p)oo如“咯”
e (p)ɤ7如“了”
ehɛE汉语拼音方案记作“ê”,注音符号记作“ㄝ”,如“诶”
iii
ihɯ / ɨM / 1注音符号记作“ㄭ”,如“私”
uuu
üyy有时使用字母“v”代替
nnn这里把鼻音视作元音处理
ngŋN
mmm

双元音 Di-vowels

汉语拼音参考IPA 音标参考X-SAMPA 音标参考备注
aiaI
aoaU
ananan
angɑŋAN
erɑɹAr\如“二”
ouɤʊ7U有些人偏好读作 oʊ
enən@n
engɤŋ7N
eieI
iaiaia
ioioio如“哟”
ieiE
ininin
uauaua
o (f)uouo与 p pʰ m f 拼读,如“播”
uouO如“我”
ongʊŋUN
üeyE如“月”
e (f)ɯʌMV如“饿”

三元音 Tri-vowels

汉语拼音参考IPA 音标参考X-SAMPA 音标参考备注
iaoiaʊiaU
iangiɑŋiAN
iuiɤʊi7U有些人偏好读作 ioʊ
iong (f)iʊŋiUN如“穷”慢读
ianiɛniEn
ingiɯŋiMN南方口音更偏好读作 iŋ
uaiuaɪuaI
uanuanuan
uanguɑŋuAN
uiueɪueI
unuənu@n
uenguɤŋu7N
iong (p)yʊŋyUN如“永”快读
üanyɛnyEn如“远”,北方口音更偏好读作 yan
ünyinyin如“云”

儿化音 R-endings

汉语拼音参考IPA 音标参考X-SAMPA 音标参考备注
oror\
e-erɤɹ7r\不是 ɑɹ,ɑɹ 已在前面列出
ehrer\
ihrɯɹMr\
urur\
üryr\

唇化鼻音 M-endings (语速快的场合)

汉语拼音参考IPA 音标参考X-SAMPA 音标参考备注
amamam
amgɑmAm
omgomom
eməm@m
emgɤm7m
imimim
üm (p)ymym
iomgiʊmiUm
iamgiɑmiAm
iamiɛmiEm
uamuamuam
uamguɑmuAm
umuəmu@m
uemguɤmu7m
üm (f)yimyim
当快读时,i u 变为 j w,儿化音与前面一个元音连读,p pʰ m 之前的鼻音变为唇化鼻音,鼻音前的元音变为鼻音化元音。

可以使用我的 Pinyin2XSampa 程序将拼音转换成 X-SAMPA。

论交互设计师的英文素养

很多 Web App 都会提供第三方调用的 API,在 API 的提示信息中如果有语法错误就贻笑大方了。
你不信?我来举几个例子:
{"error": "Hit sexy spam"} ——来自百度
{"error": "Wrong verify code"} ——来自腾讯
{"error": "Apps is not exists"} ——来自哔哩哔哩
交互设计师的英文素养很重要啊。
下面给出我的参考翻译
百度那例:Hit porn filter
腾讯那例:Wrong Captcha
哔哩哔哩那例:App does not exist

2014年6月15日星期日

用 Biligrab-Danmaku2ASS 看B站

上回说到Linux 下 Chrome Pepper Flash 上B站卡的问题,这次就与 cnBeining 合作带来了桌面观看哔哩哔哩视频的解决方案了。
使用了 cnBeining 的 Biligrab 抓取脚本和我的 Danmaku2ASS 弹幕渲染。Perfect。
用法是一句话 ./biligrab.py http://www.bilibili.com/video/av314/
海外党再加 --overseas
这个项目以 MIT 协议发布。

2014年6月1日星期日

Linux 下 Chrome Pepper Flash 上B站卡的问题

用 Linux 下 Chrome Pepper Flash 播放器访问 bilibili 会有各种问题。
第一个是字体乱码,在 bilibili 播放器的配置面板中取消勾选“应用到界面字体”“粗体”并选择一个合适的字体。默认字体很可能会导致崩溃。
第二个是弹幕播放卡顿,可以使用“宽屏模式”或“全屏模式”解决问题。

bilibili 怎么还不用 HTML5 播放器啊!ABPlayerHTML5 项目已经用 HTML5 还原高级弹幕了。
总之谁有精力开发一个第三方播放器,“逼死官方”吧。