1 2 |
cat >> /etc/bashrc.d/00_disable_dietpi_login.sh << EOF export G_DIETPI_LOGIN=1 |
1 2 |
cat >> /etc/bashrc.d/00_disable_dietpi_login.sh << EOF export G_DIETPI_LOGIN=1 |
Ubuntu 22.04 升级了 OpenSSH 到8.9,这个版本默认开启 [email protected] 作为密钥交换(KEX)方法。这个算法使用 512 bit 的 hash。
如果客户端和服务端都升级到了8.9或以上,则成功协商使用这一KEX算法,这时如果使用 gpg-agent 的 SSH 功能签名则会报 agent refused operation。
打印 gpg-agent 的日志可看到报错为 Provided object is too large。
解决方法是在客户端(~/.ssh/config)或服务端(/etc/ssh/sshd_config)中禁用这个算法
1 |
KexAlgorithms -sntrup761x25519-sha512@openssh.com |
同理 diffie-hellman-group16-sha512 和 diffie-hellman-group18-sha512 也应该被禁用,但它们优先级本来就很低。
如果仍然有问题,把Kex Host Key Algorithm也改一下,如改成
1 |
HostKeyAlgorithms ssh-ed25519 |
我们在一个项目中创(luan)新(xie)地用JSON来编码msgpack编码后的结果(即encoded = json_encode(msgpack_encode(txt))),结果发现Golang侧无法解码。
首先我们可以确定msgpack没有问题,因为输入给msgpack解码数据就与输入值不一致。
我们使用lua-cjson来编码一个JSON,因为结果不是printable的,所以在外面加一层base64.encode
1 |
print(ngx.encode_base64(require("cjson").encode({a = "\134123"}))) |
结果是eyJhIjoihjEyMyJ9。
在Python里解码它:
1 |
json.loads(base64.b64decode('eyJhIjoihjEyMyJ9').decode('raw_unicode_escape')) |
结果是{‘a’: ‘\x86123’}。没有问题,和输入一致。
在Go里解码它:
1 2 3 4 5 6 7 8 9 |
type A struct { Data string `json:"a"` } b64 := "eyJhIjoihjEyMyJ9" jsonEncoded, _ := base64.StdEncoding.DecodeString(b64) var aa A err = json.Unmarshal(jsonEncoded, &aa) fmt.Println([]byte(aa.Data)) |
结果是[239 191 189 49 50 51],可以看到\x86被解码成了\239 \191 \189即\xefbfbd,表示无效的UTF8字符。
这是因为Go默认采用UTF-8解码,如果field被标记为string,则json.Unmarshal会使用utf8.DecodeRune来尝试解码输入https://github.com/golang/go/blob/master/src/encoding/json/decode.go#L1304。但在我们的场景中,\x86是一个单字节的非UTF-8字符,所以utf8.DecodeRune返回了utf8.RuneError并把它放到了结果里。
那么到底是哪里出了问题呢?
首先,JSON的RFC指出,其中的字符串必须以UTF-8编码(https://datatracker.ietf.org/doc/html/rfc8259#section-8.1),但是同时也提到,除了几个特殊的字符外,其中的字符可以被escape也可以不escape(https://datatracker.ietf.org/doc/html/rfc8259#section-7)。所以lua-cjson的这种编码方式似乎也是合法的?
解决办法是写一个自己的Unmarshal方法。首先把结构体中的field标记为自定义类型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
type Payload struct { A RawBytes `json:"a"` } type rawBytes []byte func (r *rawBytes) UnmarshalJSON(b []byte) error { unqoutedBytes, ok := unqouteBytes(b) if !ok { return fmt.Errorf("failed to uncode msgpacked data") } *r = unqoutedBytes return nil } |
然后我们魔改unquote方法,在原来的基础上加上对解码结果是否为utf8.RuneError的判断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
// getu4 has been copied verbatim from // https://github.com/golang/go/blob/2580d0e08d5e9f979b943758d3c49877fb2324cb/src/encoding/json/decode.go#L1167-L1188 func getu4(s []byte) rune { if len(s) < 6 || s[0] != '\\' || s[1] != 'u' { return -1 } var r rune for _, c := range s[2:6] { switch { case '0' <= c && c <= '9': c = c - '0' case 'a' <= c && c <= 'f': c = c - 'a' + 10 case 'A' <= c && c <= 'F': c = c - 'A' + 10 default: return -1 } r = r*16 + rune(c) } return r } // unqouteBytes converts a quoted literal []byte s. // The rules are different than for Go, so cannot use strconv.Unqoute(). // This function is copied verbatim from // https://github.com/golang/go/blob/2580d0e08d5e9f979b943758d3c49877fb2324cb/src/encoding/json/decode.go#L1198-L1310 // Please read the comment beginning with "PATCHED" below. func unqouteBytes(s []byte) (t []byte, ok bool) { if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' { return } s = s[1 : len(s)-1] // Check for unusual characters. If there are none, // then no unquoting is needed, so return a slice of the // original bytes. r := 0 for r < len(s) { c := s[r] if c == '\\' || c == '"' || c < ' ' { break } if c < utf8.RuneSelf { r++ continue } rr, size := utf8.DecodeRune(s[r:]) if rr == utf8.RuneError && size == 1 { break } r += size } if r == len(s) { return s, true } b := make([]byte, len(s)+2*utf8.UTFMax) w := copy(b, s[0:r]) for r < len(s) { // Out of room? Can only happen if s is full of // malformed UTF-8 and we're replacing each // byte with RuneError. if w >= len(b)-2*utf8.UTFMax { nb := make([]byte, (len(b)+utf8.UTFMax)*2) copy(nb, b[0:w]) b = nb } switch c := s[r]; { case c == '\\': r++ if r >= len(s) { return } switch s[r] { default: return case '"', '\\', '/', '\'': b[w] = s[r] r++ w++ case 'b': b[w] = '\b' r++ w++ case 'f': b[w] = '\f' r++ w++ case 'n': b[w] = '\n' r++ w++ case 'r': b[w] = '\r' r++ w++ case 't': b[w] = '\t' r++ w++ case 'u': r-- rr := getu4(s[r:]) if rr < 0 { return } r += 6 if utf16.IsSurrogate(rr) { rr1 := getu4(s[r:]) if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar { // A valid pair; consume. r += 6 w += utf8.EncodeRune(b[w:], dec) break } // Invalid surrogate; fall back to replacement rune. rr = unicode.ReplacementChar } w += utf8.EncodeRune(b[w:], rr) } // Quote, control characters are invalid. case c == '"', c < ' ': return // ASCII case c < utf8.RuneSelf: b[w] = c r++ w++ // Coerce to well-formed UTF-8. default: // PATCHED: on RuneError, copy verbatim rr, size := utf8.DecodeRune(s[r:]) if rr != utf8.RuneError { r += size w += utf8.EncodeRune(b[w:], rr) } else { b[w] = s[r] r++ w++ } } } return b[0:w], true } |
还需要注意的是,Go的json包默认对[]byte类型的field进行base64编解码:
1 2 3 4 |
b, _ := json.Marshal(map[string][]byte{ "x": []byte{147}, }) fmt.Println("> ", b, string(b)) |
结果是> [123 34 120 34 58 34 107 119 61 61 34 125] {“x”:”kw==”};同理Unmarshal时也会需要输入为base64编码结果。
因此在上面这个解决方法中,我们用rawBytes这个新类型来alias到[]byte,而并不直接使用[]byte类型再在之后自己解码。
发了一个issue:https://github.com/golang/go/issues/51094。
另外的JSON库没有这个问题,测试了https://github.com/json-iterator/go 和 https://github.com/bytedance/sonic 。
35块一个,长这样
经过一番Google找到了对应的原厂屏,是Gooddisplay的GDEH042Z96,屏幕芯片是SSD1619A,这个芯片初始化序列比幻塔的新手教程还长。
整理了一下官方例程之后,发了一个PR:https://github.com/ZinggJM/GxEPD2/pull/47;有一些点需要注意:
效果如下图所示,用的是我魔改的中文版天气:https://github.com/fffonion/ESP32-e-Paper-Weather-Display