2
4
2017
15

使用 Reabble 在 Kindle 上阅读 InoReader 里的文章

2017年02月08日更新:已经无法通过 InoReader 的 OAuth 功能登录。现只能通过提交 InoReader 账号和密码到 Reabble 的方式登录。我正考虑放弃此服务。


InoReader 的核心在于阅读。Kindle 的核心也在于阅读。

InoReader 的重点在于,它是在线 RSS 阅读器,手机 app 还支持离线,不仅可以在电脑前研读,还可以随时随地使用手机来闲读。可有个问题:长时间盯着 LCD 屏幕看,对眼睛不好!

而这正是使用电子墨水的阅读器——如 Kindle——所解决的问题。所以,怎么把两者结合起来呢?

土木坛子介绍了一个名为 kindle4rss 的服务,通过电子邮件推送 RSS 源。其实我之前也听朋友说过,RSS 源是可以推送到 Kindle 的。但是这样是针对单个的 RSS 设置,推送到 Kindle 的部分不能和其它地方的阅读同步。然后,我顺着 kindle4rss 看了一下,忽然眼前一亮:他们基于 InoReader 的 API 做了一个网页版的、适合 Kindle 使用的阅读器!

就是 Reabble 啦。其实它支持各种浏览器的,只是配色、操作方式是为 Kindle 优化的。(说起来,我挺讨厌像美团招聘或者 AirDroid 局域网 HTTPS 版那样故意不支持特定客户端的行为,明明能用的,却偏偏不让用户用。)

直接访问就可以试用啦。黑白的界面,在电脑上看着挺丑的,不过在 Kindle 上看就大不一样啦。左边是文章列表界面(调整过选项,隐藏了侧栏和已读文章),右边是文章阅读界面(打开了菜单):

Reabble 文章列表 Reabble 文章阅读

在 Kindle 上的体验非常好。而且 Kindle 上的登录做得非常棒:只需要在电脑上通过 InoReader 的 OAuth 授权登录,之后就可以获得一串代码,在 Kindle 那边填写就可以了。我的 InoReader 是用 Google 账号登录的,要是让我在 Kindle 上登录我大概会直接放弃的。

它要求有 Wi-Fi 网络(废话,Kindle 那个破浏览器又不支持离线模式)。不过这对我来说不是问题。真要没 Wi-Fi 的话,用手机开个热点用也成。

不过有些小问题:

  • 不支持广播和赞,只支持加星标。
  • 侧栏里没有未读文章的项、以及星标数不能隐藏。
  • 免费用户只能每天阅读15篇文章,但是并没有阅读到那么多就会出现提示。好像重新打开又可以继续读了。

最后,这服务收费也非常便宜,一年只要18RMB,订更久还会更便宜~最重要的是,它的支付方式对于我很方便!(谁去帮 InoReader 接入支付宝或者微信支付吧,这样我就不用看广告了~)另外,推荐它的话,可以获赠两年的使用权。

Category: 网络 | Tags: 阅读 rss kindle InoReader
12
14
2016
0

使用 RSS 订阅知乎用户的动态

之前做了一个知乎专栏转RSS的网关,这次又写了个针对知乎动态的。感兴趣的话去 https://rss.lilydjwg.me/ 看看用法吧。

此功能只支持用户的回答和发布文章两个操作。别的操作,比如赞了答案啦,关注了专栏啦,参加了 live 啦,没有实质性的内容,又可能会有非常多,不适合 RSS 这种面向内容发布的东西,所以就过滤掉了。即使如此,程序每次访问取最近40条动态,缓存有好几个小时,所以对于活跃的用户是可能漏掉一些动态的。反正现在信息这么多,漏了就漏了吧,去读读别的东西呗。

知乎这次暴露出来的 API 有点意思。它有一个 include 参数,看起来是指定要返回哪些字段的。看起来知乎也在解决 GitHub 遇到的同样的问题:RESTful API 太不灵活了。只是,为什么要造轮子啊,GraphQL 不是挺好的吗……

Category: 网络 | Tags: rss 知乎
9
10
2016
6

如何取消对 WoSign 根证书的信任

WoSign 最近曝出一大堆问题,而且其处理问题的态度、解决问题的方式十分令人担忧。其官方形象也很糟糕,比如对国内 Let's Encrypt 用户进行 FUD 式威胁,比如只吊销了因 bug 误发的 GitHub 域名的证书,给某大学误发的证书视若无睹。具体问题有兴趣的可以去相关邮件组查看讨论。

