<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>kblack</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>https://blog.i64d.com/</id>
  <link href="https://blog.i64d.com/" rel="alternate"/>
  <link href="https://blog.i64d.com/rss.xml" rel="self"/>
  <rights>All rights reserved 2026, kblack</rights>
  <subtitle>kblack's blog</subtitle>
  <title>Recycle Bin</title>
  <updated>2026-11-09T04:00:00.000Z</updated>
  <entry>
    <author>
      <name>kblack</name>
    </author>
    <category term="homelab" scheme="https://blog.i64d.com/tag/homelab/"/>
    <category term="llm" scheme="https://blog.i64d.com/tag/llm/"/>
    <category term="ghost" scheme="https://blog.i64d.com/tag/ghost/"/>
    <content>
      <![CDATA[<blockquote><p>这是本 blog 从 Ghost 程序迁移到纯静态后的第一篇文章，停更了整整三年，期间虽然攒了好多乱七八糟的文章片段，但是一个都没发布，不愧是我。</p></blockquote><blockquote><p>部署的时候 nginx 写错了 server_name，iptables 还忘记放通 443，这下真成拔线了</p></blockquote><ul><li>当 GPT-1 发布时，连研究者都无法预知未来模型能做到什么程度。</li><li>当 ChatGPT 发布时，大众才第一次感受到大语言模型的能力。</li><li>当 o1 发布时，模型第一次开始了雷霆大思考。</li><li>当 Opus 4.6 发布时，Coding Agent 终于不再是赌博。</li><li>当 Dario 用 Mythos 恐吓众人时，大部分人觉得他在危言耸听。</li></ul><p>相信每位 infra 仙人都渡过了此生最令人难忘的几个月，从应用到内核无数的漏洞被发现，CVE 的编号以前所未有的速度疯狂增长，每天醒来都有软件需要紧急更新，自动化的漏洞发掘和利用从未如此高效便捷，而且现在还远远没有达到极限，接下来几个月甚至几年大概率会更糟。</p><ul><li>Ghost 程序本身可以出 CVE，而且是像 CVE-2026-26980 这种极其简单的注入，见友站 <a href="https://www.ruanx.net/ghost-redirect-attack-review/">Ghost 投毒攻击事件调查</a>。</li><li>框架可以出 CVE，这个新鲜的 React 漏洞甚至还没拿到编号 <a href="https://github.com/react/react/security/advisories/GHSA-xqc2-xf4c-x3h3">GHSA-xqc2-xf4c-x3h3</a>。</li><li>Node 可以出 CVE，，<a href="https://nodejs.org/en/blog/vulnerability/october-2026-security-releases">Wednesday, October 21, 2026 Security Releases<br>The Node.js Project</a> 一次性修复了多少危险的 CVE。</li><li>我把用户权限设置好就行了吧，OpenAI 刚宣布他们的内部模型发现了 900 多个本地提权漏洞。</li><li>那我把他装进 docker 塞进 VM 吧，<a href="https://www.cve.org/CVERecord?id=CVE-2024-21626">CVE-2024-21626</a> 和 <a href="https://www.cve.org/CVERecord?id=CVE-2026-99999">CVE-2026-99999</a> 在向你招手。</li><li>最后你什么东西都没装，然后被 <a href="https://www.cve.org/CVERecord?id=CVE-2026-98432">CVE-2026-98432</a> 单杀了。</li></ul><p>这个 blog 原来的部署架构参见 <a href="/2021/02/homelab-k8s-journey/">之前的文章</a>，对于一个不怎么更新也没人看的博客可以说是神经病一样的复杂而且无效，唯一的意义是运维起来好玩，易受攻击面也大上了天。现在静态化以后只剩 debian 上的 nginx 了（当然 vps 上还有梯子和 bot 之类的其他程序），如果这也不再安全（确实可能不），那就真的只能拔线了，然后把文件托管到各种 Pages 托管服务了。</p><p>VPS 最差情况下还能拔线，然而手机电脑可没法拔线。</p><p>许多人都在嘲讽 GMO 的入侵事件，日方的技术差劲和不作为，他们甚至能建议客户端不要开机，通过拔线来避免进一步损害，然而从揭示风险的角度，我们可能还真得感谢他们，是他们让大家看清了我们现在到底有多危险。</p><ul><li>我们每个人的手机电脑Iot设备上都有很多毫无安全理念的程序，他们无比相信服务器的内容，忠实执行服务器下发的更新文件；</li><li>很多设备里的根证书列表可能一辈子也不会更新，感谢 eMudhra 让这些设备的 https 安全性全部化为乌有；</li><li>没有任何一家公司能保证自己不被入侵，每年都能看到标榜安全的金融公司被骇入（GMO、币安等）；</li><li>中国 Android 手机对于安全的极度蔑视，永不更新的内核里漏洞满天飞；</li><li>曾经“固若金汤”的Linux/iOS/macOS在自动化分析下和一坨史没有区别；</li><li>Windows 上的管理员权限依然人手一个，都有 UAC 等于都没 UAC，都不用说提权了（网易云提示你更新你总不能不点 UAC 吧？）；</li></ul><p>这意味着什么每个人的设备上都装着一堆不得不装的潜在木马，多家重要公司被入侵👉给设备下发危险代码👉超大量用户手机/电脑被入侵/劫持的一天不远了，而且某种意义上除了第一步以外拼多多也都已经干过了，你也该庆幸拼多多只是希望你能多买点东西。</p><p>那么公共设施呢？政府办公网络呢？甚至说军用网络呢？</p><p>不是说以前就没有漏洞了，我们一直都知道这个世界是一个草台班子，但现在看来这个班子的草台程度还是远远超出我们的想象，而且草台的危险程度也达到了新的高度。在 2014 年一个 OpenSSL 漏洞 <a href="https://www.cve.org/CVERecord?id=CVE-2014-0160">Heartbleed</a> 能成为一个重大新闻，当时人们还会反思应该给予开源软件维护更多的关注和资金，到了 2026 我们发现其实全世界都血如雨下。人们曾经调侃 NSA 手里有一万个 0 day，还往各种软件里塞了一万个后门，好消息是现在那一万个后门可能很快都会无所遁形，坏消息是 0 day 也从来不止一万个。曾经我们还安慰自己，我又没什么钱也没什么重要服务，谁会没事耗费精力拿着一堆漏洞对着每个人进行渗透。现在？猴子拿着机关枪说跟我 300TPS 的大肥鱼说去吧！而且随着模型和 Harness 的不断进步，从发现漏洞到漏洞被大规模组合利用的循环的速度只会越来越快，每次留给维护者们修复漏洞的时间窗口只会越来越短。</p><p>那么我们到底应该怎么办？难道彻底与互联网断开连接吗？放弃使用任何连接的软件吗？（搞得好像能做到一样）</p><p>拔线虽可耻但有用，那拔不了线怎么办？</p><p>只有天知道</p>]]>
    </content>
    <id>https://blog.i64d.com/2026/11/the-end-of-ghost/</id>
    <link href="https://blog.i64d.com/2026/11/the-end-of-ghost/"/>
    <published>2026-11-09T04:00:00.000Z</published>
    <summary>
      <![CDATA[<blockquote>
<p>这是本 blog 从 Ghost 程序迁移到纯静态后的第一篇文章，停更了整整三年，期间虽然攒了好多乱七八糟的文章片段，但是一个都没发布，不愧是我。</p>
</blockquote>
<blockquote>
<p>部署的时候 nginx 写错了]]>
    </summary>
    <title>拔线虽可耻但有用，那拔不了线怎么办</title>
    <updated>2026-11-09T04:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>kblack</name>
    </author>
    <category term="xbox" scheme="https://blog.i64d.com/tag/xbox/"/>
    <category term="network" scheme="https://blog.i64d.com/tag/network/"/>
    <category term="routeros" scheme="https://blog.i64d.com/tag/routeros/"/>
    <content>
      <![CDATA[<p>书接上文，搞定了 <code>UU</code> 加速 <code>xbox</code> 以后没多久就买了 <code>4070ti</code>，那之后是更没有机会玩 <code>xbox</code> 了，再之后我统一了 <code>routeros</code> 标注包 / 连接的方式，也没时间去测试加速功能有没有问题，结果周六打开 <code>xbox</code> 一看，嘿，<code>UU</code> 升级给我的方案烂掉了。</p><h2>是我搞烂的？</h2><p>对于我的环境来说，肯定首先要排除 <code>routeros</code> 上流量转发的问题，从上次折腾后，我的包转发进行了一些修改，现在 <code>mangle</code> 表的逻辑是：</p><pre><code>/ip/firewall/mangleadd action=mark-packet chain=prerouting comment=&quot;connection: bypass&quot; connection-mark=mark-bypass new-packet-mark=mark-bypass passthrough=noadd action=mark-packet chain=prerouting comment=&quot;connection: proxy&quot; connection-mark=mark-proxy new-packet-mark=mark-proxy passthrough=yesadd action=mark-packet chain=prerouting comment=&quot;connection: xbox&quot; connection-mark=xbox new-packet-mark=xbox passthrough=yesadd action=jump chain=prerouting comment=&quot;judge connection&quot; connection-mark=no-mark jump-target=judge-packet packet-mark=no-markadd action=accept chain=prerouting comment=&quot;action: bypass&quot; connection-mark=mark-bypassadd action=mark-routing chain=prerouting comment=&quot;action: route to uu&quot; in-interface=!bridge.163 new-routing-mark=xbox packet-mark=xbox passthrough=noadd action=mark-routing chain=prerouting comment=&quot;action: proxy http(s)&quot; in-interface=!bridge.802 new-routing-mark=&quot;proxy-http(s)&quot; packet-mark=mark-proxy passthrough=noadd action=accept chain=prerouting comment=&quot;action: accept rest&quot;</code></pre><p>首先将 <code>connection-mark</code> 复制到 <code>packet-mark</code> 上，之后对于没有标记的包做判断，最后再根据标记的情况选择出口，判断包的逻辑大致是：</p><pre><code>add action=jump chain=judge-packet comment=&quot;judge: bypass&quot; jump-target=judge-bypass packet-mark=no-markadd action=mark-packet chain=judge-packet comment=&quot;judge: xbox&quot; dst-address-list=!range-all-local new-packet-mark=xbox packet-mark=no-mark passthrough=yes src-address-list=xboxadd action=jump chain=judge-packet comment=&quot;judge: proxy&quot; jump-target=judge-proxy packet-mark=no-markadd action=mark-packet chain=judge-packet comment=&quot;judge: bypass other&quot; dst-address-list=!range-local new-packet-mark=mark-bypass packet-mark=no-mark passthrough=yes src-address-list=range-localadd action=mark-connection chain=judge-packet comment=&quot;judge: packet to connection&quot; new-connection-mark=mark-bypass packet-mark=mark-bypass passthrough=yesadd action=mark-connection chain=judge-packet comment=&quot;judge: packet to connection&quot; new-connection-mark=mark-proxy packet-mark=mark-proxy passthrough=yesadd action=mark-connection chain=judge-packet comment=&quot;judge: packet to connection&quot; new-connection-mark=xbox packet-mark=xbox passthrough=yesadd action=return chain=judge-packet comment=&quot;judge: finish&quot;</code></pre><p>大致的逻辑是，先判断 <code>bypass</code>（主要是内网和国内 IP）再判断 <code>proxy</code>，并且在 <code>proxy</code> 之前判断 <code>UU</code>，这样内网流量、国内流量（还可以指定一些特殊流量，比如你懂的）就不需要再经过 <code>UU</code> 了，这样既稳定又安全，这也是我搞这么复杂一堆的原因。经过了路由器上多次抓包，流量转发似乎没有问题，甚至刚启动加速还能连上几个连接。</p><h2>那又咋回事儿啊？</h2><p>还是回到 <code>UU</code> 的界面，我们发现现在 <code>UU</code> 的界面会卡在一个之前没有的阶段，而且超时以后就会退出。抓包以后发现 <code>UU</code> 的接口上多了很多以前没有的包，主要是一些 <code>DNS</code> 和另一些看不懂的 <code>UDP</code> 包，而且在这个等待阶段循环发送。</p><p>没有办法，为了获取现在连接时的完整过程，只能修改到官方指定的配置，然后重新抓包。根据不同情况下抓包测试，现在使用 <code>UU</code> Windows 客户端加速 <code>xbox</code> 游戏时，客户端一共有以下几个阶段：（不考虑 IP 配置错误的情况）</p><ol><li>等待阶段：启动后这个阶段 <code>UU</code> 不会主动发送任何包，这时接收到任何包会进入识别阶段；曾经的行为是收到包以后直接进入加速阶段。</li><li>识别阶段：这个阶段 <code>UU</code> 会主动发送一系列 <code>DNS</code>包，和一些 <code>UDP</code> 包，其中 <code>DNS</code> 包是为了通过主机返回的 <code>SRV</code> 记录判断主机类型，而<code>UDP</code>则是一些主机私有的连接包，两项都正确以后才会进入加速阶段；</li><li>加速阶段：这个阶段不会再重复检查主机类型的阶段，和以前一样；</li></ol><figure class="kg-card kg-image-card"><img class="kg-image" src="/images/f9/f9b12e8996b97392def4aa976407ebf39c774e671f2fb80f3d1a55d238552c82.png"></figure><h2>那咋整啊？</h2><p>我们先要看这两个识别分别怎么坏了，首先为什么这些包都是红色的？因为他们被特意设置了 <code>TTL=2</code>，即使在我这样中间隔着一个路由器的场景，<code>TTL=2</code> 其实也够了，如果你的环境更复杂，那么可能要在路由器上 <code>mangle</code> 成更大的值。</p><p>这些包涉及到 xbox 的应该就两种，第一个 <code>:5050</code> 的 <code>UDP</code>，xbox 似乎返回了一些基本信息，包里能看的只有一个<code>UUID</code>，<code>XBL Smart Glass Issuing CA0</code>和两个序列号。<code>xbox</code> 似乎不会回复非 <code>LAN</code> 上的这个请求，而我这方案 xbox 使用的是正常网段和 UU 的网段不一样，所以解决方案就是增加一条 <code>masquerade</code>，把 <code>UU</code> 的请求 <code>snat</code> 成路由器本身的，这样 <code>xbox</code> 就能正确返回，再 <code>dnat</code> 回去完成考察。</p><p><code>DNS</code> 包的问题则在于，<code>xbox</code> 只会回复他的真 IP，而 <code>UU</code> 似乎需要假 IP一致，这里则是两个方案，一个是把录下来的 DNS 回包修改完后直接发给 <code>UU</code>，或者也可以自己拼一个伪客户端来返回。</p><h2>啊？</h2><p>上面这些全部弄完以后，终于正常识别了，但是当我想复现的时候，我发现即使上面的东西全部关闭也能正常连接识别，怎么弄都弄不坏。</p><figure class="kg-card kg-image-card"><img class="kg-image" src="/images/c3/c37e3083b6a0686e36257e74d35ee1809ad10139fb910be3e890a086e90f13b3.webp"></figure><p>我怀疑 UU 可能对于同一个 mac 有检测的时限，一段时间（不知道长短）内不需要再次识别。这下也没法做实验获知到底哪些是必要条件了。折腾了几个小时，也不是很想故意搞烂再修好了，具体哪些必要就当课后作业留给需要复刻的同学了。</p><h2>总结</h2><p><code>UU</code> 增加这个识别的初衷，其实可能是为了支持多个设备，或者防止有人选错加速类型，误伤到了我。当然，另一种可能是为了防止有人用 <code>UU</code> 的线路当梯子使用，当然，这都不可考了。</p><p>既然用了别人的服务，还是非标准用法，需要经常折腾就太正常了，但是某些人可能就是喜欢折腾？</p><h2>其他</h2><p>在查问题的时候发现一些好玩的东西，<code>UU</code> 所有发的包都要发两遍，<code>UU</code> 所有发的包都要发两遍，可能是为了防止客户的内网发生丢包影响效果去骂 <code>UU</code>，干脆都发两遍😓，这导致我一开始看到大量重传，还以为是挨批桌子配错了。</p><figure class="kg-card kg-image-card"><img class="kg-image" src="/images/99/999628e7832b89e554d4af61c1445dfc2a3c7aeac64a775df5b7e04819a65e9a.png"></figure>]]>
    </content>
    <id>https://blog.i64d.com/2023/11/messed-up-upgraded-uu/</id>
    <link href="https://blog.i64d.com/2023/11/messed-up-upgraded-uu/"/>
    <published>2023-11-18T15:08:27.000Z</published>
    <summary>
      <![CDATA[<p>书接上文，搞定了 <code>UU</code> 加速 <code>xbox</code> 以后没多久就买了 <code>4070ti</code>，那之后是更没有机会玩 <code>xbox</code> 了，再之后我统一了 <code>routeros</code> 标]]>
    </summary>
    <title>折腾：再战渣易——UU检测升级</title>
    <updated>2023-11-18T15:08:27.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>kblack</name>
    </author>
    <category term="xbox" scheme="https://blog.i64d.com/tag/xbox/"/>
    <category term="network" scheme="https://blog.i64d.com/tag/network/"/>
    <category term="routeros" scheme="https://blog.i64d.com/tag/routeros/"/>
    <content>
      <![CDATA[<p>某人洁癖发癫，不愿购买矿卡，斥巨资购入了 Xbox Series X 霓虹版，又因大陆老生常谈的网络问题，必须解决加速的需求。这里加速主要还是指加速游戏联机，下载的话其实可以导向国内服务器，速度还可以接受。</p><h2>路线方案比选</h2><p>Xbox 主机加速方案有大致上三种选择：</p><table><thead><tr><th>方案</th><th>商业加速器</th><th>商业梯子</th><th>自建隧道</th><th>自建梯子</th></tr></thead><tbody><tr><td>节点延迟</td><td>优秀</td><td>较为优秀</td><td>一分钱</td><td>一分货</td></tr><tr><td>节点选择</td><td>多</td><td>多</td><td>一分钱</td><td>一分货</td></tr><tr><td>节点运维</td><td>有</td><td>有</td><td>灵车</td><td>灵车</td></tr><tr><td>节点稳定</td><td>好</td><td>好</td><td>一分钱</td><td>一分货</td></tr><tr><td>政策风险</td><td>较小</td><td>较大</td><td>N/A</td><td>N/A</td></tr><tr><td>联机</td><td>好</td><td>较差</td><td>较好</td><td>较差</td></tr><tr><td>月开销</td><td>UU&#x3D;￥24</td><td>￥20+</td><td>劣质不加钱</td><td>优质比较贵</td></tr><tr><td>对 Xbox</td><td>需要修改 IP</td><td>需要透明代理</td><td>无需透明代理</td><td>需要透明代理</td></tr></tbody></table><p>如果优质节点的价格合适，其实倾向于自建，因为同时还能作为其他基础设施，用于其他用途，然而事实稳定且低延迟节点要么水管太小要么太过昂贵，降低了作为基础设施一部分的价值，最终还是放弃自建方案。</p><p>商业梯子游走在深灰色地带，提供的游戏节点一般也不如商业加速器提供的优秀，加速 UDP 的效果也相对较差，价格也没有很大优势，再加上 xbox 不支持直接设置代理，需要搞透明代理，综合下来主要考虑效果较差且并不便宜，还是放弃了。</p><p>商业加速器例如 UU 加速器等除了需要固定价格外，效果还算好，同时还能保证加速 PC 游戏，最后决定选择此方案。为什么用 UU？因为之前就用 UU，而且背靠网易相对还是稳一点。</p><h2>一些其他方案的讨论</h2><p>由于 xbox 不支持直接设置代理，商业加速器的实现方式是路由，即主机设置加速器提供的 IP 和网关，借用原本的二层，由加速器进行路由与 NAT 的方式完成加速，这么做的话其实灵活性并不好：</p><ol><li>流量首先经过 UU，无法前置对流量进行细分控制；</li><li>关闭加速器后需再修改 IP，xbox 反复修改 IP 较为繁琐；</li><li>需要一台一直打开的机器启动 UU 加速器；</li><li>如果购买专用的加速盒，不但完全丧失细分控制的可能，还又引入了一个危险的无法完全控制的设备。</li></ol><p>针对部分问题，有以下的一些解决方案：</p><ol><li>一直启动的机器可以选择使用官方提供的 openwrt 版 UU，已经有老板打包了 docker 镜像，可以运行在 nas 上面，缺点是需要使用手机控制；</li><li>因为我的 AP 支持 VLAN 绑定 SSID，给 xbox 新配一个 SSID 绑定 VLAN163，然后直接使用 DHCP 为 Xbox 下发 UU 要求的地址配置，把启动 UU 的机器也连接到同一个 VLAN，这样就能免去直接配置 IP 的问题。但当不希望使用 UU 的时候，就需要切换回正常的 wifi 重新获得新配置，不幸的是，xbox 重新连接 wifi 也很麻烦，虽然也可以通过两个配置联动（一个生效一个失效）来实现，但这样就无法在隔离 UU 的情况下让 xbox 随时都能访问内网资源了</li></ol><h2>最终方案</h2><p>最终我选择了一个非常傻逼但是还挺有意思的方案：</p><p>总体方案是，RouterOS 在一个独立 VLAN 设置自己的 IP 为 UU 加速器要求的 IP，作为假的 xbox 与 UU 对接，在 RouterOS 上进行流量策略路由后 NAT 通过 UU 再出口。</p><h3>网络拓扑结构</h3><figure class="kg-card kg-image-card"><img class="kg-image" src="/images/ba/ba57d2f7079ab72faf07f394ffe42d6d3aea7db7b59a1d871d0b31543c010238.png"></figure><p>RouterOS 上配置两个 VLAN 作为两个承载网，VLAN 42 是家庭主要 VLAN，能访问我的所有资源，VLAN163 作为 UU 数据专网，隔离出 UU 加速的数据，防止数据包串味。</p><h3>UU 配置</h3><p>在家用服务器上启动了一台 4G 内存的 Windows 10 VM，并配置为了 IP 为 <code>IP_WIN</code>。启动 UU 加速器的主机加速后，自动生成了主机加速 IP <code>IP_163_XBOX</code> 和 <code>IP_163_UU</code>，这些 IP 在 Windows 主机 IP 固定的情况是不变的，之后会使用到这些 IP。</p><h3>xbox 配置‌‌</h3><p>xbox 不需要进行任何特殊配置，继续使用原来在 VLAN42 内网的 IP，网路情况对于 xbox 完全透明，xbox 的 IP 为 <code>IP_XBOX</code>。</p><h3>RouterOS 配置</h3><p>RouterOS 上除了 VLAN 配置以外，还需要做三件事：策略路由，双向 NAT，自动切换，<code>bridge.163</code> 是连接到 VLAN 163 的接口。</p><p>策略路由主要包含两个部分，识别流量和路由策略，这里用一组 mangle 表规则实现，匹配来往的连接，并做上标记，再加上额外的路由表对应 mark&#x3D;xbox 完成。</p><p>双向 NAT 的去程是出站的 MASQUERADE，回程匹配入站 IP 后 DNAT，均使用netmap 保证 NAT1。</p><p>自动切换使用使用 RouterOS 的 netwatch，这个功能可以在检测 IP 可达性后，执行命令，我们可以使用这个功能在 UU 启动后启用对应的路由，检测到 UU 断线后自动 fallback 到直连，提升使用体验。检测 IP 可以使用 UU 本身提供的假 DNS 6.6.6.6，这个 IP 是不能 ping 的，但是可以建立 tcp 连接检测（毕竟要提供 DNS 服务），注意这里需要修改一下 mangle 表，放通这个 ip，不然会影响检测。</p><p>下面只放了 mangle 表和 NAT 表的规则以及检测 IP 的部分，规则还有优化的空间，应该可以进一步增加限制条件，保证流量的安全。</p><pre><code>/ip/firewall/mangleadd chain=prerouting action=mark-connection new-connection-mark=xbox passthrough=yes src-address=IP_XBOX dst-address-list=!xbox-bypassadd chain=prerouting action=mark-connection new-connection-mark=xbox passthrough=yes dst-address=IP_163_XBOX in-interface=bridge.163 src-address=!6.6.6.6add chain=prerouting action=mark-packet new-routing-mark=xbox passthrough=no connection-mark=xbox/ip/firewall/natadd chain=srcnat action=netmap to-addresses=IP_163_XBOX src-address=IP_XBOX connection-mark=xbox out-interface=bridge.163add chain=dstnat action=netmap to-addresses=IP_XBOX dst-address=IP_163_XBOX in-interface=bridge.163/ip/routeadd disabled=no distance=1 dst-address=6.6.6.6/32 gateway=IP_163_UU pref-src=&quot;&quot; routing-table=main scope=30 suppress-hw-offload=no target-scope=10/tool/netwatchadd disabled=no down-script=&quot;/ip/firewall/mangle/disable [find comment=\&quot;routexbox\&quot;]&quot; host=6.6.6.6 http-codes=&quot;&quot; interval=10s port=22 test-script=&quot;&quot; type=tcp-conn up-script=\    &quot;/ip/firewall/mangle/enable [find comment=\&quot;routexbox\&quot;]&quot;</code></pre><h2>总结时间</h2><p>那么一个 xbox 发出的数据包会经由多少机器呢？</p><p>数据包从 xbox 发出，经过 VLAN42 到达路由器，路由器发现这是一个需要转发的包，SNAT 为加速 IP 后从 VLAN163 发送到 UU，UU 处理后通过自己的协议和渠道从 VLAN42 正常经路由器正常出站；回来的包通过 UU 从 VLAN163 发回路由器，由路由器 DNAT 到 xbox，整个环路就完成了。</p><p>这个方案虽然折腾，但是用着其实还挺稳定，而且对路由的控制权比较大，比如可以自己过滤掉国内 IP，保证内网访问不经过 UU，对部分境外 IP 也走自己的通道等。理论上把这里的 Windows UU 换成 UU 加速盒，链路依然是可以跑通的，但是具体规则等可能要做一定的修改。</p><p>网络折腾好了，游戏却没怎么玩，这一切值得吗？</p>]]>
    </content>
    <id>https://blog.i64d.com/2023/04/xia-gao-uu-xbox/</id>
    <link href="https://blog.i64d.com/2023/04/xia-gao-uu-xbox/"/>
    <published>2023-04-23T16:45:25.000Z</published>
    <summary>
      <![CDATA[<p>某人洁癖发癫，不愿购买矿卡，斥巨资购入了 Xbox Series X 霓虹版，又因大陆老生常谈的网络问题，必须解决加速的需求。这里加速主要还是指加速游戏联机，下载的话其实可以导向国内服务器，速度还可以接受。</p>
<h2>路线方案比选</h2><p>Xbox 主机加速方案]]>
    </summary>
    <title>折腾：UU 加速 Xbox，但不改网关</title>
    <updated>2023-11-18T11:16:40.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>kblack</name>
    </author>
    <category term="esim" scheme="https://blog.i64d.com/tag/esim/"/>
    <category term="android" scheme="https://blog.i64d.com/tag/android/"/>
    <content>
      <![CDATA[<blockquote><p>如果你能骑墙，最好还是先骑着</p></blockquote><p>为什么想搞 esim.me 开通境外电话卡？</p><p><em><del>这是一篇鸽了两个月的文章</del></em></p><ul><li>为了能够注册限制境外手机号的服务</li><li>为了规避本国对境外短信的审查（比如拦截验证码短信）</li><li>应急可用的出国通道（国际漫游可以回源到归属地）</li><li>esim 可以远程发卡，而且可以开被认为实体卡的号码，更容易通过一些验证</li><li>esim.me 可以同时存储多个 esim 卡，而且可以比较方便地切换</li><li>esim.me 可以让不支持 esim 的手机支持 esim 功能，也方便达成 esim 移机</li></ul><p>国内直接购买 <a href="https://esim.me/">esim.me</a> 在转运过海关等方面均比较麻烦，一般还是采用拼车或者让朋友从境外稍带回来的方式，通过这种方式能相对低成本地搞到手。</p><p>从官方选购 esim.me 时候，会让你在多个套餐中进行选择，三个（大类别）套餐的差异在可以存储的 sim 卡数量以及限制管理 sim 卡的手机。</p><ul><li>最严重的限制其实是管理设别数量（使用 sim 卡是不受限制的，毕竟提供的是标准的 sim 卡接口，只限制管理），最便宜的卡甚至只能使用唯一一台手机管理，甚至连换手机都不行。</li><li>卡的数量反而限制小一些，即使最便宜的套餐也能存 2 个，稍微加钱就有 5 个，作为合法使用目的的普通人，这样的限制基本没什么关系。</li></ul><figure class="kg-card kg-image-card"><img class="kg-image" src="/images/18/1817fa3fc4fd25afe792799a864bb4bc6a5a39bb6b15b3077f6a7a586ba96030.png"></figure><p>然而天无绝人之路，其实你完全可以无视这些所有的套餐，因为其实无论你选择了什么套餐，你买到的卡，硬件上都是完全都是一模一样的，区别只是他们会有不同的序列号，实施限制的并不是卡，而是官方的管理程序。而这些卡的管理，其实使用的标注的 esim 卡管理接口（iOS 上没开放，所以不能用 iOS 管理），所以只要你有支持这些接口的程序，完全可以自己来管理，甚至只要支持 sim 卡的 Windows 设备都行。以上内容的来源参见本文最后的链接。</p><p>那么问题就在于，如果你没有支持 sim 卡的 Windows 设备，如何进行管理？你只需要一台 root 了的 Android 手机，就可以了！有人已经用写完了使用标准接口进行管理的 GUI 程序，<a href="https://gitea.angry.im/PeterCxy/OpenEUICC">OpenEUICC</a>，同时还有配套的 <a href="https://github.com/AndroPlus-org/magisk-module-openeuicc">magisk 模块</a>！</p><p>开源万岁！</p><p>那么接下来问题就是，应该开通哪张卡。选择套餐有几个需要考虑的点：</p><ol><li>开通难度：并不是所有卡都支持直接远程开 esim 的，有些需要实体 sim 后转移，有些可能需要额外的身份验证，合适的实体 sim 的获取难度可能已经较高，转移到 esim 对于本文的情况没有太大的意义。</li><li>漫游条件：并不是所有的手机号都能长时间漫游的，比如土耳其的基本想都别想，部分便宜的号并不支持漫游流量等，也不是所有的都支持 VoLTE VoWifi 等通常能减少使用成本提升体验的功能，需要根据具体需求过滤。</li><li>归属地：归属地非常重要，例如想要注册 OpenAI，则一定要选择相应地区例如欧美日等的运营商和套餐，例如香港则无法使用。这里还有一点是有些套餐属于虚拟运营商，可能受到一定的限制，也需要提前做功课。</li><li>使用成本：主要包含两部分，保号成本是最低限度的套餐，或是充值保持活跃的要求等，计费成本则是实际使用流量短信甚至打电话的成本，非重度使用只需要考虑保号成本，但也需要小心有的套餐接短信都要收费。</li></ol><p>对于我的需求，我需要一个便宜方便的 PoC 证明我跑通了整个流程，也希望能提供适量的漫游流量，同时又正好有去香港旅行的计划，最后选择了 <a href="https://www.three.com.hk/tc/home.html">3HK</a> 的预付卡，268HKD 的 45G 一年期大中华区流量，价格合适，支持 Wifi-Calling，也支持直接开通 esim，唯一的确定可能是国内漫游相对略微不稳定一些，但无伤大雅，总体十分符合我的需求。于是在官网上购买后，扫描二维码直接开通，整个过程花费时间最多的，反而是给我的闲置 Android 手机安装 magisk 和最终选择套餐。</p><p>最后随便放几张（假装是）过程中的截图，以供参考：</p><figure class="kg-card kg-image-card"><img class="kg-image" src="/images/5d/5d8c3a20ab0cadaa2d5bd16e2361c497b0b8bf783b35dbf17ca6e44698e6fc10.png"></figure><figure class="kg-card kg-image-card"><img class="kg-image" src="/images/03/03e0d6b3331c56e509f745fadfaf84fb04b066719e8f7a6b5f4d82c23ca89309.png"></figure><figure class="kg-card kg-image-card"><img class="kg-image" src="/images/e7/e7f2b62d60cc8bceb11865ca10bfe10c23a67c96ec3db69769c4d1c9c4747f52.png"></figure><figure class="kg-card kg-image-card"><img class="kg-image" src="/images/7e/7ef6225de5abc509b226a99e8c4d7c5ef52baf39ade23451dfd646dca8e7e9a9.png"></figure><figure class="kg-card kg-image-card"><img class="kg-image" src="/images/1f/1fb042ff36ae44a8fa0ff072ea002f7a1bb4f72330cb5a44bc09ca196c228ee6.png"></figure><figure class="kg-card kg-image-card"><img class="kg-image" src="/images/0c/0c67adf46c5d8a0b16854ecacd85c12e1321ba051e2609ba69b0bfa6951a9fe1.png"></figure><p>鸣谢：</p><ul><li><a href="https://log.zero.root.me/thoughts/esimme-sim-card-with-windows">https://log.zero.root.me/thoughts/esimme-sim-card-with-windows</a></li><li><a href="https://sparktour.me/2022/11/20/configure-esim-me-card-with-pc-sc-reader/">https://sparktour.me/2022/11/20/configure-esim-me-card-with-pc-sc-reader/</a></li><li><a href="https://gitea.angry.im/PeterCxy/OpenEUICC">https://gitea.angry.im/PeterCxy/OpenEUICC</a></li></ul>]]>
    </content>
    <id>https://blog.i64d.com/2023/04/zhe-teng-esim-3hk/</id>
    <link href="https://blog.i64d.com/2023/04/zhe-teng-esim-3hk/"/>
    <published>2023-04-21T18:23:31.000Z</published>
    <summary>
      <![CDATA[<blockquote>
<p>如果你能骑墙，最好还是先骑着</p>
</blockquote>
<p>为什么想搞 esim.me 开通境外电话卡？</p>
<p><em><del>这是一篇鸽了两个月的文章</del></em></p>
<ul>
<li>为了能够注册限制境外手机]]>
    </summary>
    <title>折腾：esim.me 开通 3hk 预付卡</title>
    <updated>2023-11-18T11:16:45.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>kblack</name>
    </author>
    <category term="life" scheme="https://blog.i64d.com/tag/life/"/>
    <category term="iphone" scheme="https://blog.i64d.com/tag/iphone/"/>
    <category term="ios" scheme="https://blog.i64d.com/tag/ios/"/>
    <category term="review" scheme="https://blog.i64d.com/tag/review/"/>
    <content>
      <![CDATA[<p>我就是饿死，死外面，从这里跳下去，也不会买你们厨子家一点东西。</p><p>国庆前在京东自营买了自带的十三香。</p><h3>为什么？</h3><p>为什么要枪毙麻匪？啊不对，为什么要买水果机？到目前为止我用过的所有手机的列表，以及每次选择和更换手机的原因如下表：</p><table><thead><tr><th>时间</th><th>手机</th><th>选择原因</th><th>更换原因</th></tr></thead><tbody><tr><td>初中</td><td>某 Nokia</td><td>需要一个手机</td><td>进水</td></tr><tr><td>初中</td><td>Nokia N97 mini</td><td>接盘表姐旧手机</td><td>遗失</td></tr><tr><td>高中</td><td>Lumia 640</td><td>因为某次机缘巧合后，信了微软 Windows 8 的鬼话</td><td>确实用不下去了</td></tr><tr><td>大学</td><td>MI 5S Plus</td><td>葛朗台只想要性能够用的情况下便宜一些的手机</td><td>一年保外坏了尾插，修好第二年又坏了</td></tr><tr><td>大学</td><td>MI CC9</td><td>same</td><td>主要是 Google Play 和隐私问题</td></tr><tr><td>NOW</td><td>iPhone 13</td><td>没得选了</td><td>当无法忍受傻逼厨子的时候</td></tr></tbody></table><p>// 曾经我也觉得自己不应该沉迷玩手机</p><p>如果你现在问我是否后悔当时使用 Lumia 640，那肯定是不后悔的，他基本完成了我当时的需求（除了没有支付宝），同时防止了沉迷手机，还能写 UWP 玩。</p><p>MI 5SP 如果尾插不坏，可能用完大学四年并不会有太大问题，但是坏两次真的让人无法忍受了，修一次的价格得 500 大洋，实在是没法赌上第二次。同时没有买新不买旧也带来一些更新等等的问题，也算是得了教训。</p><p>MIUI 12 后某个版本开始，CC9 不再支持 Google Play，听闻后立刻禁用了自动更新，虽然 Google Play 的情况下，但是却再也无法得到更新，新功能并不感冒，但小 bug 也永远修不了了。随后了解到了一些实在无法忍受的事情，最后还是趁着十三香买了。</p><p>哦还有一个问题是，由于有轻微仓鼠病，聊天记录屯了几十 G 后，QQ 的清理聊天记录的界面直接卡死，想选择性删除都不行，这也是最后更换的原因之一。</p><p>玩 ROM 是不可能玩的，已经是老年人了，玩不了了。</p><h3>体验怎么样？</h3><p>经过了两周的双机双待，和两个月的试用，iPhone 还是总体来说达到了我的预期。</p><p>先说好的：</p><ol><li>应用权限确实管老实了，定位摄像头等等，不给权限绝不会不给用，文件不会拉满地的屎（虽然这个分割独立后的文件系统依旧没法让人喜欢），总体来说隐私保护方面应该是更强了；虽然你要说看不到果子代码，我也同意，但比起隔壁文件还开倒车的咕果，至少目前达到了我的需求；</li><li>虽然不能自由安装应用了，但是暂时 AppStore 还能基本满足我的需要，需要两个市场之间切换来切换去，至少现在支持 Store 账号单独切了，行吧；</li><li>推送由于全部都走果子的渠道，相对来说比较省电，也比较方便一键拉黑，唯一的问题是最近 Durov 又在作恶，（很可能有故意）劣化了推送体验，只要不关掉就会不停推送空消息；</li><li>整个界面虽然不是最喜欢的风格，但总体来说还是比较能看的。</li><li>AppStore 退款非常痛快，直接退掉了如果在其他平台可能很难退的自动大会员，虽然似乎有一些代价（比如之后再买可能不太方便）；</li><li>ApplePay 的卡面很准（至少比 MiPay），但是不知道为什么工行的信用卡怎么都绑不上，虽然看上去应该是工行傻逼了；</li><li>硬件上确实都代差，代码也是编译型的，流畅程度上还是比较令人满意的。</li><li>轻度使用续航一天没有问题，一开始梯子什么都没上的时候可以两天，后来可以一天半，但是玩富途的话一天最多了，电竞实锤；</li></ol><p>再说说坏的：</p><ol><li>没有全局返回后部分交互容易出现问题，后面详细说；</li><li>有些设计语言不统一后导致比较严重的操作问题，比如 Apple 账户和 AppStore 登录的界面交互语言是不一样的，一个登录键在下面，一个在右上角，当需要切换的时候，一个不注意点错，密码就白输了，即使有密码管理软件这个过程依旧令人抓狂；</li><li>加入 FaceID 以后直接去掉指纹的做法实在无法令人接受，有人说“不要给用户多余的选择”，有人说“你用 Apple Watch 不就好了”，还有人说“谁知道会有疫情”，前两者我不想做任何评论，但对于第三者实在无法接受，当你留有长发的时候，在躺下或者大风天的情况下，头发是很可能把脸部部分遮挡的，而这种情况下 FaceID 是经常无法（也不应该）解锁的，所以即使没有疫情，这种硬生生只留一种认证方式的做法，我依旧无法接受；听说要能把指纹加回来了，但 FaceID 不能留？厨子我 ×××；</li><li>因为不能自由安装应用，比较严重的是不能装滴滴，幸好早不在字节了不需要使用企业滴滴，这里就强行列在这里，虽然傻逼的是不可名状的某些东西，但也是顺便 cue 一下不能自由安装应用的问题；</li><li>稍微好用的梯子软件都需要钱？ShadowRocket 好不容易才调教到可以用的程度，因为我代理需要多组路由按可连接性多策略，配置起来真的很麻烦，也没什么文档，全靠 UI 来试和猜支持程度；</li><li>贵，真的贵，本体贵，配件贵，Care 贵；我觉得他没有达到我对 5999 的手机的期望；假装是撒了一两千币后，我觉得可以，挺值；</li></ol><p>下面具体说说全局返回：</p><p>iOS 并没有全局返回的概念，在 Android 一直存在三大金刚（WP 也有），曾经试图写 UWP 框架时也着重努力了如何实现合理的全局返回，在全面屏之前操作相对没那么方便，而且无论是否虚拟都会一定程度上占用下巴的空间，在有了全面屏手势以后，直接成为了非常自然高效的交互方式；</p><p>iOS 则一直只有一个 HOME，最早所有的返回都要点左上角，后来推荐的交互方式则是向右拉，在对面是“三大金刚”的时候确实有一定优越性。这种设计还有两个问题，一个是和元素的左右滑动有动作上的冲突（例如邮件处理），一旦软件识别不到位就会很难受；另一个是向右拉作为回退相对全局返回来说，是需要应用开发者自己来定义输入和反馈的，分别造成两个问题：操作并不一定可用、反馈并不一定符合你的预期（虽然后者全局返回其实也可以算有第二个问题，但具体到实际应用问题似乎相对不严重），例如如今套娃 HTML 横行的年代，弹出一个浏览器类的窗口，返回操作有两种歧义，一种是浏览器内的返回上一页，一种是关掉整个弹出的浏览器，我在实际使用中发现确实很多 iOS 在这类逻辑上有问题（会直接退出，需要填表单时更甚）。这种问题交互一旦发生就会很令人厌烦，导致需要记忆，哪个应用在哪里处理哪个返回操作会有问题。操作其实都是应用定义，这么说这问题不应该怪苹果？当然是怪苹果不能提出一个解决方案，毕竟是你说要提用户做最好的选择的，选不好当然怪你。</p><p>【花絮】当我发现 Telegram 官方 iOS 客户端不能支持切换 Enter 发送后，我全身发热瘫倒在公司的桌子上，半个小时都没法冷静下来，幸好还有个 Nicegram。</p><h3>总结</h3><p>就像之前说的，对于 5999，我真的觉得不值，当然苹果的东西怎么能用价格来衡量呢？（笑）毕竟四个轮子能卖 5e3，显示器支架能买 8e3。</p><p>如果符合你的需求且买得起，可以买，但最好还是需要找人试试体验下，看看你能不能忍受厨子作为产品经理规定你的一切。</p>]]>
    </content>
    <id>https://blog.i64d.com/2021/12/iphone-13-ios-dual-month-review/</id>
    <link href="https://blog.i64d.com/2021/12/iphone-13-ios-dual-month-review/"/>
    <published>2021-12-05T16:30:54.000Z</published>
    <summary>
      <![CDATA[<p>我就是饿死，死外面，从这里跳下去，也不会买你们厨子家一点东西。</p>
<p>国庆前在京东自营买了自带的十三香。</p>
<h3>为什么？</h3><p>为什么要枪毙麻匪？啊不对，为什么要买水果机？到目前为止我用过的所有手机的列表，以及每次选择和更换手机的原因如下表：</p]]>
    </summary>
    <title>瞎扯淡：iPhone 13 + iOS 两月评</title>
    <updated>2022-07-27T18:29:45.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>kblack</name>
    </author>
    <category term="homelab" scheme="https://blog.i64d.com/tag/homelab/"/>
    <category term="ops-daily" scheme="https://blog.i64d.com/tag/ops-daily/"/>
    <category term="synology" scheme="https://blog.i64d.com/tag/synology/"/>
    <content>
      <![CDATA[<p>回到家躺在椅子上，突然发现什么最可怕？</p><ul><li>A: 得知作业截止时间不是晚上而是中午十二点</li><li>B: 老板在 Slack 给你发了 114514 条消息未读</li><li>C: 偷偷藏着的裙子被妈妈发现</li><li>D: 家里服务器从内网上消失了</li></ul><h2>发生了肾么事情？</h2><p>上周日回到了家里，突然发现发现 ping 不到家里的服务器了，过去一看发现似乎是网线松动（灯不亮了），可是插紧以后仍旧 ping 不上，因为懒得搬显示器和键盘具体排查了，干脆直接彻底断电让他自己硬重启了。</p><p>重启以后发现群晖迟迟没有上线，也完全 ping 不到，等了五分钟也没有反应，<em><strong>大事不妙</strong></em>！</p><h3>SRE 开始</h3><p>在开机过程中，持续开着的 ping 返回了类似下面的内容：</p><pre><code class="language-bash">root@proxmox:~# ping 10.99.xx.xxPING 10.99.xx.xx (10.99.xx.xx) 56(84) bytes of data.timeouttimeout...64 bytes from 10.99.xx.xx: icmp_seq=1926 ttl=64 time=4037ms64 bytes from 10.99.xx.xx: icmp_seq=1927 ttl=64 time=3037ms64 bytes from 10.99.xx.xx: icmp_seq=1928 ttl=64 time=2036ms64 bytes from 10.99.xx.xx: icmp_seq=1929 ttl=64 time=1036ms64 bytes from 10.99.xx.xx: icmp_seq=1930 ttl=64 time=35mstimeout...timoeut</code></pre><p><em>在母鸡上 ping</em></p><p>这就很尴尬了，看上去是机器在启动后一瞬间又挂了，那么基本可以确定群晖出了大问题。</p><p>于是第一时间关掉了母鸡上所有的虚拟机，因为我的 k8s 集群的储存也是使用 nfs 挂载的群晖，所以现在整个集群应该已经挂得不成样子了。</p><p>对着所有硬盘先来了一顿 <code>smartctl -a</code> ，结果令人欣慰，总共七块硬盘（一块 NVME 系统盘，两块 SSD 母盘，两块 HGST 数据盘，两块准备送他们上路的老盘，全都能 <code>PASSED</code>，至少最糟糕的情况应该还没有发生。</p><p>由于症状包括了 ping 成功后突然中断，那么不能排除启动后 ip 发生改变也可能导致类似的情况，但是中断了无法检查，黑群晖的引导也导致我们无法看到群晖自己的 log，只能尝试使用 <code>Synology Assistant</code>，但是修改并没有什么卵用，状态也会卡在 <code>Connection Failed/ Ready</code> 这样的奇怪状态，由此基本判断系统挂壁，需要急救。</p><h3>群晖的储存结构</h3><p>群晖的储存结构一共分为了三个部分：</p><ul><li>引导固件，白群晖应该位于主板上，黑群晖一般是 USB 或是 SD 卡，虚拟机的情况则可以是转制的虚拟硬盘，固件只负责提供一些基础服务（例如刷机和寻找下面储存的真系统启动），也不会修改；</li><li>每一块硬盘最前的两个分区（我的情况下是 4.4），一同通过 madm 组成了 RAID1 用来安装真正的系统，也就是理论上只要有一个是好的，那么整个就是系统就能正常运行；</li><li>硬盘的剩余区域，单块分区或是使用 mdadm 提供软 raid，然后格式化为 ext4 或是 btrfs 用于实际数据储存。</li></ul><p>下一步自然是应该确认硬盘上的数据是否还完整，群晖直接挂载了所有四块老盘，给群晖的机器先挂上了一个 ubuntu live iso，然后进 shell 尝试只读挂载那上面的三个分区，大致查看了一下数据，应该问题不大，那至少数据是安全的，那么下一步应该就是如何恢复系统了，由于挂载时真系统的阵列并没有出现，怀疑系统已经损坏。</p><h3>尝试从备份修复</h3><p>刚才也说了群晖会在每块硬盘上都留下自己的分身，那么也就是了理论上任何一块硬盘正常，我们都应该能正常启动系统。为了更好地利用这个功能，我一直挂载了一块容量较小的虚拟硬盘，并把应用程序也安装在其中，并使用 proxmox 备份，准备应对的应该也就是现在这个情况。</p><p>但是将最新的备份还原后，系统的症状并没有改变，还是同样的错误；尝试了一个月前的备份，依旧无法启动；单独尝试挂载任意一块实体硬盘，也是一样的卡死。</p><p>所以可能系统很早就出现了问题，只是一直没有重启，导致问题没有暴露出来；也可能是因为没有办法安装 <code>qemu-agent</code>，导致备份实际上不完整而不可用。但这些都已经没用了，现在能做的只有想办法重新安装系统，希望能够保留数据，由于忘了把配置导出，所以完全还原已经是不可能了，但只要数据还完整，就没有大问题。</p><h3>尝试重新安装</h3><p>重新安装一个全新的群晖并没有难度，可是并不知道如何不让已有的数据丢失，这时那块虚拟硬盘又有了意义，虚拟磁盘可以被 clone，于是可以进行没有风险的尝试，成功后再对物理硬盘操作。</p><ul><li>第一个尝试是安装全新的群晖，再将旧盘挂载，结果是只要旧盘没有处理的挂载上虚拟机，启动就会失败；</li><li>第二个尝试是将前面所述的两个用来保存群晖系统分区删除，结果是如果直接用来安装系统，则会全盘清除，如果挂载到新系统，则无法识别内容需要初始化，初始化后数据报废；</li><li>最后还是从网上搜索得到了一个解决方案，只格式化前两个分区，这次能够直接启动了，固件认为系统损毁，于是可以正常地重新安装系统，同时可能因为分区表并没有问题，系统也不会主动破坏，数据也得到了保留。</li></ul><p>在虚拟硬盘上成功后，又添加了两块送终的物理盘做同样的处理，系统也可以发现系统分区损毁，由群晖修复后恢复正常；将最后两块物理盘处理后，所有数据终于恢复正常可以访问。但是由于配置没有备份，之后又花了一定的时间重新配置了账户、同步、定时备份等功能。</p><h3>后记</h3><p>在群晖恢复后，其他的虚拟机也都重新被启动，经过了一段时间，集群等等也哦度顺利恢复。</p><p>这次的故障恢复给了一些启示：</p><ul><li>并不是备份正常运行就可以高枕无忧了，至少应该定期确认能够正常从备份恢复，也就是灾容一定要进行测试，否则真正发生问题后并不能确定哪些是可靠的；</li><li>最好还是需要有一个冷备，以及 onsite 备用储存，否则如果这次的情况真的有硬盘损坏或是阵列都是，数据安全无法保证；</li><li>存储以后有条件还是一定不用群晖了（无论是黑的白的），等到我有一个四平米恒温恒湿的机房后，我一定上单独的 truenas 物理机解决存储问题！</li></ul>]]>
    </content>
    <id>https://blog.i64d.com/2021/04/ops-daily-1-synoboot-boom/</id>
    <link href="https://blog.i64d.com/2021/04/ops-daily-1-synoboot-boom/"/>
    <published>2021-04-01T03:02:25.000Z</published>
    <summary>
      <![CDATA[<p>回到家躺在椅子上，突然发现什么最可怕？</p>
<ul>
<li>A: 得知作业截止时间不是晚上而是中午十二点</li>
<li>B: 老板在 Slack 给你发了 114514 条消息未读</li>
<li>C: 偷偷藏着的裙子被妈妈发现</li>
<li>D: 家里服务]]>
    </summary>
    <title>运维日记之一：自爆黑群晖</title>
    <updated>2024-10-06T17:35:10.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>kblack</name>
    </author>
    <category term="kubernetes" scheme="https://blog.i64d.com/tag/kubernetes/"/>
    <category term="homelab" scheme="https://blog.i64d.com/tag/homelab/"/>
    <category term="rancher" scheme="https://blog.i64d.com/tag/rancher/"/>
    <category term="coredns" scheme="https://blog.i64d.com/tag/coredns/"/>
    <category term="zerotier" scheme="https://blog.i64d.com/tag/zerotier/"/>
    <category term="calico" scheme="https://blog.i64d.com/tag/calico/"/>
    <content>
      <![CDATA[<h3>流水账</h3><ul><li>物理上位于四个地点的五个节点，两个节点位于家中虚拟化平台上、国内腾讯云与阿里云的学生机上各有一个节点、一个节点位于香港的某 IDC，<code>etcd</code> 节点在家中。</li><li>整个内网三层互通，家中虚拟化平台上两个节点，通过位于内网的转发节点与其他节点通过 <code>zerotier</code> 虚拟网络通信。</li><li>集群搭建使用了 rancher 作为管理平台，在内网配置完成后，在各个节点依次启动 agent，完成集群建设。</li><li>CNI 选择了 <code>calico</code> 在原本三层互通的结构下直接 bgp 路由完成内部连接。</li><li>ingress 使用了 <code>traefik</code>，部署使用了 <code>daemonset</code> 在所有节点提供服务。</li><li>储存选择了 nfs，在家中黑群晖上暴露了整个网段可读写的 nfs 共享盘，再使用 catalog 里的 nfs-client-provisioner 即可，注意如果你也使用了 <code>synology</code>，权限 Squash 一定要选择 <code>no mapping</code> 否则权限问题会影响很多应用。</li><li>在集群外部搭建了 influxdb 用于储存一些 metric 信息。</li></ul><h3>踩坑</h3><h5>docker 镜像拉取过慢</h5><p>甚至分不清是否是集群启动脚本出错，解决方案推荐使用 ailyun 的容器镜像加速，国内其他云服务商则换成服务商自己的镜像源。‌‌</p><h5>calico 内部 ip 识别错误</h5><p>因为我的集群家中节点需要直接使用以太网 ip，而其他节点需要使用 <code>zerotier</code> 的 ip，同时外网 ip 也确实可以联通，需要限制 <code>calico</code> 的 ip 选择。通过下面的命令可以限制 <code>calico</code> 使用可以通信的那个 ip。</p><pre><code class="language-bash">kubectl set env daemonset/calico-node -n kube-system IP_AUTODETECTION_METHOD=can-reach=your_internal_ip</code></pre><h5>默认 coredns 配置不合适</h5><p><code>coredns</code> 默认情况下只会有少量节点，不适合我们的分（qiong）散（guang）式（dan）集群，至少产生三个问题：跨境查询 dns 性能下降、国内特色 dns 污染影响外国节点、每个节点不能选择最快的 CDN。</p><p>解决方法包括两个步骤：</p><ol><li>修改 <code>coredns</code> 自动扩容脚本参数 <code>nodesPerReplica</code>  为 <code>1</code>，这样每个节点都会运行一个 <code>coredns</code>，但此时访问依旧是均衡负载的。</li><li>确保启用集群 <code>feature gate</code> <code>ServiceTopology</code>, <code>EndpointSlice</code>，随后为 kube-dns service 增加限制 <code>topologyKeys</code> 限制，这样默认情况下每个节点只会访问本机的 dns，其次是同区域的，配合上一条，解决了 dns 的问题。</li></ol><pre><code class="language-yaml">topologyKeys:  - &#39;kubernetes.io/hostname&#39;  - &#39;topology.kubernetes.io/zone&#39;  - &#39;topology.kubernetes.io/region&#39;</code></pre><h3>部署的服务</h3><ul><li>mysql 提供数据库</li><li>phpmyadmin 方便改数据库</li><li>cert-manager DNS 记录签野卡</li><li>code-server 用于快速修改配置的</li><li>gitea 私有化 git 服务</li><li>drone 私有化 ci 服务</li><li>baget 私有化 nuget</li><li>grafana 时序数据可视化</li><li>ghost blog（当前）</li><li>calibre-web 电子书管理</li><li>rooftop-simulator 虚拟货币 k 线 telegram bot</li><li>ron-data-collect 聚合币安币价实时数据</li></ul><h3>推荐资源</h3><ul><li><a href="https://artifacthub.io/packages/search?page=1&org=k8s-at-home">k8s@home</a> 绝对不会只有你一个人这么无聊，在自己家里基于 k8s 搭建服务</li><li><a href="https://artifacthub.io/packages/search?page=1&org=bitnami">bitnami</a> 专业的基础设施 chart 提供商，这家还提供其他部署方式的脚手架</li></ul><h3>急救手册</h3><p>以下是一些排障帮助，希望对大家有帮助：</p><ul><li>如果有功能运行不正常，先检查 <code>system</code> Project 下的组件都是否正常。</li><li>注意 MTU 问题，即使在非混合架构下，网络中 MTU 也会因为 ip 隧道等原因变得不再那么规整，有时会导致网络无法访问。</li></ul>]]>
    </content>
    <id>https://blog.i64d.com/2021/02/homelab-k8s-journey/</id>
    <link href="https://blog.i64d.com/2021/02/homelab-k8s-journey/"/>
    <published>2021-02-18T13:50:14.000Z</published>
    <summary>
      <![CDATA[<h3>流水账</h3><ul>
<li>物理上位于四个地点的五个节点，两个节点位于家中虚拟化平台上、国内腾讯云与阿里云的学生机上各有一个节点、一个节点位于香港的某 IDC，<code>etcd</code> 节点在家中。</li>
<li>整个内网三层互通，家中虚拟化平台上两个]]>
    </summary>
    <title>home-lab Kubernetes 集群流水账</title>
    <updated>2023-12-11T10:04:31.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>kblack</name>
    </author>
    <category term="cnblogs" scheme="https://blog.i64d.com/tag/cnblogs/"/>
    <content>
      <![CDATA[<p>多校第9场第3题，一场比赛3个计算几何，甘版下风。</p><p>讲道理，看到题就知道是要转一圈，题目也规定了线段之间无相交，这也意味着以同一个点为中心，线段之间的遮挡关系是不变的，我们就可以通过维护当前角度上最近的线段，来判断某个点是否能被击中。</p><p>维护遮挡关系，可以直接比较他们与当前角度的射线的交点，到原点的距离，这里用了一些奇怪的方法来获得这个距离（这个方法只有在确定与射线有交点后才是正确的。</p><pre><code class="language-c++">#include&lt;cstdio&gt;#include&lt;cmath&gt;#include&lt;cstdlib&gt;#include&lt;cstring&gt;#include&lt;iostream&gt;#include&lt;algorithm&gt;#include&lt;functional&gt;#include&lt;queue&gt;#include&lt;set&gt;#include&lt;ctime&gt;#include&lt;map&gt; using namespace std; typedef long long ll;typedef unsigned long long ull; typedef long double ld; const ld eps = 1e-20;ld PI = acos((ld)-1.0); //#define ENABLE_FREAD struct io_s {     bool negative;    char ch;     inline bool isdigitx(char c) {        return c &gt;= &#39;0&#39; &amp;&amp; c &lt;= &#39;9&#39;;    }  #ifdef ENABLE_FREAD    inline char nextchar() {        static char buf[100000], *p1 = buf, *p2 = buf;        return p1 == p2 &amp;&amp; (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;    }#else    inline char nextchar() {        return getchar();    }#endif     template&lt;typename T&gt;    bool rn(T&amp; _v) {        negative = false;        _v = 0;        while (!isdigitx(ch = nextchar()) &amp;&amp; ch != EOF) { negative = ch == &#39;-&#39;; };        if (ch == EOF)return false;        do {            _v *= 10;            _v += ch - &#39;0&#39;;        } while (isdigitx(ch = nextchar()));        if (negative) _v = -_v;        return true;    }     template&lt;typename T&gt;    bool run(T&amp; _v) {        _v = 0;        while (!isdigitx(ch = nextchar()) &amp;&amp; ch != EOF) {};        if (ch == EOF)return false;        do {            _v *= 10;            _v += ch - &#39;0&#39;;        } while (isdigitx(ch = nextchar()));        return true;    }     template&lt;typename T&gt;    inline T r() {        T v = T();        rn(v);        return v;    }     inline int ri() { return r&lt;int&gt;(); }    inline ll rll() { return r&lt;ll&gt;(); }     inline int gets(char* s) {        return gets(s, &#39;\0&#39;);        /*        char* c = s;        do {        ch = nextchar();        } while (ch == &#39;\n&#39; || ch == &#39;\r&#39;);        do {        *(c++) = ch;        ch = nextchar();        } while (ch != &#39;\n&#39; &amp;&amp; ch != &#39;\r&#39;);        *(c++) = &#39;\0&#39;;        return c - s - 1;        */    }     inline int gets(char* s, char base) {        char* c = s;        do {            ch = nextchar();        } while (ch == &#39;\n&#39; || ch == &#39;\r&#39;);        do {            *(c++) = ch - base;            ch = nextchar();        } while (ch != &#39;\n&#39; &amp;&amp; ch != &#39;\r&#39;);        *(c++) = &#39;\0&#39;;        return c - s - 1;    }     inline char get() {        do {            ch = nextchar();        } while (ch &lt;&#39;A&#39; || ch &gt;&#39;Z&#39;);        return ch;    }     template&lt;class T&gt;    inline void o(T p) {        static int stk[70], tp;        if (p == 0) { putchar(&#39;0&#39;); return; }        if (p &lt; 0) { p = -p; putchar(&#39;-&#39;); }        while (p) stk[++tp] = p % 10, p /= 10;        while (tp) putchar(stk[tp--] + &#39;0&#39;);    }    inline void ln() { putchar(&#39;\n&#39;); }     inline void space() { putchar(&#39; &#39;); }     template&lt;class T&gt;    inline void oln(T p) {        o(p); ln();    } } io; int n, m, q; struct V2D {    ll x, y;    V2D() : x(0), y(0) {}    V2D(ll _x, ll _y) : x(_x), y(_y) {}     inline double acos() const {        return atan2(y, x);    }     inline ll len2() const {        return x*x + y*y;    }     inline ld len() const {        return sqrt(len2());    }     inline V2D operator *(const ll q) const {        return V2D(x*q, y*q);    }     inline ll operator ^(const V2D q) const {        return x*q.y - y*q.x;    }     inline ll operator *(const V2D q) const {        return x*q.x + y*q.y;    }     inline V2D operator +(const V2D q)const {        return V2D(x + q.x, y + q.y);    }     inline V2D operator -(const V2D q)const {        return V2D(x - q.x, y - q.y);    }     inline V2D operator +=(const V2D q) {        x += q.x, y += q.y;        return *this;    }     inline V2D operator -=(const V2D q) {        x -= q.x, y -= q.y;        return *this;    }     inline void read() {        io.rn(x), io.rn(y);    }}; const int N = 1e4 + 10;const int M = N * 3;const int Q = 12 + 3; V2D Enemies[N]; struct Wall {    V2D a, b;} Walls[N]; bool ap[N]; V2D Base;V2D Lv; struct Node {    V2D point;    int type;    double angle;} ns[M]; inline ld dis(const Wall&amp; p) {    V2D L = p.a - p.b;    swap(L.x, L.y);    L.y = -L.y;    return abs(p.a*L) * Lv.len() / abs(L*Lv);    //return abs(p.a*L) / abs(L*Lv) * Lv.len();    //ld v = abs(p.a*L) / L.len();    //ld v2 = abs(L*Lv) / Lv.len() * v / L.len();    //printf(&quot;%.4Lf: %.4Lf!\n&quot;, v, v2);    //return v * v / v2;} class WallComp{public:    inline bool operator()(const int&amp; left, const int&amp; right) const    {        double dx = dis(Walls[left]);        double dy = dis(Walls[right]);        return dx &lt; dy;    }}; set&lt;int, WallComp&gt; s; inline bool equal(double x, double y) {    return fabs(x - y) &lt; eps;} inline bool cmp(const Node &amp;x, const Node &amp;y) {    return equal(x.angle, y.angle) ? x.type &gt; y.type : x.angle &lt; y.angle;} int ct = 0; inline Node&amp; AddNode(V2D v, int t) {    Node &amp;p = ns[ct++];    p.point = v;    p.angle = v.acos();    p.type = t;    return p;} int main() {    int T = 0;    while (io.rn(n) &amp;&amp; io.rn(m) &amp;&amp; io.rn(q)) {        printf(&quot;Case #%d:\n&quot;, ++T);        for (int i = 1; i &lt;= n; ++i) Enemies[i].read();        for (int i = 1; i &lt;= m; ++i)            Walls[i].a.read(), Walls[i].b.read();         queue&lt;int&gt; ready_to;        while (q--) {            s.clear();            memset(ap, 0, sizeof(ap));            Base.read();            ct = 0;             for (int i = 1; i &lt;= n; ++i)                Enemies[i] -= Base;            for (int i = 1; i &lt;= m; ++i)                Walls[i].a -= Base, Walls[i].b -= Base;             for (int i = 1; i &lt;= n; ++i) {                AddNode(Enemies[i], 0);            }            Lv = V2D(-1, 0);            ll dx = -1e9;            for (int i = 1; i &lt;= m; ++i) {                AddNode(Walls[i].a, i);                AddNode(Walls[i].b, i);                V2D a = Walls[i].a;                V2D b = Walls[i].b;                if (min(a.y, b.y) &gt;= 0)continue;                if (max(a.y, b.y) &lt; 0)continue;                if (max(a.y, b.y) == 0) {                    if (a.y == 0 &amp;&amp; a.x &lt; 0) {                        s.insert(i);                        ap[i] = true;                        continue;                    }                    if (b.y == 0 &amp;&amp; b.x &lt; 0) {                        s.insert(i);                        ap[i] = true;                        continue;                    }                    continue;                }                double dx = 1.0 * (abs(a.y)*b.x + abs(b.y)*a.x) / (abs(a.y) + abs(b.y));                if (dx &lt; 0) {                    s.insert(i); ap[i] = true;                }            }            sort(ns, ns + ct, cmp);             int cur = 0, ans = 0;            while (cur &lt; ct) {                Lv = ns[cur].point;                double now = ns[cur].angle;                ld dx = 1e9;                bool flag = false;                while (cur &lt; ct) {                    if (fabs(ns[cur].angle - now) &gt; eps)break;                    if (ns[cur].type == 0) {                        if (!flag) {                            flag = true;                            if (s.size() &gt; 0) dx = min(dx, dis(Walls[*s.begin()]));                        }                        ld d = ns[cur].point.len();                        if (d &lt; dx)++ans;                        ++cur;                        continue;                    }                    int t = ns[cur].type;                    if (cur + 1 &lt; ct &amp;&amp; fabs(ns[cur + 1].angle - now) &lt; eps &amp;&amp; ns[cur + 1].type == t) {                        dx = min(dx, min(ns[cur].point.len(), ns[cur + 1].point.len()));                        cur += 2;                        continue;                    }                    bool &amp;x = ap[t];                    if (x) {                        ready_to.push(t);                    }                    else {                        s.insert(t);                    }                    x ^= 1;                    ++cur;                }                while (ready_to.size() &gt; 0) {                    s.erase(ready_to.front());                    ready_to.pop();                }            }            io.oln(ans);             for (int i = 1; i &lt;= n; ++i)                Enemies[i] += Base;            for (int i = 1; i &lt;= m; ++i)                Walls[i].a += Base, Walls[i].b += Base;        }    }    return 0;}</code></pre>]]>
    </content>
    <id>https://blog.i64d.com/2017/09/cnblogs-hdu6163/</id>
    <link href="https://blog.i64d.com/2017/09/cnblogs-hdu6163/"/>
    <published>2017-09-13T13:06:00.000Z</published>
    <summary>
      <![CDATA[<p>多校第9场第3题，一场比赛3个计算几何，甘版下风。</p>
<p>讲道理，看到题就知道是要转一圈，题目也规定了线段之间无相交，这也意味着以同一个点为中心，线段之间的遮挡关系是不变的，我们就可以通过维护当前角度上最近的线段，来判断某个点是否能被击中。</p>
<p>维护遮挡关]]>
    </summary>
    <title>[cnblogs] [hdu6163]CSGO</title>
    <updated>2024-07-17T03:53:24.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>kblack</name>
    </author>
    <category term="cnblogs" scheme="https://blog.i64d.com/tag/cnblogs/"/>
    <content>
      <![CDATA[<p>// 听说大考UR水平，估计自己会滚粗了，所以先占坑滚粗记</p><p>2016.4.18 Update</p><p>两场小考已经结束了，和预想的一样已经大滚粗QAQ</p><p>2016.4.24 Update</p><p>大考结束，继续大滚粗QAQQAQQAQ</p><p>2016.6.9 Update</p><p>wrz突然因未知原因跑了，于是一不小心又滚回A队了smg</p>]]>
    </content>
    <id>https://blog.i64d.com/2016/04/cnblogs-shtsc-2016/</id>
    <link href="https://blog.i64d.com/2016/04/cnblogs-shtsc-2016/"/>
    <published>2016-04-14T05:00:00.000Z</published>
    <summary>
      <![CDATA[<p>// 听说大考UR水平，估计自己会滚粗了，所以先占坑滚粗记</p>
<p>2016.4.18 Update</p>
<p>两场小考已经结束了，和预想的一样已经大滚粗QAQ</p>
<p>2016.4.24 Update</p>
<p>大考结束，继续大滚粗QAQQAQQAQ<]]>
    </summary>
    <title>[cnblogs] SHTSC 2016 滚出A队又一不小心滚回来记</title>
    <updated>2024-07-17T08:24:58.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>kblack</name>
    </author>
    <category term="cnblogs" scheme="https://blog.i64d.com/tag/cnblogs/"/>
    <content>
      <![CDATA[<p>在UWP中尝试使用T4的朋友们可能会注意到，VS设计器默认生成的运行时T4无法通过编译，因为他使用了一些不支持UWP的API，解决方案也很简单，知道将其复制出来，修正不兼容的部分，指定inherits参数为新的基类即可，这里提供一个简单的版本，应该还可以优化</p><pre><code class="language-c#">    public class FormatterBase    {        private bool _endsWithNewline;        private StringBuilder _generationEnvironmentField;        private List&lt;int&gt; _indentLengthsField;        protected StringBuilder GenerationEnvironment        {            get            {                if ((_generationEnvironmentField == null))                {                    _generationEnvironmentField = new StringBuilder();                }                return _generationEnvironmentField;            }            set { _generationEnvironmentField = value; }        }        /// &lt;summary&gt;        ///     A list of the lengths of each indent that was added with PushIndent        /// &lt;/summary&gt;        private List&lt;int&gt; IndentLengths        {            get            {                if ((_indentLengthsField == null))                {                    _indentLengthsField = new List&lt;int&gt;();                }                return _indentLengthsField;            }        }        /// &lt;summary&gt;        ///     Gets the current indent we use when adding lines to the output        /// &lt;/summary&gt;        public string CurrentIndent { get; private set; } = &quot;&quot;;        /// &lt;summary&gt;        ///     Current transformation session        /// &lt;/summary&gt;        public virtual IDictionary&lt;string, object&gt; Session { get; set; }        public virtual string TransformText()        {            GenerationEnvironment.Clear();            Render();            return string.Empty;        }        protected virtual void Render()        {        }        #region Transform-time helpers        /// &lt;summary&gt;        ///     Write text directly into the generated output        /// &lt;/summary&gt;        public void Write(string textToAppend)        {            if (string.IsNullOrEmpty(textToAppend))            {                return;            }            // If we&#39;re starting off, or if the previous text ended with a newline,            // we have to append the current indent first.            if (((GenerationEnvironment.Length == 0)                 || _endsWithNewline))            {                GenerationEnvironment.Append(CurrentIndent);                _endsWithNewline = false;            }            // Check if the current text ends with a newline            if (textToAppend.EndsWith(Environment.NewLine, StringComparison.CurrentCulture))            {                _endsWithNewline = true;            }            // This is an optimization. If the current indent is &quot;&quot;, then we don&#39;t have to do any            // of the more complex stuff further down.            if ((CurrentIndent.Length == 0))            {                GenerationEnvironment.Append(textToAppend);                return;            }            // Everywhere there is a newline in the text, add an indent after it            textToAppend = textToAppend.Replace(Environment.NewLine, (Environment.NewLine + CurrentIndent));            // If the text ends with a newline, then we should strip off the indent added at the very end            // because the appropriate indent will be added when the next time Write() is called            if (_endsWithNewline)            {                GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - CurrentIndent.Length));            }            else            {                GenerationEnvironment.Append(textToAppend);            }        }        /// &lt;summary&gt;        ///     Write text directly into the generated output        /// &lt;/summary&gt;        public void WriteLine(string textToAppend)        {            Write(textToAppend);            GenerationEnvironment.AppendLine();            _endsWithNewline = true;        }        /// &lt;summary&gt;        ///     Write formatted text directly into the generated output        /// &lt;/summary&gt;        public void Write(string format, params object[] args)        {            Write(string.Format(CultureInfo.CurrentCulture, format, args));        }        /// &lt;summary&gt;        ///     Write formatted text directly into the generated output        /// &lt;/summary&gt;        public void WriteLine(string format, params object[] args)        {            WriteLine(string.Format(CultureInfo.CurrentCulture, format, args));        }        /// &lt;summary&gt;        ///     Increase the indent        /// &lt;/summary&gt;        public void PushIndent(string indent)        {            if ((indent == null))            {                throw new ArgumentNullException(nameof(indent));            }            CurrentIndent = (CurrentIndent + indent);            IndentLengths.Add(indent.Length);        }        /// &lt;summary&gt;        ///     Remove the last indent that was added with PushIndent        /// &lt;/summary&gt;        public string PopIndent()        {            var returnValue = &quot;&quot;;            if ((IndentLengths.Count &gt; 0))            {                var indentLength = IndentLengths[(IndentLengths.Count - 1)];                IndentLengths.RemoveAt((IndentLengths.Count - 1));                if ((indentLength &gt; 0))                {                    returnValue = CurrentIndent.Substring((CurrentIndent.Length - indentLength));                    CurrentIndent = CurrentIndent.Remove((CurrentIndent.Length - indentLength));                }            }            return returnValue;        }        /// &lt;summary&gt;        ///     Remove any indentation        /// &lt;/summary&gt;        public void ClearIndent()        {            IndentLengths.Clear();            CurrentIndent = &quot;&quot;;        }        #endregion        #region ToString Helpers        /// &lt;summary&gt;        ///     Utility class to produce culture-oriented representation of an object as a string.        /// &lt;/summary&gt;        public class ToStringInstanceHelper        {            private IFormatProvider _formatProviderField = CultureInfo.InvariantCulture;            /// &lt;summary&gt;            ///     Gets or sets format provider to be used by ToStringWithCulture method.            /// &lt;/summary&gt;            public IFormatProvider FormatProvider            {                get { return _formatProviderField; }                set                {                    if ((value != null))                    {                        _formatProviderField = value;                    }                }            }            /// &lt;summary&gt;            ///     This is called from the compile/run appdomain to convert objects within an expression block to a string            /// &lt;/summary&gt;            public string ToStringWithCulture(object objectToConvert)            {                if ((objectToConvert == null))                {                    throw new ArgumentNullException(nameof(objectToConvert));                }                var t = objectToConvert.GetType();                var method = t.GetMethod(&quot;ToString&quot;, new[]                {                    typeof (IFormatProvider)                });                //if ((method == null))                //{                return objectToConvert.ToString();                //}                //return ((string) (method.Invoke(objectToConvert, new object[]                //{                //    _formatProviderField                //})));            }        }        /// &lt;summary&gt;        ///     Helper to produce culture-oriented representation of an object as a string        /// &lt;/summary&gt;        public ToStringInstanceHelper ToStringHelper { get; } = new ToStringInstanceHelper();        #endregion    }</code></pre>]]>
    </content>
    <id>https://blog.i64d.com/2016/01/cnblogs-uwp-t4/</id>
    <link href="https://blog.i64d.com/2016/01/cnblogs-uwp-t4/"/>
    <published>2016-01-17T07:11:00.000Z</published>
    <summary>
      <![CDATA[<p>在UWP中尝试使用T4的朋友们可能会注意到，VS设计器默认生成的运行时T4无法通过编译，因为他使用了一些不支持UWP的API，解决方案也很简单，知道将其复制出来，修正不兼容的部分，指定inherits参数为新的基类即可，这里提供一个简单的版本，应该还可以优化</p>
<pr]]>
    </summary>
    <title>[cnblogs] 在UWP中使用T4的注意事项</title>
    <updated>2024-07-17T07:36:10.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>kblack</name>
    </author>
    <category term="cnblogs" scheme="https://blog.i64d.com/tag/cnblogs/"/>
    <content>
      <![CDATA[<p>开源中国的WP客户端还是WP7时代的，而且已经好久没有更新了，界面不符合WindowsUniversal的风格，而且代码很多地方不是很规范，所以我决定自己来为开源中国开发一个UWP客户端，支持Desktop &amp; Mobile</p><p>&nbsp;</p><p>客户端还在开发当中，以后会放出测试版本的appx</p><p>（我还没有决定是否最终将客户端开源）</p><p>一下是现在的一些截图</p><figure class="kg-card kg-image-card"><img class="kg-image" src="/images/25/25cd133af42e12f00ad3b045ca218690cb1d8b6c868048c6f3d4b822dc09b9d5.png"></figure><figure class="kg-card kg-image-card"><img class="kg-image" src="/images/85/85f697fb14143678cf9945da340375c0273e6b830613429c19601d7c15966ef5.png"></figure><figure class="kg-card kg-image-card"><img class="kg-image" src="/images/f6/f6fac181606ec4142fc532a976b4857476b523e3049e4de2282d82fa1021484b.png"></figure>]]>
    </content>
    <id>https://blog.i64d.com/2016/01/cnblogs-uosc-uwp/</id>
    <link href="https://blog.i64d.com/2016/01/cnblogs-uosc-uwp/"/>
    <published>2016-01-04T12:50:00.000Z</published>
    <summary>
      <![CDATA[<p>开源中国的WP客户端还是WP7时代的，而且已经好久没有更新了，界面不符合WindowsUniversal的风格，而且代码很多地方不是很规范，所以我决定自己来为开源中国开发一个UWP客户端，支持Desktop &amp; Mobile</p>
<p>&nbsp;</p>
<p]]>
    </summary>
    <title>[cnblogs] 一个正在开发中的开源中国的UWP客户端</title>
    <updated>2024-07-17T08:34:45.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>kblack</name>
    </author>
    <category term="cnblogs" scheme="https://blog.i64d.com/tag/cnblogs/"/>
    <content>
      <![CDATA[<pre><code class="language-bash">&gt;blog initInitialized empty Blog at http://www.cnblogs.com/kblack/&gt;blog add kblack&gt;blog commit[master (root-commit) 1754ffc] init commit1 user changed, 0 insertions(+), 0 deletions(-)create mode 100644 terminal&gt;blog push origin</code></pre>]]>
    </content>
    <id>https://blog.i64d.com/2016/01/cnblogs-terminal/</id>
    <link href="https://blog.i64d.com/2016/01/cnblogs-terminal/"/>
    <published>2016-01-03T13:11:00.000Z</published>
    <summary>
      <![CDATA[<pre><code class="language-bash">&gt;blog init

Initialized empty Blog at http://www.cnblogs.com/kblack/

&gt;blog add kblack

&gt;blog comm]]>
    </summary>
    <title>[cnblogs] terminal</title>
    <updated>2024-07-17T02:39:09.000Z</updated>
  </entry>
</feed>
