Julia Evans 发布的文章

嗨!今天我 在 twitter 询问有没有一些像 ripgrepfdfzfexabat 之类的新式命令行工具。

我收到了一大堆回复,都是我没有听说过的工具,所以我想我应该在这里列出一个清单。另外,很多人还指出有一个 modern-unix 的列表。

标准工具的替代物

新的发明

这里有一些不完全是替代标准工具的工具:

  • zfasdautojumpzoxide (使查找文件/改变目录更容易的工具)
  • brootnnnranger (文件管理器)
  • direnv (根据当前目录加载环境变量)
  • fzfpeco (“模糊查找器”)
  • crocmagic-wormhole (将文件从一台计算机发送到另一台)
  • hyperfine 基准测试
  • httpiecurliexh (用于发出 HTTP 请求)
  • entr (当文件改变时运行任意命令)
  • asdf (多语言的版本管理器)
  • tiglazygitgit 的交互界面)
  • lazydockerdocker 的交互式界面)
  • choose (基本版的 awk/cut
  • ctop (容器的 top
  • fuck (自动纠正命令行错误)
  • tmate (与朋友分享你的终端)
  • lnavangle-grinder (管理日志的工具)
  • mdpglow (在终端显示 Markdown 的方法)
  • pbcopy/pbpaste(用于剪贴板和标准输入/标准输出之间)可能不是“新的”,但被提到了很多次。你可以使用 xclip 在 Linux 上做同样的事情

JSON/YAML/CSV 工具

  • jq (一个很好的 JSON 处理工具)
  • jc (将各种工具的输出转换成 JSON)
  • jo (创建 JSON 对象)
  • yq (类似 jq,但用于 YAML),还有 另一个 yq
  • fq (类似 jq,但用于二进制)
  • htmlq (类似 jq,但用于 HTML)
  • fx (交互式 JSON 工具)
  • jless (JSON 分页器)
  • xsv (一个用于 CSV 文件的命令行工具,来自 burntsushi)
  • visidata (“一个用于表格数据的交互式多功能工具”)
  • miller (“像用于 CSV/TSV/JSON/JSON 的 awk/sed/cut/join/sort”)

grep 工具

  • pdfgrep (用于 PDF 的 grep
  • gron (用于 JSON 的 grep
  • ripgrep-allripgrep,但也用于 PDF、zip、电子书等)

不太新的工具

下面是人们提到的一些不那么新的工具,但不那么知名:

  • pv (“管道查看程序”,给你一个管道的进度条)
  • vidir (来自 moreutils,可以让你在 vim 中批量重命名/删除文件)
  • spongetsparallel (也来自 moreutils)

我的一些最爱

我最喜欢的是 entrripgrepgit-deltahttpieplocatejq,这些都是我已经使用过的。

我也想尝试一下 direnvbtmzxsvduf,但我认为我知道到的最令人兴奋的工具是 vidir


via: https://jvns.ca/blog/2022/04/12/a-list-of-new-ish--command-line-tools/

作者:Julia Evans 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

大家好!几天前我写了篇 小型的个人程序 的文章,里面提到了调用没有文档说明的“秘密” API 很有意思,你需要从你的浏览器中把 cookies 复制出来才能访问。

有些读者问如何实现,因此我打算详细描述下,其实过程很简单。我们还会谈谈在调用没有文档说明的 API 时,可能会遇到的错误和道德问题。

我们用谷歌 Hangouts 举例。我之所以选择它,并不是因为这个例子最有用(我认为官方的 API 更实用),而是因为在这个场景中更有用的网站很多是小网站,而小网站的 API 一旦被滥用,受到的伤害会更大。因此我们使用谷歌 Hangouts,因为我 100% 肯定谷歌论坛可以抵御这种试探行为。

我们现在开始!

第一步:打开开发者工具,找一个 JSON 响应

我浏览了 https://hangouts.google.com,在Firefox的开发者工具中打开“ruby网络rtNetwork/rt/ruby”标签,找到一个 JSON 响应。你也可以使用 Chrome 的开发者工具。

打开之后界面如下图:

找到其中一条 “ 类型 Type ” 列显示为 json 的请求。

为了找一条感兴趣的请求,我找了好一会儿,突然我找到一条 “people” 的端点,看起来是返回我们的联系人信息。听起来很有意思,我们来看一下。

第二步:复制为 cURL

下一步,我在感兴趣的请求上右键,点击 “复制Copy” -> “ 复制为 cURL Copy as cURL ”。

然后我把 curl 命令粘贴到终端并运行。下面是运行结果:

$ curl 'https://people-pa.clients6.google.com/v2/people/?key=REDACTED' -X POST ........ (省略了大量请求标头)
Warning: Binary output can mess up your terminal. Use "--output -" to tell 
Warning: curl to output it to your terminal anyway, or consider "--output 
Warning: <FILE>" to save to a file.

你可能会想 —— 很奇怪,“二进制的输出在你的终端上无法正常显示” 是什么错误?原因是,浏览器默认情况下发给服务器的请求头中有 Accept-Encoding: gzip, deflate 参数,会把输出结果进行压缩。

我们可以通过管道把输出传递给 gunzip 来解压,但是我们发现不带这个参数进行请求会更简单。因此我们去掉一些不相关的请求头。

第三步:去掉不相关的请求头

下面是我从浏览器获得的完整 curl 命令。有很多行!我用反斜杠(\)把请求分开,这样每个请求头占一行,看起来更清晰:

curl 'https://people-pa.clients6.google.com/v2/people/?key=REDACTED' \
-X POST \
-H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:96.0) Gecko/20100101 Firefox/96.0' \
-H 'Accept: */*' \
-H 'Accept-Language: en' \
-H 'Accept-Encoding: gzip, deflate' \
-H 'X-HTTP-Method-Override: GET' \
-H 'Authorization: SAPISIDHASH REDACTED' \
-H 'Cookie: REDACTED'
-H 'Content-Type: application/x-www-form-urlencoded' \
-H 'X-Goog-AuthUser: 0' \
-H 'Origin: https://hangouts.google.com' \
-H 'Connection: keep-alive' \
-H 'Referer: https://hangouts.google.com/' \
-H 'Sec-Fetch-Dest: empty' \
-H 'Sec-Fetch-Mode: cors' \
-H 'Sec-Fetch-Site: same-site' \
-H 'Sec-GPC: 1' \
-H 'DNT: 1' \
-H 'Pragma: no-cache' \
-H 'Cache-Control: no-cache' \
-H 'TE: trailers' \
--data-raw 'personId=101777723309&personId=1175339043204&personId=1115266537043&personId=116731406166&extensionSet.extensionNames=HANGOUTS_ADDITIONAL_DATA&extensionSet.extensionNames=HANGOUTS_OFF_NETWORK_GAIA_GET&extensionSet.extensionNames=HANGOUTS_PHONE_DATA&includedProfileStates=ADMIN_BLOCKED&includedProfileStates=DELETED&includedProfileStates=PRIVATE_PROFILE&mergedPersonSourceOptions.includeAffinity=CHAT_AUTOCOMPLETE&coreIdParams.useRealtimeNotificationExpandedAcls=true&requestMask.includeField.paths=person.email&requestMask.includeField.paths=person.gender&requestMask.includeField.paths=person.in_app_reachability&requestMask.includeField.paths=person.metadata&requestMask.includeField.paths=person.name&requestMask.includeField.paths=person.phone&requestMask.includeField.paths=person.photo&requestMask.includeField.paths=person.read_only_profile_info&requestMask.includeField.paths=person.organization&requestMask.includeField.paths=person.location&requestMask.includeField.paths=person.cover_photo&requestMask.includeContainer=PROFILE&requestMask.includeContainer=DOMAIN_PROFILE&requestMask.includeContainer=CONTACT&key=REDACTED'

第一眼看起来内容有很多,但是现在你不需要考虑每一行是什么意思。你只需要把不相关的行删掉就可以了。

我通常通过删掉某行查看是否有错误来验证该行是不是可以删除 —— 只要请求没有错误就一直删请求头。通常情况下,你可以删掉 Accept*RefererSec-*DNTUser-Agent 和缓存相关的头。

在这个例子中,我把请求删成下面的样子:

curl 'https://people-pa.clients6.google.com/v2/people/?key=REDACTED' \
-X POST \
-H 'Authorization: SAPISIDHASH REDACTED' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H 'Origin: https://hangouts.google.com' \
-H 'Cookie: REDACTED'\
--data-raw 'personId=101777723309&personId=1175339043204&personId=1115266537043&personId=116731406166&extensionSet.extensionNames=HANGOUTS_ADDITIONAL_DATA&extensionSet.extensionNames=HANGOUTS_OFF_NETWORK_GAIA_GET&extensionSet.extensionNames=HANGOUTS_PHONE_DATA&includedProfileStates=ADMIN_BLOCKED&includedProfileStates=DELETED&includedProfileStates=PRIVATE_PROFILE&mergedPersonSourceOptions.includeAffinity=CHAT_AUTOCOMPLETE&coreIdParams.useRealtimeNotificationExpandedAcls=true&requestMask.includeField.paths=person.email&requestMask.includeField.paths=person.gender&requestMask.includeField.paths=person.in_app_reachability&requestMask.includeField.paths=person.metadata&requestMask.includeField.paths=person.name&requestMask.includeField.paths=person.phone&requestMask.includeField.paths=person.photo&requestMask.includeField.paths=person.read_only_profile_info&requestMask.includeField.paths=person.organization&requestMask.includeField.paths=person.location&requestMask.includeField.paths=person.cover_photo&requestMask.includeContainer=PROFILE&requestMask.includeContainer=DOMAIN_PROFILE&requestMask.includeContainer=CONTACT&key=REDACTED'

这样我只需要 4 个请求头:AuthorizationContent-TypeOriginCookie。这样容易管理得多。

第四步:在 Python 中发请求

现在我们知道了我们需要哪些请求头,我们可以把 curl 命令翻译进 Python 程序!这部分是相当机械化的过程,目标仅仅是用 Python 发送与 cUrl 相同的数据。

下面是代码实例。我们使用 Python 的 requests 包实现了与前面 curl 命令相同的功能。我把整个长请求分解成了元组的数组,以便看起来更简洁。

import requests
import urllib

data = [
    ('personId','101777723'), # I redacted these IDs a bit too
    ('personId','117533904'),
    ('personId','111526653'),
    ('personId','116731406'),
    ('extensionSet.extensionNames','HANGOUTS_ADDITIONAL_DATA'),
    ('extensionSet.extensionNames','HANGOUTS_OFF_NETWORK_GAIA_GET'),
    ('extensionSet.extensionNames','HANGOUTS_PHONE_DATA'),
    ('includedProfileStates','ADMIN_BLOCKED'),
    ('includedProfileStates','DELETED'),
    ('includedProfileStates','PRIVATE_PROFILE'),
    ('mergedPersonSourceOptions.includeAffinity','CHAT_AUTOCOMPLETE'),
    ('coreIdParams.useRealtimeNotificationExpandedAcls','true'),
    ('requestMask.includeField.paths','person.email'),
    ('requestMask.includeField.paths','person.gender'),
    ('requestMask.includeField.paths','person.in_app_reachability'),
    ('requestMask.includeField.paths','person.metadata'),
    ('requestMask.includeField.paths','person.name'),
    ('requestMask.includeField.paths','person.phone'),
    ('requestMask.includeField.paths','person.photo'),
    ('requestMask.includeField.paths','person.read_only_profile_info'),
    ('requestMask.includeField.paths','person.organization'),
    ('requestMask.includeField.paths','person.location'),
    ('requestMask.includeField.paths','person.cover_photo'),
    ('requestMask.includeContainer','PROFILE'),
    ('requestMask.includeContainer','DOMAIN_PROFILE'),
    ('requestMask.includeContainer','CONTACT'),
    ('key','REDACTED')
]
response = requests.post('https://people-pa.clients6.google.com/v2/people/?key=REDACTED',
    headers={
        'X-HTTP-Method-Override': 'GET',
        'Authorization': 'SAPISIDHASH REDACTED',
        'Content-Type': 'application/x-www-form-urlencoded',
        'Origin': 'https://hangouts.google.com',
        'Cookie': 'REDACTED',
    },
    data=urllib.parse.urlencode(data),
)

print(response.text)

我执行这个程序后正常运行 —— 输出了一堆 JSON 数据!太棒了!

你会注意到有些地方我用 REDACTED 代替了,因为如果我把原始数据列出来你就可以用我的账号来访问谷歌论坛了,这就很不好了。

运行结束!

现在我可以随意修改 Python 程序,比如传入不同的参数,或解析结果等。

我不打算用它来做其他有意思的事了,因为我压根对这个 API 没兴趣,我只是用它来阐述请求 API 的过程。

但是你确实可以对返回的一堆 JSON 做一些处理。

curlconverter 看起来很强大

有人评论说可以使用 https://curlconverter.com/ 自动把 curl 转换成 Python(和一些其他的语言!),这看起来很神奇 —— 我都是手动转的。我在这个例子里使用了它,看起来一切正常。

追踪 API 的处理过程并不容易

我不打算夸大追踪 API 处理过程的难度 —— API 的处理过程并不明显!我也不知道传给这个谷歌论坛 API 的一堆参数都是做什么的!

但是有一些参数看起来很直观,比如 requestMask.includeField.paths=person.email 可能表示“包含每个人的邮件地址”。因此我只关心我能看懂的参数,不关心看不懂的。

(理论上)适用于所有场景

可能有人质疑 —— 这个方法适用于所有场景吗?

答案是肯定的 —— 浏览器不是魔法!浏览器发送给你的服务器的所有信息都是 HTTP 请求。因此如果我复制了浏览器发送的所有的 HTTP 请求头,那么后端就会认为请求是从我的浏览器发出的,而不是用 Python 程序发出的。

当然,我们去掉了一些浏览器发送的请求头,因此理论上后端是可以识别出来请求是从浏览器还是 Python 程序发出的,但是它们通常不会检查。

这里有一些对读者的告诫 —— 一些谷歌服务的后端会通过令人难以理解(对我来说是)方式跟前端通信,因此即使理论上你可以模拟前端的请求,但实际上可能行不通。可能会遭受更多攻击的大型 API 会有更多的保护措施。

我们已经知道了如何调用没有文档说明的 API。现在我们再来聊聊可能遇到的问题。

问题 1:会话 cookie 过期

一个大问题是我用我的谷歌会话 cookie 作为身份认证,因此当我的浏览器会话过期后,这个脚本就不能用了。

这意味着这种方式不能长久使用(我宁愿调一个真正的 API),但是如果我只是要一次性快速抓取一小组数据,那么可以使用它。

问题 2:滥用

如果我正在请求一个小网站,那么我的 Python 脚本可能会把服务打垮,因为请求数超出了它们的处理能力。因此我请求时尽量谨慎,尽量不过快地发送大量请求。

这尤其重要,因为没有官方 API 的网站往往是些小网站且没有足够的资源。

很明显在这个例子中这不是问题 —— 我认为在写这篇文章的过程我一共向谷歌论坛的后端发送了 20 次请求,他们肯定可以处理。

如果你用自己的账号身份过度访问这个 API 并导致了故障,那么你的账号可能会被暂时封禁(情理之中)。

我只下载我自己的数据或公共的数据 —— 我的目的不是寻找网站的弱点。

请记住所有人都可以访问你没有文档说明的 API

我认为本文最重要的信息并不是如何使用其他人没有文档说明的 API。虽然很有趣,但是也有一些限制,而且我也不会经常这么做。

更重要的一点是,任何人都可以这么访问你后端的 API!每个人都有开发者工具和网络标签,查看你传到后端的参数、修改它们都很容易。

因此如果一个人通过修改某些参数来获取其他用户的信息,这不值得提倡。我认为提供公开 API 的大部分开发者们都知道,但是我之所以再提一次,是因为每个初学者都应该了解。: )


via: https://jvns.ca/blog/2022/03/10/how-to-use-undocumented-web-apis/

作者:Julia Evans 选题:lujun9972 译者:lxbwolf 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

使得 DNS 难以理解的事情之一是它是 分布式的。有成千上万(也许是几十万?我不知道!)的 权威性域名服务器 authoritative nameserver ,以及至少 1000 万个解析器。而且它们正在运行许多不同的软件!不同服务器运行着不同的软件意味着 DNS 的工作方式有很多不一致的地方,这可能导致各种令人沮丧的问题。

但是,与其谈论这些问题,我更感兴趣的是弄清楚 —— 为什么 DNS 是分布式的是一件好事?

为什么 DNS 是分布式的是件好事?

一个原因是 可扩展性 —— DNS 的分布式设计使其更容易扩展,对故障的恢复能力更强。我发现,尽管 DNS 已经有近 40 年的历史,但它的扩展性仍然很好,这真是令人惊讶。这一点非常重要,但这并不是这篇文章的主题。

相反,我想说的是,它是分布式的意味着你可以 控制 你的 DNS 的工作方式。你可以向巨大而复杂的 DNS 服务器中添加更多的服务器!添加你控制的服务器!

昨天我 在 Twitter 上问 为什么你要运行自己的 DNS 服务器,我得到了很多很好的答案,我想在这里总结一下。

你可以运行 2 种类型的 DNS 服务器

你可以运行 2 种主要类型的 DNS 服务器:

  1. 如果你拥有一个域名,你可以为该域名运行一个 权威名称服务器
  2. 如果你有一台电脑(或一个有很多电脑的公司),你可以运行一个 解析器 来为这些电脑解析 DNS。

DNS 不是静态数据库

我经常看到 DNS 的 “电话簿” 比喻,域名就像人名,IP 地址就像电话号码。

这是一个不错的思维模型。但是,“电话簿” 思维模型可能会使你认为,如果你对 google.com 进行 DNS 查询,你将永远得到相同的结果。而这是不正确的。

你在 DNS 查询中得到的记录可能取决于:

  • 你在世界的哪个地方(也许你会得到一个离你更近的服务器的 IP 地址)
  • 你是否在企业网络里(你可以在其中解析内部域名)
  • 该域名是否被你的 DNS 解析器认为是 “坏” 的(它可能被封锁!)
  • 之前的 DNS 查询(也许 DNS 解析器正在做基于 DNS 的负载平衡,每次给你一个不同的 IP 地址)
  • 你是否在使用机场 Wi-Fi 专属门户(机场 Wi-Fi 会在你登录前以不同的方式解析 DNS 记录,它会给你发送一个特殊的 IP 来重定向你)
  • 随便什么

你可能想控制你自己的服务器的很多原因都与 DNS 不是一个静态数据库这一事实有关 —— 对于如何处理 DNS 查询,你可能会有不同的选择(无论是为你的域名还是为你的组织)。

运行权威性名称服务器的理由

这些原因并没有任何特定的顺序。

对于其中一些原因,你不一定必须要运行你自己的权威名称服务器,你只需选择提供了该功能的权威名称服务器服务就行了。

要明确的是:有很多理由 运行自己的权威名称服务器 —— 我就没有运行,我也不想说服你应该这样做。它需要时间来维护,你的服务可能不那么可靠,等等。

原因:安全

这条推文说的很好

[存在] 攻击者通过你的 DNS 供应商的客户支持人员获得 DNS 变更权限的风险,客服本来只应该提供帮助。他们可以被你的 DNS 阻止(也许就是因为缺少这个)。内部可能更容易审计和验证内容。

原因:你喜欢运行 bind/nsd

有几个人提到的一个原因是:“我习惯于编写区域文件和运行 bindnsd ,对我来说这样做更容易。”

如果你喜欢 bind/nsd 的方式,但又不想运维自己的服务器,有几个人提到,你也可以通过运行一个 “隐藏的主服务器” 来获得 bind 的优势,该服务器存储记录,但从一个 “辅助” 服务器提供所有的实际 DNS 查询。这里有一些我发现的关于配置辅助 DNS 的网页,以 NS1cloudflareDyn 作为示例。

我真的不知道什么是最佳的权威 DNS 服务器。我想我只在工作中使用过 nsd。

原因:你可以使用新的记录类型

并非所有的 DNS 服务都支持某些较新的 DNS 记录类型,但如果你运行你自己的 DNS,你就可以支持任何你想要的记录类型。

原因:用户界面

你可能不喜欢你正在使用的 DNS 服务的用户界面(或 API,或干脆没有 API)。这与 “你喜欢运行 BIND ”的原因差不多,也许你喜欢编写区域文件的方式。

原因:你可以自己修复问题

当问题出现时,能够自己解决,有一些明显的优点和缺点(优点:你可以解决问题,缺点:你必须解决问题)。

原因:做一些奇怪的、自定义的事情

你可以写一个 DNS 服务器,做任何你想做的事情,它不一定要只返回一组静态记录。

有几个例子:

原因:省钱

权威名称服务器似乎一般按每百万次 DNS 查询收费。比如,似乎 Route 53 每百万次查询收费 0.5 美元,NS1 每百万次查询收费约 8 美元。

我对一个大型网站的权威 DNS 服务器实际需要解决多少次查询没有概念(哪些类型的网站会对其权威 DNS 服务器进行 10 亿次 DNS 查询?可能是很多,但我没有这方面的经验)。但是有几个人在回复中提到成本是一个原因。

原因:你可以改变你的注册商

如果你为你的域名使用一个单独的权威名称服务器,而不是你的注册商的名称服务器,那么当你转移到一个不同的注册商时,你所要做的就是把你的权威 DNS 服务器设置为正确的值,从而使你的 DNS 恢复正常。你不需要迁移你所有的 DNS 记录,那非常痛苦。

但你不需要为此而运行你自己的名字服务器。

原因:地理 DNS

你可能想根据客户的位置为你的域名返回不同的 IP 地址,给他们一个离他们很近的服务器。

这是很多权威的域名服务器服务所提供的服务,你不需要为此而专门运行名字服务器。

原因:避免针对他人的拒绝服务攻击

许多权威 DNS 服务器是共享的。这意味着,如果有人攻击 google.com 或其他的 DNS 服务器,而你恰好在使用同一个权威 DNS 服务器,你可能会受到影响,即使攻击不是针对你。例如,2016 年的这次 对 Dyn 的 DDoS 攻击

原因:把所有的配置放在一个地方

有一个人提到,他们喜欢把所有的配置(DNS 记录、let's encrypt、nginx 等)放在一台服务器上的同一个地方。

另类原因:把 DNS 当作 VPN 使用

显然,iodine 是一个可以让你通过 DNS 隧道传输流量的权威 DNS 服务器,它可以让你像 VPN 一样与外界联系,

运行解析器的原因

原因:隐私

如果有人能看到你所有的 DNS 查询,他们就有你(或你组织中的每个人)正在访问的所有域名的完整列表!你可能更愿意保持这种隐私。你可能更愿意保持这种隐私。

原因:阻止恶意网站

如果你运行你自己的解析器,你可以(通过不返回任何结果)拒绝解析你认为 “坏” 的域名的 DNS 查询。

几个你可以自己运行(或只是使用)的解析器的例子:

  • Pi-Hole 阻止广告商
  • Quad9 阻止做恶意软件/网络钓鱼/间谍软件的域名。 Cloudflare 似乎有一个 类似的服务
  • 我想也有一些企业安全软件会阻止那些托管恶意软件的域名的 DNS 查询
  • DNS 不是静态数据库。它是非常动态的,答案往往实时取决于查询的 IP 地址、内容服务器的当前负载等。除非你将这些记录的服务委托给做出这些决定的实体,否则很难实时实现。
  • DNS 委派控制使访问控制非常简单。从区域中切下的部分都由控制委派服务器的人控制,所以对一个主机名的责任是隐含在 DNS 委派中的。

原因:在 nginx 中获得动态代理

这里有一个很酷的故事,来自 这条推文

我在一个应用程序中写了一个 DNS 服务器,然后把它设置为 nginx 的解析器,这样我就可以获得动态的后端代理,而不需要 nginx 运行 lua。Nginx 向应用程序发送 DNS 查询,应用程序查询 Redis 并作出相应的反应。这对我正在做的事情来说,效果非常好。

原因:避免恶意解析器

一些 ISP 运行的 DNS 解析器会做一些不好的事情,比如把不存在的域名指向他们控制的 IP,向你显示广告或他们控制的奇怪的搜索页面。

使用你控制的解析器或你信任的另一个解析器可以帮助你避免这种情况。

原因:解析内部域名

你可能有一个内部网络,其域名(比如 blah.corp.yourcompany.com)并不在公共互联网上。为内部网络中的机器运行你自己的解析器,就有可能访问这些域名。

无论是访问只在本地的服务,还是为公共互联网上的服务获得本地地址,你都可以在家庭网络中做同样的事情。

原因:避免你的 DNS 查询被中间人劫持

有一个人

我在我的局域网路由器上运行了一个解析器,它的上游使用了基于 HTTPS 的 DNS(DoH),所以物联网和其他不支持 DoH 或 DoT 的设备不会在外面喷射明文 DNS 查询。

就是这样

对我来说,探索 DNS 的 “原因” 感觉很重要,因为它是一个如此复杂凌乱的系统,我认为大多数人如果不理解为什么这些复杂的东西是有用的,就很难有动力去学习这么复杂的主题。

感谢 Marie 和 Kamal 对这篇文章的讨论,也感谢 Twitter 上提供这些原因的所有人。


via: https://jvns.ca/blog/2022/01/05/why-might-you-run-your-own-dns-server-/

作者:Julia Evans 选题:lujun9972 译者:wxy 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

大家好!不久前我学会了如何使用着色器制作有趣的闪亮旋转八面体:

我的着色器能力仍然非常基础,但事实证明制作这个有趣的旋转八面体比我想象中要容易得多(从其他人那里复制了很多代码片段!)。

我在做这件事时, 从一个非常有趣的叫做 符号距离函数教程:盒子和气球 的教程中学到了“符号距离函数”的重要思路。

在本文中,我将介绍我用来学习编写简单着色器的步骤,并努力让你们相信着色器并不难入门!

更高级着色器的示例

如果你还没有看过用着色器做的真正有趣的事情,这里有几个例子:

  1. 这个非常复杂的着色器就像一条河流的真实视频:https://www.shadertoy.com/view/Xl2XRW
  2. 一个更抽象(更短!)有趣的着色器,它有很多发光的圆圈:https://www.shadertoy.com/view/lstSzj

步骤一:我的第一个着色器

我知道你可以在 shadertoy 上制作着色器,所以我去了 https://www.shadertoy.com/new。它们提供了一个默认着色器,如下图所示:

代码如下:

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    // 规范像素坐标 (从 0 到 1)
    vec2 uv = fragCoord / iResolution.xy;

    // 随时间改变像素颜色
    vec3 col = 0.5 + 0.5 * cos(iTime + uv.xyx + vec3(0, 2, 4));

    // 输出到屏幕
    fragColor = vec4(col, 1.0);
}

虽然还没有做什么令人兴奋的事情,但它已经教会了我着色器程序的基本结构!

思路:将一对坐标(和时间)映射到一个颜色

这里的思路是获得一对坐标作为输入(fragCoord),你需要输出一个 RGBA 向量作为此坐标的颜色。该函数也可以使用当前时间(iTime),图像从而可以随时间变化。

这种编程模型(将一对坐标和时间映射到其中)的巧妙之处在于,它非常容易并行化。我对 GPU 了解不多,但我的理解是,这种任务(一次执行 10000 个微不足道的可并行计算)正是 GPU 擅长的事情。

步骤二:使用 shadertoy-render 加快开发迭代

玩了一段时间的 shadertoy 之后,我厌倦了每次保存我的着色器时都必须在 shadertoy 网站上单击“重新编译”。

我找到了一个名为 shadertoy-render 命令行工具,它会在每次保存时实时查看文件并更新动画。现在我可以运行:

shadertoy-render.py circle.glsl

并更快地开发迭代!

步骤三:画一个圆圈

接下来我想 —— 我擅长数学!我可以用一些基本的三角学来画一个会弹跳的彩虹圈!

我知道圆的方程为(x^2 + y^2 = 任意正数!),所以我写了一些代码来实现它:

代码如下:(你也可以 在 shadertoy 上查看

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    // 规范像素坐标 (从 0 到 1)
    vec2 uv = fragCoord / iResolution.xy;
    // 绘制一个中心位置依赖于时间的圆
    vec2 shifted = uv - vec2((sin(iGlobalTime) + 1) / 2, (1 + cos(iGlobalTime)) / 2);
    if (dot(shifted, shifted) < 0.03) {
        // 改变像素颜色
        vec3 col = 0.5 + 0.5 * cos(iGlobalTime + uv.xyx + vec3(0, 2, 4));
        fragColor = vec4(col, 1.0);
    } else {
        // 使圆之外的其他像素都是黑色
        fragColor = vec4(0,0, 0, 1.0);
    }
}

代码将坐标向量 fragCoord 与自身点积,这与计算 x^2 + y^2 相同。我还在这个圆圈的中心玩了一点花活 – 圆心为 vec2((sin(iGlobalTime) + 1)/ 2,(1 + cos(faster)) / 2,这意味着圆心也随着时间沿另一个圆移动。

着色器是一种学习数学的有趣方式!

我觉得有意思的(即使我们没有做任何超级高级的事情!)是这些着色器为我们提供了一种有趣的可视化方式学习数学 - 我用 sincos 来使某些东西沿着圆移动,如果你想更直观地了解三角函数的工作方式, 也许编写着色器会是一种有趣的方法!

我喜欢的是,可以获得有关数学代码的即时视觉反馈 - 如果你把一些东西乘以 2,图像里的东西会变得更大!或更小!或更快!或更慢!或更红!

但是我们如何做一些真正有趣的事情呢?

这个会弹跳的圆圈很好,但它与我见过的其他人使用着色器所做的非常奇特的事情相去甚远。那么下一步要做什么呢?

思路:不要使用 if 语句,而是使用符号距离函数!

在我上面的圆圈代码中,我基本上是这样写的:

if (dot(uv, uv) < 0.03) {
    // 圆里的代码
} else {
    // 圆外的代码
}

但问题(也是我感到卡住的原因)是不清楚如何将它推广到更复杂的形状!编写大量的 if 语句似乎不太好用。那人们要如何渲染这些 3d 形状呢?

所以! 符号距离函数 Signed distance function 是定义形状的另一种方式。不是使用硬编码的 if 语句,而是定义一个 函数,该函数告诉你,对于世界上的任何一个点,该点与你的形状有多远。比如,下面是球体的符号距离函数。

float sdSphere( vec3 p, float center )
{
  return length(p) - center;
}

符号距离函数非常棒,因为它们:

  • 易于定义!
  • 易于组合!如果你想要一个被切去一块的球体, 你可以用一些简单的数学来计算并集/交集/差集。
  • 易于旋转/拉伸/弯曲!

制作旋转陀螺的步骤

当我开始时,我不明白需要编写什么代码来制作一个闪亮的旋转东西。结果表明如下是基本步骤:

  1. 为想要的形状创建一个符号距离函数(在我的例子里是八面体)
  2. 光线追踪符号距离函数,以便可以在 2D 图片中显示它(或沿光线行进?我使用的教程称之为光线追踪,我还不明白光线追踪和光线行进之间的区别)
  3. 编写代码处理形状的表面纹理并使其发光

我不打算在本文中详细解释符号距离函数或光线追踪,因为我发现这个 关于符号距离函数的神奇教程 非常友好,老实说,它比我做的更好,它解释了如何执行上述 3 个步骤,并且代码有大量的注释,非常棒。

步骤四:复制教程代码并开始更改内容

我在这里使用了久负盛名的编程实践,即“复制代码并以混乱的方式更改内容,直到得到我想要的结果”。

最后一堆闪亮的旋转八面体着色器在这里:https://www.shadertoy.com/view/wdlcR4

动画出来的样子是这样的:

为了做到这一点,我基本上只是复制了关于符号距离函数的教程,该函数根据符号距离函数呈现形状,并且:

  • sdfBalloon 更改为 sdfOctahedron,并使八面体旋转而不是在我的符号距离函数中静止不动
  • 修改 doBalloonColor 着色功能,使其有光泽
  • 有很多八面体而不是一个

使八面体旋转!

下面是我用来使八面体旋转的代码!事实证明这真的很简单:首先从 这个页面 复制一个八面体符号距离函数,然后添加一个 rotate 使其根据时间旋转,然后它就可以旋转了!

vec2 sdfOctahedron( vec3 currentRayPosition, vec3 offset ){
    vec3 p = rotate((currentRayPosition), offset.xy, iTime * 3.0) - offset;
    float s = 0.1; // s 是啥?
    p = abs(p);
    float distance = (p.x + p.y + p.z - s) * 0.57735027;
    float id = 1.0;
    return vec2( distance,  id );
}

用一些噪音让它发光

我想做的另一件事是让我的形状看起来闪闪发光/有光泽。我使用了在 这个 GitHub gist 中找到的噪声函数使表面看起来有纹理。

以下是我如何使用噪声函数的代码。基本上,我只是随机地将参数更改为噪声函数(乘以 2?3?1800?随你!),直到得到喜欢的效果。

float x = noise(rotate(positionOfHit, vec2(0, 0), iGlobalTime * 3.0).xy * 1800.0);
float x2 = noise(lightDirection.xy * 400.0);
float y = min(max(x, 0.0), 1.0);
float y2 = min(max(x2, 0.0), 1.0);
vec3 balloonColor = vec3(y, y + y2, y + y2);

编写着色器很有趣!

上面就是全部的步骤了!让这个八面体旋转并闪闪发光使我很开心。如果你也想用着色器制作有趣的动画,希望本文能帮助你制作出很酷的东西!

通常对于不太了解的主题,我可能在文章中说了至少一件关于着色器的错误事情,请让我知道错误是什么!

再说一遍,如下是我用到的两个资源:

  1. “符号距离函数教程:盒子和气球”:https://www.shadertoy.com/view/Xl2XWt(修改和玩起来真的很有趣)
  2. 可以将大量符号距离函数复制并粘贴到你的代码中:http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm

via: https://jvns.ca/blog/2020/03/15/writing-shaders-with-signed-distance-functions/

作者:Julia Evans 选题:lujun9972 译者:Starryi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

昨天,Facebook 发生了由 BGP 引起的离线事故。我对学习更多关于 BGP 的知识已经隐约感兴趣了很长时间,所以我阅读了一些文章。

我感到很沮丧,因为没有一篇文章告诉我如何在我的电脑上实际查找与 BGP 有关的信息,因此我 写了一条询问有关工具的推特

我一如既往地得到了一堆有用的回复,所以这篇博文展示了一些你可以用来查询 BGP 信息的工具。这篇文章中可能会有较多的错误,因为我对 BGP 不是很了解。

我不能发布 BGP 路由

我从来没有了解过 BGP 的原因之一是,据我所知,我没有在互联网上发布 BGP 路由的权限。

对于大多数网络协议,如果需要,你可以非常轻松地自己实现该协议。例如,你可以:

  • 发行你自己的 TLS 证书
  • 编写你自己的 HTTP 服务器
  • 编写你自己的 TCP 实现
  • 为你的域名编写你自己的权威 DNS 服务器(我现在正在为一个小项目尝试这样做)
  • 建立你自己的证书机构(CA)

但是对于 BGP,我认为除非你拥有自己的 ASN,否则你不能自己发布路由(你可以在你的家庭网络上实现 BGP,但这对我来说有点无聊,当我做实验的时候,我希望它们真的在真正的互联网上)。

无论如何,尽管我不能用它做实验,但我仍然认为它超级有趣,因为我喜欢网络,所以我将向你展示我找到的一些用来学习 BGP 的工具。

首先我们来谈谈 BGP 的一些术语。我打算很快掠过,因为我对工具更感兴趣,而且网上有很多关于 BGP 的高水平解释(比如这篇 cloudflare 的文章)。

AS 是什么?

我们首先需要了解的是 AS(“ 自治系统 autonomous system ”)。每个 AS:

  1. 由一个组织拥有(通常是一个大型组织,如你的 ISP、政府、大学、Facebook 等)。
  2. 控制一组特定的 IP 地址(例如,我的 ISP 的 AS 包括 247,808 个 IP 地址)。
  3. 有一个编号 ASN(如 1403)。

下面是我通过做一些实验对 AS 的一些观察:

  • 一些相当大的科技公司并没有自己的 AS。例如,我在 BGPView 上查看了 Patreon,就我所知,他们没有自己的 AS,他们的主要网站(patreon.com104.16.6.49)在 Cloudflare 的 AS 中。
  • 一个 AS 可以包括许多国家的 IP。Facebook 的 AS(AS32934)肯定有新加坡、加拿大、尼日利亚、肯尼亚、美国和其他国家的 IP 地址。
  • 似乎 IP 地址可以在一个以上的 AS 中。例如,如果我查找 209.216.230.240,它有 2 个 ASN 与之相关:AS6130AS21581。显然,当这种情况发生时,更具体的路线会被优先考虑 —— 所以到该 IP 的数据包会被路由到 AS21581

什么是 BGP 路由?

互联网上有很多的路由器。例如,我的 ISP 就有路由器。

当我给我的 ISP 发送一个数据包时(例如通过运行 ping 129.134.30.0),我的 ISP 的路由器需要弄清楚如何将我的数据包实际送到 IP 地址 129.134.30.0

路由器计算的方法是,它有一个路由表:这是个有一堆 IP 地址范围的列表(比如 129.134.30.0/23),以及它知道的到达该子网的路由。

下面是一个 129.134.30.0/23 (Facebook 的一个子网)的真实路由的例子。这不是来自我的 ISP。

11670 32934
  206.108.35.2 from 206.108.35.254 (206.108.35.254)
    Origin IGP, metric 0, valid, external
    Community: 3856:55000
    Last update: Mon Oct  4 21:17:33 2021

我认为这是在说通往 129.134.30.0 的一条路径是通过机器 206.108.35.2,这是在它的本地网络上。所以路由器接下来可能会把我的 ping 包发送到 206.108.35.2,然后 206.108.35.2 会知道如何把它送到 Facebook。开头的两个数字(11670 32934)是 ASN。

BGP 是什么?

我对 BGP 的理解非常浅薄,它是一个公司用来公布 BGP 路由的协议。

昨天发生在 Facebook 身上的事情基本上是他们发布了一个 BGP 公告,撤销了他们所有的 BGP 路由,所以世界上的每个路由器都删除了所有与 Facebook 有关的路由,没有流量可以到达那里。

好了,现在我们已经涵盖了一些基本的术语,让我们来谈谈你可以用来查看 AS 和 BGP 的工具吧!

工具 1:用 BGPView 查看你的 ISP 的 AS

为了使 AS 这个东西不那么抽象,让我们用一个叫做 BGPView的 工具来看看一个真实的 AS。

我的 ISP(EBOX)拥有 AS 1403。这是 我的 ISP 拥有的 IP 地址。如果我查找我的计算机的公共 IPv4 地址,我可以看到它是我的 ISP 拥有的IP地址之一:它在 104.163.128.0/17 块中。

BGPView 也有这个图,显示了我的 ISP 与其他 AS 的连接情况。

工具 2:traceroute -A 和 mtr -z

好了,我们感兴趣的是 AS 。让我们看看我从哪些 AS 中穿过。

traceroutemtr 都有选项可以告诉你每个 IP 的 ASN。其选项分别是 traceroute -Amtr -z

让我们看看我用 mtr 在去 facebook.com 的路上经过了哪些 AS!

$ mtr -z facebook.com
 1. AS???    LEDE.lan
 2. AS1403   104-163-190-1.qc.cable.ebox.net
 3. AS???    10.170.192.58
 4. AS1403   0.et-5-2-0.er1.mtl7.yul.ebox.ca
 5. AS1403   0.ae17.er2.mtl3.yul.ebox.ca
 6. AS1403   0.ae0.er1.151fw.yyz.ebox.ca
 7. AS???    facebook-a.ip4.torontointernetxchange.net
 8. AS32934  po103.psw01.yyz1.tfbnw.net
 9. AS32934  157.240.38.75
10. AS32934  edge-star-mini-shv-01-yyz1.facebook.com

这很有意思,看起来我们直接从我的 ISP 的 AS(1403)到 Facebook 的 AS(32934),中间有一个“互联网交换”。

我不确定 互联网交换 internet exchange (IX)是什么,但我知道它是互联网的一个极其重要的部分。不过这将是以后的事了。我猜是,它是互联网中实现“对等”的部分,就假设它是一个有巨大的交换机的机房,里面有无限的带宽,一堆不同的公司把他们的电脑放在里面,这样他们就可以互相发送数据包。

mtr 用 DNS 查找 ASN

我对 mtr 如何查找 ASN 感到好奇,所以我使用了 strace。我看到它看起来像是在使用 DNS,所以我运行了 dnspeep,然后就看到了!

$ sudo dnspeep
...
TXT   1.190.163.104.origin.asn.cymru.com 192.168.1.1          TXT: 1403 | 104.163.176.0/20 | CA | arin | 2014-08-14, TXT: 1403 | 104.163.160.0/19 | CA | arin | 2014-08-14, TXT: 1403 | 104.163.128.0/17 | CA | arin | 2014-08-14
...

所以,看起来我们可以通过查找 1.190.163.104.origin.asn.cymru.com 上的 txt 记录找到 104.163.190.1 的 ASN,像这样:

$ dig txt 1.190.163.104.origin.asn.cymru.com
1.190.163.104.origin.asn.cymru.com. 13911 IN TXT "1403 | 104.163.160.0/19 | CA | arin | 2014-08-14"
1.190.163.104.origin.asn.cymru.com. 13911 IN TXT "1403 | 104.163.128.0/17 | CA | arin | 2014-08-14"
1.190.163.104.origin.asn.cymru.com. 13911 IN TXT "1403 | 104.163.176.0/20 | CA | arin | 2014-08-14"

这很好!让我们继续前进吧。

工具 3:数据包交换所的观察镜

PCH(“ 数据包交换所 packet clearing house ”)是运行大量互联网交换点的组织。“ 观察镜 looking glass ”似乎是一个通用术语,指的是让你从另一个人的计算机上运行网络命令的 Web 表单。有一些观察镜不支持 BGP,但我只对那些能显示 BGP 路由信息的观察镜感兴趣。

这里是 PCH 的观察镜: https://www.pch.net/tools/looking_glass/

在该网站的 Web 表单中,我选择了多伦多 IX(“TORIX”),因为 mtr 说我是用它来访问 facebook.com 的。

操作 1:显示 ip bgp 摘要

下面是输出结果。我修改了其中的一些内容:

IPv4 Unicast Summary:
BGP router identifier 74.80.118.4, local AS number 3856 vrf-id 0
BGP table version 33061919
RIB entries 513241, using 90 MiB of memory
Peers 147, using 3003 KiB of memory
Peer groups 8, using 512 bytes of memory

Neighbor        V         AS MsgRcvd MsgSent   TblVer  InQ OutQ  Up/Down State/PfxRcd
...
206.108.34.248  4       1403  484672  466938        0    0    0 05w3d03h           50
...
206.108.35.2    4      32934  482088  466714        0    0    0 01w6d07h           38
206.108.35.3    4      32934  482019  466475        0    0    0 01w0d06h           38
...

Total number of neighbors 147

我的理解是,多伦多 IX(“TORIX”)直接连接到我的 ISP (EBOX,AS 1403)和 Facebook(AS 32934)。

操作 2:显示 ip bgp 129.134.30.0

这是筛选自 show ip bgp129.134.30.0(Facebook 的一个 IP 地址)的输出:

BGP routing table entry for 129.134.30.0/23
Paths: (4 available, best #4, table default)
  Advertised to non peer-group peers:
  206.220.231.55
  11670 32934
    206.108.35.2 from 206.108.35.254 (206.108.35.254)
      Origin IGP, metric 0, valid, external
      Community: 3856:55000
      Last update: Mon Oct  4 21:17:33 2021

  11670 32934
    206.108.35.2 from 206.108.35.253 (206.108.35.253)
      Origin IGP, metric 0, valid, external
      Community: 3856:55000
      Last update: Mon Oct  4 21:17:31 2021

  32934
    206.108.35.3 from 206.108.35.3 (157.240.58.225)
      Origin IGP, metric 0, valid, external, multipath
      Community: 3856:55000
      Last update: Mon Oct  4 21:17:27 2021

  32934
    206.108.35.2 from 206.108.35.2 (157.240.58.182)
      Origin IGP, metric 0, valid, external, multipath, best (Older Path)
      Community: 3856:55000
      Last update: Mon Oct  4 21:17:27 2021

这似乎是在说,从该 IX 到 Facebook 有 4 条路线。

魁北克 IX 似乎对 Facebook 一无所知

我也试过从魁北克 IX(“QIX”,它可能离我更近,因为我住在蒙特利尔而不是多伦多)做同样的事情。但 QIX 似乎对 Facebook 一无所知:当我输入129.134.30.0 时,它只是说 “% 网络不在表中”。

所以我想这就是为什么我被送到多伦多 IX 而不是魁北克的 IX。

更多的 BGP 观察镜

这里还有一些带观察镜的网站,可以从其他角度给你类似的信息。它们似乎都支持相同的 show ip bgp 语法,也许是因为他们运行的是同一个软件?我不太确定。

似乎有很多这样的观察镜服务,远不止这 3 个列表。

这里有一个与这个列表上的一个服务器进行会话的例子:route-views.routeviews.org。这次我是通过 telnet 连接的,而不是通过 Web 表单,但输出的格式看起来是一样的。

$ telnet route-views.routeviews.org

route-views>show ip bgp 31.13.80.36

BGP routing table entry for 31.13.80.0/24, version 1053404087
Paths: (23 available, best #2, table default)
  Not advertised to any peer
  Refresh Epoch 1
  3267 1299 32934
    194.85.40.15 from 194.85.40.15 (185.141.126.1)
      Origin IGP, metric 0, localpref 100, valid, external
      path 7FE0C3340190 RPKI State valid
      rx pathid: 0, tx pathid: 0
  Refresh Epoch 1
  6939 32934
    64.71.137.241 from 64.71.137.241 (216.218.252.164)
      Origin IGP, localpref 100, valid, external, best
      path 7FE135DB6500 RPKI State valid
      rx pathid: 0, tx pathid: 0x0
  Refresh Epoch 1
  701 174 32934
    137.39.3.55 from 137.39.3.55 (137.39.3.55)
      Origin IGP, localpref 100, valid, external
      path 7FE1604D3AF0 RPKI State valid
      rx pathid: 0, tx pathid: 0
  Refresh Epoch 1
  20912 3257 1299 32934
    212.66.96.126 from 212.66.96.126 (212.66.96.126)
      Origin IGP, localpref 100, valid, external
      Community: 3257:8095 3257:30622 3257:50001 3257:53900 3257:53904 20912:65004
      path 7FE1195AF140 RPKI State valid
      rx pathid: 0, tx pathid: 0
  Refresh Epoch 1
  7660 2516 1299 32934
    203.181.248.168 from 203.181.248.168 (203.181.248.168)
      Origin IGP, localpref 100, valid, external
      Community: 2516:1030 7660:9001
      path 7FE0D195E7D0 RPKI State valid
      rx pathid: 0, tx pathid: 0

这里有几个路由的选择:

  • 3267 1299 32934
  • 6939 32934
  • 701 174 32934
  • 20912 3257 1299 32934
  • 7660 2516 1299 32934

我想这些都有不止一个 AS 的原因是,31.13.80.36 是 Facebook 在多伦多的 IP 地址,所以这个服务器(可能在美国西海岸,我不确定)不能直接连接到它,它需要先到另一个 AS。所以所有的路由都有一个或多个 ASN。

最短的是 6939(“Hurricane Electric”),它是一个 “全球互联网骨干”。他们也有自己的 Hurricane Electric 观察镜 页面。

工具 4:BGPlay

到目前为止,所有其他的工具都只是向我们展示了 Facebook 路由的当前状态,其中一切正常,但这第四个工具让我们看到了这个 Facebook BGP 互联网灾难的历史。这是一个 GUI 工具,所以我将包括一堆屏幕截图。

该工具在 https://stat.ripe.net/special/bgplay。我输入了 IP 地址 129.134.30.12(Facebook 的一个 IP),如果你想一起试试。

首先,让我们看看一切出错之前的状态。我点击了在 10 月 4 日 13:11:28 的时间线,得到了这个结果:

我最初发现这很让人不知所措。发生了什么事?但后来有人在推特上指出,下一个要看的地方是点击 Facebook 灾难发生后的时间线(10 月 4 日 18 点 38 分)。

很明显,这张图有问题:所有的 BGP 路线都不见了!哦,不要!

顶部的文字显示了最后一条 Facebook BGP 路由的消失:

Type: W > withdrawal Involving: 129.134.30.0/24
Short description: The route 50869, 25091, 32934 has been withdrawn.
Date and time: 2021-10-04 16:02:33 Collected by: 20-91.206.53.12

如果我再点击“ 快进 fast forward ”按钮,我们看到 BGP 路由开始回来了。

第一个宣告的是 137409 32934。但我不认为这实际上是第一个宣布的,在同一秒内有很多路由宣告(在 2021-10-04 21:00:40),我认为 BGPlay 内部的排序是任意的。

如果我再次点击“ 快进 fast forward ”按钮,越来越多的路由开始回来,路由开始恢复正常。

我发现在 BGPlay 里看这个故障真的很有趣,尽管一开始界面很混乱。

也许了解一下 BGP 是很重要的?

我在这篇文章的开头说,你不能改变 BGP 路由,但后来我想起在 2016 年或 2017 年,有一个 Telia 路由问题,给我们的工作造成了一些小的网络问题。而当这种情况发生时,了解为什么你的客户无法到达你的网站其实是很有用的,即使它完全不受你控制。当时我不知道这些工具,但我希望能知道!

我认为对于大多数公司来说,应对由其他人的错误 BGP 路由造成的中断,你所能做的就是“什么都不做,等待它得到修复”,但能够\_自信地\_什么都不做是很好的。

一些发布 BGP 路由的方法

如果你想(作为一个业余爱好者)真正发布 BGP 路由,这里有一些评论中的链接:

  • 获取你自己的 ASN 的指南
  • dn42 似乎有一个 BGP 的实验场(它不在公共互联网上,但确实有其他人在上面,这似乎比自己在家里做 BGP 实验更有趣)

目前就这些了

我想还有很多 BGP 工具(比如 PCH 有一堆 路由数据的每日快照,看起来很有趣),但这篇文章已经很长了,而且我今天还有其他事情要做。

我对我作为一个普通人可以得到这么多关于 BGP 的信息感到惊讶,我一直认为它是一个“秘密的网络巫师”这样的东西,但显然有各种公共机器,任何人都可以直接 telnet 到它并用来查看路由表!没想到!


via: https://jvns.ca/blog/2021/10/05/tools-to-look-at-bgp-routes/

作者:Julia Evans 选题:lujun9972 译者:wxy 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

大家好!昨天我与一位朋友聊天,他正在准备编程面试,并试图学习一些算法基础知识。

我们聊到了 二次时间 quadratic-time 线性时间 linear-time 算法的话题,我认为在这里写这篇文章会很有趣,因为避免二次时间算法不仅在面试中很重要——有时在现实生活中了解一下也是很好的!后面我会快速解释一下什么是“二次时间算法” :)

以下是我们将要讨论的 3 件事:

  1. 二次时间函数比线性时间函数慢得非常非常多
  2. 有时可以通过使用 hashmap 把二次算法变成线性算法
  3. 这是因为 hashmap 查找非常快(即时查询!)

我会尽量避免使用数学术语,重点关注真实的代码示例以及它们到底有多快/多慢。

目标问题:取两个列表的交集

我们来讨论一个简单的面试式问题:获取 2 个数字列表的交集。 例如,intersect([1,2,3], [2,4,5]) 应该返回 [2]

这个问题也是有些现实应用的——你可以假设有一个真实程序,其需求正是取两个 ID 列表的交集。

“显而易见”的解决方案:

我们来写一些获取 2 个列表交集的代码。下面是一个实现此需求的程序,命名为 quadratic.py

import sys

# 实际运行的代码
def intersection(list1, list2):
    result = []
    for x in list1:
        for y in list2:
            if x == y:
                result.append(y)
    return result

# 一些样板,便于我们从命令行运行程序,处理不同大小的列表
def run(n):
    # 定义两个有 n+1 个元素的列表
    list1 = list(range(3, n)) + [2]
    list2 = list(range(n+1, 2*n)) + [2]
    # 取其交集并输出结果
    print(list(intersection(list1, list2)))

# 使用第一个命令行参数作为输入,运行程序
run(int(sys.argv[1]))

程序名为 quadratic.py(LCTT 译注:“quadratic”意为“二次方的”)的原因是:如果 list1list2 的大小为 n,那么内层循环(if x == y)会运行 n^2 次。在数学中,像 x^2 这样的函数就称为“二次”函数。

quadratic.py 有多慢?

用一些不同长度的列表来运行这个程序,两个列表的交集总是相同的:[2]

$ time python3 quadratic.py 10
[2]

real    0m0.037s
$ time python3 quadratic.py 100
[2]

real    0m0.053s
$ time python3 quadratic.py 1000
[2]

real    0m0.051s
$ time python3 quadratic.py 10000 # 10,000
[2]

real    0m1.661s

到目前为止,一切都还不错——程序仍然只花费不到 2 秒的时间。

然后运行该程序处理两个包含 100,000 个元素的列表,我不得不等待了很长时间。结果如下:

$ time python3 quadratic.py 100000 # 100,000
[2]

real    2m41.059s

这可以说相当慢了!总共花费了 160 秒,几乎是在 10,000 个元素上运行时(1.6 秒)的 100 倍。所以我们可以看到,在某个点之后,每次我们将列表扩大 10 倍,程序运行的时间就会增加大约 100 倍。

我没有尝试在 1,000,000 个元素上运行这个程序,因为我知道它会花费又 100 倍的时间——可能大约需要 3 个小时。我没时间这样做!

你现在大概明白了为什么二次时间算法会成为一个问题——即使是这个非常简单的程序也会很快变得非常缓慢。

快速版:linear.py

好,接下来我们编写一个快速版的程序。我先给你看看程序的样子,然后再分析。

import sys

# 实际执行的算法
def intersection(list1, list2):
    set1 = set(list1) # this is a hash set
    result = []
    for y in list2:
        if y in set1:
            result.append(y)
    return result

# 一些样板,便于我们从命令行运行程序,处理不同大小的列表
def run(n):
    # 定义两个有 n+1 个元素的列表
    list1 = range(3, n) + [2]
    list2 = range(n+1, 2*n) + [2]
    # 输出交集结果
    print(intersection(list1, list2))

run(int(sys.argv[1]))

(这不是最惯用的 Python 使用方式,但我想在尽量避免使用太多 Python 思想的前提下编写代码,以便不了解 Python 的人能够更容易理解)

这里我们做了两件与慢速版程序不同的事:

  1. list1 转换成名为 set1 的 set 集合
  2. 只使用一个 for 循环而不是两个

看看 linear.py 程序有多快

在讨论 为什么 这个程序快之前,我们先在一些大型列表上运行该程序,以此证明它确实是很快的。此处演示该程序依次在大小为 10 到 10,000,000 的列表上运行的过程。(请记住,我们上一个的程序在 100,000 个元素上运行时开始变得非常非常慢)

$ time python3 linear.py 100
[2]

real    0m0.056s
$ time python3 linear.py 1000
[2]

real    0m0.036s
$ time python3 linear.py 10000 # 10,000
[2]

real    0m0.028s
$ time python3 linear.py 100000 # 100,000
[2]

real    0m0.048s <-- quadratic.py took 2 minutes in this case! we're doing it in 0.04 seconds now!!! so fast!
$ time python3 linear.py 1000000 # 1,000,000
[2]

real    0m0.178s
$ time python3 linear.py 10000000 # 10,000,000
[2]

real    0m1.560s

在极大型列表上运行 linear.py

如果我们试着在一个非常非常大的列表(100 亿 / 10,000,000,000 个元素)上运行它,那么实际上会遇到另一个问题:它足够 了(该列表仅比花费 4.2 秒的列表大 100 倍,因此我们大概应该能在不超过 420 秒的时间内完成),但我的计算机没有足够的内存来存储列表的所有元素,因此程序在运行结束之前崩溃了。

$ time python3 linear.py 10000000000
Traceback (most recent call last):
  File "/home/bork/work/homepage/linear.py", line 18, in <module>
    run(int(sys.argv[1]))
  File "/home/bork/work/homepage/linear.py", line 13, in run
    list1 = [1] * n + [2]
MemoryError

real    0m0.090s
user    0m0.034s
sys 0m0.018s

不过本文不讨论内存使用,所以我们可以忽略这个问题。

那么,为什么 linear.py 很快呢?

现在我将试着解释为什么 linear.py 很快。

再看一下我们的代码:

def intersection(list1, list2):
    set1 = set(list1) # this is a hash set
    result = []
    for y in list2:
        if y in set1:
            result.append(y)
    return result

假设 list1list2 都是大约 10,000,000 个不同元素的列表,这样的元素数量可以说是很大了!

那么为什么它还能够运行得如此之快呢?因为 hashmap!!!

hashmap 查找是即时的(“常数级时间”)

我们看一下快速版程序中的 if 语句:

if y in set1:
    result.append(y)

你可能会认为如果 set1 包含 1000 万个元素,那么这个查找——if y in set1 会比 set1 包含 1000 个元素时慢。但事实并非如此!无论 set1 有多大,所需时间基本是相同的(超级快)。

这是因为 set1 是一个哈希集合,它是一种只有键没有值的 hashmap(hashtable)结构。

我不准备在本文中解释 为什么 hashmap 查找是即时的,但是神奇的 Vaidehi Joshi 的 basecs 系列中有关于 hash tablehash 函数 的解释,其中讨论了 hashmap 即时查找的原因。

不经意的二次方:现实中的二次算法!

二次时间算法真的很慢,我们看到的的这个问题实际上在现实中也会遇到——Nelson Elhage 有一个很棒的博客,名为 不经意的二次方,其中有关于不经意以二次时间算法运行代码导致性能问题的故事。

二次时间算法可能会“偷袭”你

关于二次时间算法的奇怪之处在于,当你在少量元素(如 1000)上运行它们时,它看起来并没有那么糟糕!没那么慢!但是如果你给它 1,000,000 个元素,它真的会花费几个小时去运行。

所以我认为它还是值得深入了解的,这样你就可以避免无意中使用二次时间算法,特别是当有一种简单的方法来编写线性时间算法(例如使用 hashmap)时。

总是让我感到一丝神奇的 hashmap

hashmap 当然不是魔法(你可以学习一下为什么 hashmap 查找是即时的!真的很酷!),但它总是让人 感觉 有点神奇,每次我在程序中使用 hashmap 来加速,都会使我感到开心 :)


via: https://jvns.ca/blog/2021/09/10/hashmaps-make-things-fast/

作者:Julia Evans 选题:lujun9972 译者:unigeorge 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出