这次问题我认为比起 CNNIC 要严重多了(最主要是这态度、这水准,就算它不主动作恶,也很容易被利用的样子),所以我获知情况之后就取消对了 WoSign 的信任。StartCom 签名 WoSign 的证书,所以需要一并吊销(反正也是一家人)。

火狐(桌面版)

依次打开「首选项」->「高级」->「证书」->「查看证书」,找到并选择 StartCom 和 WoSign 下的所有证书(使用 Shift 键可以选择连续的项目),然后点「编辑信任」按钮,取消弹出框中三个选项框的勾选。

Arch Linux

archlinuxcn 源用户直接执行命令:

sudo pacman -Sy revoke-disputable-ca

手动操作的话,是这样子。把需要取消信任的证书复制(不要软链接)到 /etc/ca-certificates/trust-source/blacklist/ 目录下,然后执行 update-ca-trust 命令即可。

我那个包里取消信任的证书是下边这八个:

CA_WoSign_ECC_Root.pem                    CNNIC_ROOT.pem                          StartCom_Certification_Authority_G2.pem  WoSign_China.pem
Certification_Authority_of_WoSign_G2.pem  StartCom_Certification_Authority.1.pem  StartCom_Certification_Authority.pem     WoSign.pem

Android

在「设置」->「安全」->「受信任的凭据」中禁用掉相关证书。这对 Opera Mobile 有效,但是对火狐无效。

目前还没找到火狐 Android 版禁用根证书的方式。

确认方法

访问 https://www.wosign.com/ 即可。

目前 USTC 已经更改证书,禁用这些根证书不影响 USTC 镜像源的使用。现在我因此不能访问的网站主要是 Python 邮件列表

Category: 网络 | Tags: http ssl 安全
7
23
2016
0

发包太快,请勿跟踪

之前写的那个处理 DNS AAAA 的程序,后来请求量大的时候就经常报错。经过研究,是在sendto的时候返回了「Pemission Denied」错误。后来的 Rust 版本也发生了类似的问题,得到操作系统返回的代码「EPERM」。

我翻了半天 man 手册,其中只说到向广播地址发包可能会得到 EACCES 错误。Google 也没有得到结果(都是些权限不够的问题,但我的程序是 root 跑的呀,并且错误比较零星)。后来发到 shlug 邮件列表中询问,才终于得知了和我有同样问题的人,但是也没有结论,只是说关掉 iptables 就正常了。可我的程序依赖 iptables 呢……而且我要的不仅仅是解决方案(实际上这个问题并没有造成什么可感知的影响,就算有,我也有办法 migrate),我更想知道为什么。

确定是发包太快造成的问题,拿着相关关键词去搜,还真找到了一些有用的信息。比如之前看过的 CloudFlare 低延迟 UDP 实验时会让 iptables 不跟踪相关数据包,有人在使用 SIP 协议时也遇到了同样的问题,并且在内核日志的帮助下解决了。于是我照着做,让 conntrack 放过我发出的 UDP 包:

iptables -t raw -I OUTPUT -p udp -m udp --sport 53 -j NOTRACK

然后不仅那些错误都没了,而且处理速度快了一倍!(图中红虚线是发生错误的时候。)

程序统计信息

Category: 网络 | Tags: iptables linux UDP
4
17
2016
6

用 nfqueue + Python 回复 IPv6 DNS 请求

发生了这么一件事:服务器访问某些 URL 时经常会花费好几秒的时间。重现并分析 strace 记录之后,发现是 DNS 的 AAAA 记录的问题。

情况是这样的:CentOS 6 默认启用了 IPv6,于是 glibc 就会同时进行 A 和 AAAA 记录查询。然后呢,上游 DNS 是运营商的,不给力,经常在解析 AAAA 记录时花费几秒甚至十几秒,然后超时或者返回个 SERVFAIL。

不过咱在天朝,也没有 IPv6 网络可用,所以就禁用 IPv6 吧。通过 sysctl 禁用 IPv6 无效。glibc 是通过创建 IPv6 套接字的方式来决定是否进行 AAAA 请求的。查了一下,禁用掉 ipv6 这个内核模块就可以了。可是,rmmod ipv6 报告模块正在使用中。

lsof -nPi | grep -i ipv6

会列出几个正在使用 IPv6 套接字的进程。重启服务,或者重启机器太麻烦了,也不知道会不会有进程会起不来……于是我想起了歪心思:既然是因为 AAAA 记录回应慢,而咱并不使用这个回应,那就及时伪造一个呗。

于是上 nfqueue。DNS 是基于 UDP 的,所以处理起来也挺简单。用的是 netfilterqueue 这个库。DNS 解析用的是 dnslib

因为 53 端口已经被 DNS 服务器占用了,所以想从这个地址发送回应还得用底层的方法。尝试过 scapy,然而包总是发不出去,Wireshark 显示 MAC 地址没有正确填写……实在弄不明白要怎么做,干脆用 RAW 套接字好了。man 7 raw 之后发现挺简单的嘛,因为它直接就支持 UDP 协议,不用自己处理 IP 头。UDP 头还是要自己处理的,8 字节,其中最麻烦的校验和可以填全零=w= 至于解析 nfqueue 那边过来的 IP 包,我只需要源地址就可以了,所以就直接从相应的偏移取了~(其实想想,好像 dnslib 也不需要呢~)

代码如下(Gist 上也放了一份):

#!/usr/bin/env python3

import socket
import struct
import traceback
import subprocess
import time
import signal

import dnslib
from dnslib import DNSRecord
from netfilterqueue import NetfilterQueue

AAAA = dnslib.QTYPE.reverse['AAAA']
udpsock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_UDP)
PORT = 53

def handle_packet(pkt):
  s = time.time()
  try:
    ip = pkt.get_payload()
    # 28 = 20B IPv4 header + 8B UDP header
    dns = DNSRecord.parse(ip[28:])
    if dns.q.qtype == AAAA:
      ret = dns.reply()
      src = socket.inet_ntoa(ip[12:16])
      sport = struct.unpack('!H', ip[20:22])[0]
      p = ret.pack()
      # print(ret, p)
      checksum = 0
      p = struct.pack('!HHHH', PORT, sport, len(p) + 8, checksum) + p
      udpsock.sendto(p, (src, sport))
      pkt.drop()
    else:
      pkt.accept()
  except KeyboardInterrupt:
    pkt.accept()
    raise
  except Exception:
    traceback.print_exc()
    pkt.accept()
  e = time.time()
  print('%.3fms' % ((e - s) * 1000))

def main():
  nfqueue = NetfilterQueue()
  nfqueue.bind(1, handle_packet)
  try:
    nfqueue.run()
  except KeyboardInterrupt:
    print()

def quit(signum, sigframe):
  raise KeyboardInterrupt

if __name__ == '__main__':
  signal.signal(signal.SIGTERM, quit)
  signal.signal(signal.SIGQUIT, quit)
  signal.signal(signal.SIGHUP, quit)
  subprocess.check_call(['iptables', '-I', 'INPUT', '-p', 'udp', '-m', 'udp', '--dport', str(PORT), '-j', 'NFQUEUE', '--queue-num', '1'])
  try:
    main()
  finally:
    subprocess.check_call(['iptables', '-D', 'INPUT', '-p', 'udp', '-m', 'udp', '--dport', str(PORT), '-j', 'NFQUEUE', '--queue-num', '1'])

写好之后、准备部署前,我还担心了一下 Python 的执行效率——要是请求太多处理不过来就麻烦了,得搞多进程呢。看了一下,一个包只有 3ms 的处理时间。然后发现 Python 其实也没有那么慢嘛,绝大部分时候不到 1ms 就搞定了~

部署到咱的 DNS 服务器上之后,AAAA 记录回应迅速,再也不会慢了~

调试网络程序,Wireshark 就是好用!

PS: 后来有人告诉我改 gai.conf 也可以。我试了一下,如下设置并没有阻止 glibc 请求 AAAA 记录——它压根就没读这个文件!

precedence ::ffff:0:0/96  100

PPS: 我还发现发送这两个 DNS 请求,glibc 2.12 用了两次 sendto,但是 Arch Linux 上的 glibc 2.23 只用了一次 sendmmsg~所以大家还是尽量升级吧,有好处的呢。

Category: 网络 | Tags: python DNS iptables 中国特色
4
11
2016
19

MongoDB 到底要吃多少内存?

发现一只32G内存的服务器,上边跑了几个 sharding 模式的 mongod,把内存吃到只剩下4G,8G swap 更是丁点不剩。

我见过吃内存的 mongod,可没见过大胃口的 mongod 啊。不过以前我也没怎么见到在这么大内存的机器上跑的 mongod。不过不管如何,把 swap 全吃掉总归是不对的。

于是翻了翻 mongodb 源码,发现出现这种情况还真是机器的配置的问题。代码里有这么一段(在 GitHub 上的位置):

        if (cacheSizeGB == 0) {
            // Since the user didn't provide a cache size, choose a reasonable default value.
            // We want to reserve 1GB for the system and binaries, but it's not bad to
            // leave a fair amount left over for pagecache since that's compressed storage.
            ProcessInfo pi;
            double memSizeMB = pi.getMemSizeMB();
            if (memSizeMB > 0) {
                double cacheMB = (memSizeMB - 1024) * 0.6;
                cacheSizeGB = static_cast<size_t>(cacheMB / 1024);
                if (cacheSizeGB < 1)
                    cacheSizeGB = 1;
            }
        }

大概这就是决定它自己要用多少内存的代码了。先留出1G,然后再留出40%,剩下的能吃就吃!于是,好几只 mongod 开始抢食了!默认vm.swappiness=60的内核看到内存快用完了,于是开始往 swap 挪。结果造成内核挪多少,mongod 吃多少……

这种情况在机器内存少的时候没有出现,大概是因为内存少的时候,mongod 留出的比例比较高,内核就没那么卖力地把数据往 swap 上挪了。而且这次是好几只 mongod 哄抢呢。

Category: 网络 | Tags: 数据库 linux mongodb
3
8
2016
2

ssh 会话复用及用户级的 sleep.target

这里看到 ssh 的 Control master 特性之后,就在~/.ssh/config里启用了这个特性:

ControlPath ~/.ssh/master-%r@%h:%p
ControlMaster auto
ControlPersist yes
Compression yes

会话连接复用,对于以交互操作的使用,很不错的!对低延迟的服务器可能只是少了用户认证过程,但对于连接国外服务器,少了 TCP 握手、SSH 握手与认证等来来回回的过程,连接会快非常多的,尤其是对于常用的服务器,比如 GitHub 之类的,提速非常明显。

然后,问题就来了:系统挂起再恢复之后,大部分连接会 stalled,需要手工断开连接。即使配置了超时,它也不一定及时。于是我想,既然 netctl-auto 之类的服务能够在挂起系统时适当处理,那么我是不是也能写一个用户级的 systemd 服务来处理这件事情呢?

于是我按系统级的配置方法弄好了,结果什么也没有发生……后来才明白,只有系统级的 sleep.target,没有用户级的啊。

在 Arch Linux 官方论坛看到有人这么尝试,让系统级的 systemd 调用用户级的 systemd。配置有点不对,但是想法是非常好的!

于是就有了我现在用的方案:

[Unit]
Description=sleep.target of a systemd user session
Before=sleep.target
StopWhenUnneeded=yes

[Service]
Type=oneshot
User=%I
Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/%I/bus
RemainAfterExit=yes
ExecStart=/usr/bin/systemctl --user start sleep.target
ExecStop=/usr/bin/systemctl --user stop sleep.target

[Install]
WantedBy=sleep.target

启用(enable)user-sleep@1000.service之后,系统挂起时,就会调用 ID 为 1000 的用户的用户级 systemd,也 reach sleep.target 啦。当然这个用户级的 sleep.target 也得自己写:

[Unit]
Description=Sleep
Documentation=man:systemd.special(7)
DefaultDependencies=no
StopWhenUnneeded=yes

然后就可以让用户级的服务WantedBy=sleep.target啦~

Category: 网络 | Tags: linux ssh systemd
12
5
2015
21

博客「被入侵」之谜底

2015年9月13日,我的博客流量突然少了很多:

来自百度的访问量下降

因为博客突然被百度标记为「安全联盟提醒您:该页面可能已被非法篡改!」(后边那个峰的流量来源于知乎,与此事件并无关联。)

然而我一直未发现任何异常。我使用了 Google 站长工具,Google 也没说有任何异常。所以我以为这不过是百度又发了什么神经。毕竟它也说,「有一个网友举报」。「安全联盟」那里可以申请解封,但是需要出卖手机号等隐私。更神奇的是,它要求你修正问题,却连是什么问题都不清楚。「安全联盟」的客服表示,这事他们也不清楚。

然而几天前,又有网友报告我的博客跳转到了奇怪的页面。第一次有人报告时我只当用户系统或者网络的问题,虽然我也疑惑,恶意软件或者 ISP 插广告不至于插 CJB 的广告啊。但是对方并没有能力来调查此事。这次总算是遇到了一个会抓包的读者了。于是让报告者 @xuboying 帮忙抓包,这才真相大白。

简单地说,确实有页面在传输过程中被篡改了。至于是不是非法的,就得问「有关部门」了。

事情是这个样子的:我有一篇文章嵌入了 GitHub Gist。而 GitHub Gist 的域名 gist.github.com 被污染,其中一个污染 IP 为 216.234.179.13。这大概是 CJB 的源服务器地址。

本来呢,嵌入 GitHub Gist 的代码是这样子:

<script src="https://gist.github.com/lilydjwg/0bfa6807b88e6d39a995.js"></script>

当解析到 216.234.179.13 之后,最奇妙的事情发生了:

  1. CJB 使用了自签名、过期、弱密钥的证书。火狐会嫌它太弱而不可覆盖地拒绝,其它主流浏览器也会因为证书问题而报错。小米浏览器会询问用户是否接受有问题的证书(普通用户哪里懂这个啊,估计大都会选择接受吧)。一旦接受,则进入下一步。
  2. 该服务器在访问时会返回一句复合了 HTML 和 JavaScript 的脚本,修改window.location到 CJB 的主页。而引入 GitHub Gist 的方式恰好是 JavaScript 脚本,于是它得到执行,跳转到 CJB 主页去了……(不过现在只会返回空白页了。)

后来百度取消了那个「被非法篡改」的提示,不过权重依然很低,不注意看根本找不到我博客唉。

知道是 GitHub gist 的原因之后就可以很快找到遇到同样问题的人了,比如:

Category: 网络 | Tags: 网络 博客 安全 中国特色
11
22
2015
26

获取 Google Chrome 中打开的标签页的 URL

Google Chrome 把这些信息保存在配置目录下的Current Session文件里了,比如我的位于~/.config/google-chrome/Default/Current Session。这个文件是二进制的,使用 Chromagnon 里的脚本可以解析成文本,然后拿 Vim 编辑编辑就可以得到一行一个链接的文本文件了。这样就可以很容易地把它们在火狐里打开了,比如:

cat links | xargs firefox

这样就把链接们从 Google Chrome 里迁移到了一个单独的火狐窗口中。

我做这个的原因是,最近我的火狐运行微信时会持续占用一个 CPU 核。改用 Google Chrome 来运行微信之后风扇才能安静下来。然而 Google Chrome 的字体渲染我并不喜欢,更重要的是它会不时地崩溃一下。所以我辛辛苦苦「转发」了N个链接到「文件传输助手」,并艰难地(因为这个过程非常卡)将它们一一点开之后,希望能把这些页面转到火狐中来阅读。

暂时手工操作了。以后用得多的话再写自动化脚本。

Category: 网络 | Tags: 微信 Google Chrome 火狐
11
11
2015
27

使用 RSS 订阅知乎专栏

知乎专栏不提供 RSS,那我自己做一个好了。用法见网站:https://rss.lilydjwg.me/。计划是支持各种我想要使用 RSS 订阅却又不提供的网站。当然目前只支持知乎专栏啦。代码是开源的,欢迎来提交 pull request :-)

2015年11月12日更新:其实我所使用的 RSS 阅读器——InoReader——是支持订阅知乎专栏的……不过我有对其排版做处理的,就是删掉所有的 <br>。知乎专栏的页面有时会有很多 <br>,其页面使用 CSS 隐藏掉了一些 <br>,但是 RSS 阅读器里它们都显示出来了,搞得段落间距很大。

我这个程序打算支持多种多样的来源的。而最初的想法是针对性地做中文乱码的处理,只是现在那些乱码早已消失了。知乎专栏只是开了个头,以后有需要慢慢加别的东西啦。

2016年12月14日更新:支持订阅知乎用户的动态了。

Category: 网络 | Tags: rss 知乎

部分静态文件存储由又拍云存储提供。 | Theme: Aeros 2.0 by TheBuckmaker.com