From e7ae557b9111edd5667cfdb44574da30db8dfe17 Mon Sep 17 00:00:00 2001 From: hahafool <75717694+hahahrfool@users.noreply.github.com> Date: Thu, 21 Apr 2022 21:34:56 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E8=AE=A2=E6=96=87=E6=A1=A3,=20sockopt?= =?UTF-8?q?=20=E5=92=8C=20utils=E5=8C=85=E7=9A=84=E4=BB=A3=E7=A0=81.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 15 ++++---- apiServer.go | 4 +-- docs/install.md | 4 ++- netLayer/addr.go | 4 +++ netLayer/dial.go | 2 +- netLayer/listen.go | 4 +-- netLayer/sockopt.go | 4 +-- netLayer/sockopt_linux.go | 11 +++--- netLayer/sockopt_other.go | 3 +- netLayer/tproxy/tproxy_linux.go | 56 +++++++++++++++++++++++++++++ netLayer/tproxy/udp_linux.go | 2 ++ proxy/simplesocks/server.go | 2 +- proxy/socks5/server.go | 2 +- proxy/trojan/server.go | 2 +- proxy/vless/server.go | 2 +- utils/buffers.go | 2 +- utils/log.go | 5 +-- utils/pool.go | 64 +++++++++++++++++---------------- utils/time.go | 4 +-- utils/utils.go | 3 +- utils/uuid.go | 5 ++- 21 files changed, 133 insertions(+), 67 deletions(-) diff --git a/README.md b/README.md index c41ee53..2d73afb 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![GoDoc][1]][2] [![MIT licensed][3]][4] [![Go Report Card][5]][6] +[![GoDoc][1]][2] [![MIT licensed][3]][4] [![Go Report Card][5]][6] [![Downloads][7]][8] [![release][9]][8] ![GoVersion][10] [1]: https://godoc.org/github.com/hahahrfool/v2ray_simple?status.svg [2]: https://godoc.org/github.com/hahahrfool/v2ray_simple @@ -6,6 +6,11 @@ [4]: LICENSE [5]: https://goreportcard.com/badge/github.com/hahahrfool/v2ray_simple [6]: https://goreportcard.com/report/github.com/hahahrfool/v2ray_simple +[7]: https://img.shields.io/github/downloads/hahahrfool/v2ray_simple/total.svg +[8]: https://github.com/hahahrfool/v2ray_simple/releases/latest +[9]: https://img.shields.io/github/release/hahahrfool/v2ray_simple/all.svg?style=flat-square +[10]: https://img.shields.io/github/go-mod/go-version/hahahrfool/v2ray_simple?style=flat-square + # verysimple @@ -25,13 +30,13 @@ vs的一些亮点是 全协议readv加速,lazy技术,vless v1,hysteria 阻 支持的功能有: -ws(以及earlydata)/grpc/quic(以及hy阻控)/smux, +socks5/http/dokodemo/tproxy(透明代理)/trojan/simplesocks/vless/vless_v1, -tproxy(即透明代理)/trojan/simplesocks/vless/vless_v1/socks5/http/dokodemo, +ws(以及earlydata)/grpc/quic(以及hy阻控)/smux, dns(udp/tls)/route(geoip/geosite)/fallback(path/sni/alpn), -tcp/udp/unix domain socket, uTls, lazy, http伪装头, cli/apiServer +tcp/udp/unix domain socket, uTls, lazy, http伪装头, cli(交互模式)/apiServer 为了不吓跑小白,目前 本 README 把安装、使用方式 放在了前面,如果你要直接阅读本作的技术介绍部分,点击跳转 -> [创新点](#创新点) @@ -296,8 +301,6 @@ api服务器;tproxy 透明代理; http伪装头 这里的v1是 verysimple 自己制定的,总是要摸着石头过河嘛。标准的讨论详见 [vless_v1](docs/vless_v1.md) -在客户端的 配置url中,添加 `?version=1` 即可生效。 - 总之,强制tls,简单修订了一下协议格式,然后重点完善了fullcone。 verysimple 实现了 一种独创的 非mux型“分离信道”方法的 udp over tcp 的fullcone diff --git a/apiServer.go b/apiServer.go index c21e98e..a6e99d9 100644 --- a/apiServer.go +++ b/apiServer.go @@ -38,9 +38,7 @@ func checkConfigAndTryRunApiServer() { //阻塞 func runApiServer(adminUUID string) { - if ce := utils.CanLogInfo("Start Api Server"); ce != nil { - ce.Write() - } + utils.Info("Start Api Server") ser := newApiServer("admin", adminUUID) diff --git a/docs/install.md b/docs/install.md index 90f0f26..bf55a10 100644 --- a/docs/install.md +++ b/docs/install.md @@ -7,7 +7,7 @@ 下面的命令也不要整个一大段拷贝,而要分条拷贝到vps并运行。 -## 第〇步,准备阶段 +## 第〇步,准备 首先确保自己服务器相应端口都是打开状态,防火墙要处理一下。然后安装一些BBR之类的加速组件。 @@ -20,6 +20,8 @@ 注意,如果你以前用过verysimple,则最好在运行前将自己配置文件先拷贝到其它地方,防止下面代码将你原来配置误删除。 或者你可以先解压 新版verysimple可执行文件 到其他位置,然后再用 mv 覆盖掉老版本 的可执行文件 和 examples 目录。 +### 命令 + ```sh sudo rm -rf /usr/local/etc/verysimple sudo mkdir -p /usr/local/etc/verysimple diff --git a/netLayer/addr.go b/netLayer/addr.go index e2adb63..0e88498 100644 --- a/netLayer/addr.go +++ b/netLayer/addr.go @@ -292,6 +292,10 @@ func (a *Addr) IsEmpty() bool { return a.Name == "" && len(a.IP) == 0 && a.Network == "" && a.Port == 0 } +func (a *Addr) IsIpv6() bool { + return a.IP.To4() == nil +} + func (a *Addr) GetNetIPAddr() (na netip.Addr) { if len(a.IP) < 1 { return diff --git a/netLayer/dial.go b/netLayer/dial.go index 6e81f81..3353b56 100644 --- a/netLayer/dial.go +++ b/netLayer/dial.go @@ -100,7 +100,7 @@ func (addr Addr) DialWithOpt(sockopt *Sockopt) (net.Conn, error) { } dialer.Control = func(network, address string, c syscall.RawConn) error { return c.Control(func(fd uintptr) { - SetSockOpt(int(fd), sockopt, addr.IsUDP()) + SetSockOpt(int(fd), sockopt, addr.IsUDP(), addr.IsIpv6()) }) } diff --git a/netLayer/listen.go b/netLayer/listen.go index e5fb9dd..679bf85 100644 --- a/netLayer/listen.go +++ b/netLayer/listen.go @@ -77,7 +77,7 @@ func ListenAndAccept(network, addr string, sockopt *Sockopt, acceptFunc func(net } if sockopt != nil { - SetSockOptForListener(tcplistener, sockopt, false) + SetSockOptForListener(tcplistener, sockopt, false, ta.IP.To4() == nil) } go loopAccept(tcplistener, acceptFunc) @@ -138,7 +138,7 @@ func (addr Addr) ListenUDP_withOpt(sockopt *Sockopt) (net.PacketConn, error) { var lc net.ListenConfig lc.Control = func(network, address string, c syscall.RawConn) error { return c.Control(func(fd uintptr) { - SetSockOpt(int(fd), sockopt, true) + SetSockOpt(int(fd), sockopt, true, addr.IsIpv6()) }) } return lc.ListenPacket(context.Background(), "udp", addr.String()) diff --git a/netLayer/sockopt.go b/netLayer/sockopt.go index 435df62..2613f25 100644 --- a/netLayer/sockopt.go +++ b/netLayer/sockopt.go @@ -23,13 +23,13 @@ type ConnWithFile interface { File() (f *os.File, err error) } -func SetSockOptForListener(tcplistener ListenerWithFile, sockopt *Sockopt, isudp bool) { +func SetSockOptForListener(tcplistener ListenerWithFile, sockopt *Sockopt, isudp bool, isipv6 bool) { fileDescriptorSource, err := tcplistener.File() if err != nil { return } defer fileDescriptorSource.Close() - SetSockOpt(int(fileDescriptorSource.Fd()), sockopt, isudp) + SetSockOpt(int(fileDescriptorSource.Fd()), sockopt, isudp, isipv6) } //SetSockOpt 是平台相关的. diff --git a/netLayer/sockopt_linux.go b/netLayer/sockopt_linux.go index c6555be..0fa6134 100644 --- a/netLayer/sockopt_linux.go +++ b/netLayer/sockopt_linux.go @@ -5,7 +5,7 @@ import ( "syscall" ) -func SetSockOpt(fd int, sockopt *Sockopt, isudp bool) { +func SetSockOpt(fd int, sockopt *Sockopt, isudp bool, isipv6 bool) { if sockopt == nil { return } @@ -18,7 +18,7 @@ func SetSockOpt(fd int, sockopt *Sockopt, isudp bool) { setTproxy(int(fd)) if isudp { - setTproxy_udp(int(fd)) + setTproxy_udp(int(fd), isipv6) } } @@ -28,12 +28,15 @@ func setTproxy(fd int) error { return syscall.SetsockoptInt(fd, syscall.SOL_IP, syscall.IP_TRANSPARENT, 1) } -func setTproxy_udp(fd int) error { +func setTproxy_udp(fd int, isipv6 bool) error { err1 := syscall.SetsockoptInt(fd, syscall.SOL_IP, syscall.IP_RECVORIGDSTADDR, 1) if err1 != nil { return err1 } - return syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, unix.IPV6_RECVORIGDSTADDR, 1) + if isipv6 { + return syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, unix.IPV6_RECVORIGDSTADDR, 1) + } + return nil } func setSomark(fd int, somark int) error { diff --git a/netLayer/sockopt_other.go b/netLayer/sockopt_other.go index f8fbcf2..a34b6b8 100644 --- a/netLayer/sockopt_other.go +++ b/netLayer/sockopt_other.go @@ -3,6 +3,5 @@ package netLayer -func SetSockOpt(fd int, sockopt *Sockopt, isudp bool) { - +func SetSockOpt(fd int, sockopt *Sockopt, isudp bool, isipv6 bool) { } diff --git a/netLayer/tproxy/tproxy_linux.go b/netLayer/tproxy/tproxy_linux.go index a549d01..710049c 100644 --- a/netLayer/tproxy/tproxy_linux.go +++ b/netLayer/tproxy/tproxy_linux.go @@ -35,6 +35,62 @@ https://github.com/FarFetchd/simple_tproxy_example/blob/master/tproxy_captive_po 同时,trojan-go还使用了 https://github.com/cybozu-go/transocks/blob/master/original_dst_linux.go +Iptables + +iptables配置教程: +https://toutyrater.github.io/app/tproxy.html + +下面把该教程的重要部分搬过来。 + + + ip rule add fwmark 1 table 100 + ip route add local 0.0.0.0/0 dev lo table 100 + + iptables -t mangle -N V2RAY + iptables -t mangle -A V2RAY -d 127.0.0.1/32 -j RETURN + iptables -t mangle -A V2RAY -d 224.0.0.0/4 -j RETURN + iptables -t mangle -A V2RAY -d 255.255.255.255/32 -j RETURN + iptables -t mangle -A V2RAY -d 192.168.0.0/16 -p tcp -j RETURN + iptables -t mangle -A V2RAY -d 192.168.0.0/16 -p udp ! --dport 53 -j RETURN + iptables -t mangle -A V2RAY -p udp -j TPROXY --on-port 12345 --tproxy-mark 1 + iptables -t mangle -A V2RAY -p tcp -j TPROXY --on-port 12345 --tproxy-mark 1 + iptables -t mangle -A PREROUTING -j V2RAY + + iptables -t mangle -N V2RAY_MASK + iptables -t mangle -A V2RAY_MASK -d 224.0.0.0/4 -j RETURN + iptables -t mangle -A V2RAY_MASK -d 255.255.255.255/32 -j RETURN + iptables -t mangle -A V2RAY_MASK -d 192.168.0.0/16 -p tcp -j RETURN + iptables -t mangle -A V2RAY_MASK -d 192.168.0.0/16 -p udp ! --dport 53 -j RETURN + iptables -t mangle -A V2RAY_MASK -j RETURN -m mark --mark 0xff + iptables -t mangle -A V2RAY_MASK -p udp -j MARK --set-mark 1 + iptables -t mangle -A V2RAY_MASK -p tcp -j MARK --set-mark 1 + iptables -t mangle -A OUTPUT -j V2RAY_MASK # 应用规则 + + +Persistant iptables + +单独设置iptables,重启后会消失. 下面是持久化方法 + + mkdir -p /etc/iptables && iptables-save > /etc/iptables/rules.v4 + + vi /etc/systemd/system/tproxyrule.service + + [Unit] + Description=Tproxy rule + After=network.target + Wants=network.target + + [Service] + + Type=oneshot + ExecStart=/sbin/ip rule add fwmark 1 table 100 ; /sbin/ip route add local 0.0.0.0/0 dev lo table 100 ; /sbin/iptables-restore /etc/iptables/rules.v4 + + [Install] + WantedBy=multi-user.target + + + systemctl enable tproxyrule + */ package tproxy diff --git a/netLayer/tproxy/udp_linux.go b/netLayer/tproxy/udp_linux.go index a0e8f08..fe4a88f 100644 --- a/netLayer/tproxy/udp_linux.go +++ b/netLayer/tproxy/udp_linux.go @@ -11,6 +11,8 @@ import ( "unsafe" ) +//credit: https://github.com/LiamHaworth/go-tproxy/blob/master/tproxy_udp.go , which is under MIT License + // ListenUDP will construct a new UDP listener // socket with the Linux IP_TRANSPARENT option // set on the underlying socket diff --git a/proxy/simplesocks/server.go b/proxy/simplesocks/server.go index 5477aa6..bf0cf52 100644 --- a/proxy/simplesocks/server.go +++ b/proxy/simplesocks/server.go @@ -45,7 +45,7 @@ func (s *Server) Handshake(underlay net.Conn) (result net.Conn, msgConn netLayer } defer underlay.SetReadDeadline(time.Time{}) - readbs := utils.GetBytes(utils.StandardBytesLength) + readbs := utils.GetBytes(utils.MTU) wholeReadLen, err := underlay.Read(readbs) if err != nil { diff --git a/proxy/socks5/server.go b/proxy/socks5/server.go index 13311dd..b69032a 100644 --- a/proxy/socks5/server.go +++ b/proxy/socks5/server.go @@ -276,7 +276,7 @@ func (u *ServerUDPConn) ReadMsgFrom() ([]byte, netLayer.Addr, error) { if !addr.IP.Equal(u.clientSupposedAddr.IP) || addr.Port != u.clientSupposedAddr.Port { //just random attack message. - return nil, netLayer.Addr{}, utils.ErrInErr{ErrDesc: "socks5 UDPConn ReadMsg failed, addr not comming from supposed client addr", ErrDetail: utils.ErrInvalidData, Data: addr.String()} + return nil, netLayer.Addr{}, utils.ErrInErr{ErrDesc: "socks5 UDPConn ReadMsg failed, addr not coming from supposed client addr", ErrDetail: utils.ErrInvalidData, Data: addr.String()} } } diff --git a/proxy/trojan/server.go b/proxy/trojan/server.go index 479448e..bcf6fb2 100644 --- a/proxy/trojan/server.go +++ b/proxy/trojan/server.go @@ -66,7 +66,7 @@ func (s *Server) Handshake(underlay net.Conn) (result net.Conn, msgConn netLayer } defer underlay.SetReadDeadline(time.Time{}) - readbs := utils.GetBytes(utils.StandardBytesLength) + readbs := utils.GetBytes(utils.MTU) wholeReadLen, err := underlay.Read(readbs) if err != nil { diff --git a/proxy/vless/server.go b/proxy/vless/server.go index db7c99c..9c1456f 100644 --- a/proxy/vless/server.go +++ b/proxy/vless/server.go @@ -142,7 +142,7 @@ func (s *Server) Handshake(underlay net.Conn) (result net.Conn, msgConn netLayer // 因此v1.0.3以及更老版本都是直接一段一段read的。 //但是,因为需要支持fallback技术,所以还是要 进行缓存 - readbs := utils.GetBytes(utils.StandardBytesLength) + readbs := utils.GetBytes(utils.MTU) wholeReadLen, err := underlay.Read(readbs) if err != nil { diff --git a/utils/buffers.go b/utils/buffers.go index 55ab627..c82970a 100644 --- a/utils/buffers.go +++ b/utils/buffers.go @@ -133,7 +133,7 @@ func MergeBuffers(bs [][]byte) (result []byte, duplicate bool) { return b0, false } - if allLen <= MaxBufLen { + if allLen <= MaxPacketLen { result = GetPacket() } else { diff --git a/utils/log.go b/utils/log.go index f7b890e..f846151 100644 --- a/utils/log.go +++ b/utils/log.go @@ -25,8 +25,6 @@ const ( // LogLevel 值越小越唠叨, 废话越多,值越大打印的越少,见log_开头的常量; // -// 默认是 info级别.因为还在开发中,所以默认级别高一些有好处,方便排错 -// //我们的loglevel具体值 与 zap的 loglevel+1 的含义等价 var ( LogLevel int @@ -109,7 +107,7 @@ func InitLog() { if ShouldLogToFile && LogOutFileName != "" { jsonConf := zap.NewProductionEncoderConfig() - jsonConf.EncodeTime = zapcore.TimeEncoderOfLayout("060102 150405.000") //用一种比较简短的方式输出时间,年月日 时分秒.毫秒。 年分只需输出后两位数字即可, 不管Y2K问题, 80年后要是还没实现网络自由那这个世界完蛋了. + jsonConf.EncodeTime = zapcore.TimeEncoderOfLayout("060102 150405.000") //用一种比较简短的方式输出时间,年月日 时分秒.毫秒。 年只需输出后两位数字即可, 不管Y2K问题, 80年后要是还没实现网络自由那这个世界完蛋了. jsonConf.LevelKey = "L" jsonConf.TimeKey = "T" jsonConf.MessageKey = "M" @@ -123,7 +121,6 @@ func InitLog() { } - //zap.NewDevelopmentEncoderConfig() ZapLogger.Info("zap log init complete.") } diff --git a/utils/pool.go b/utils/pool.go index 8dee7e6..b404ca7 100644 --- a/utils/pool.go +++ b/utils/pool.go @@ -6,43 +6,47 @@ import ( ) var ( - standardBytesPool sync.Pool //专门储存 长度为 StandardBytesLength 的 []byte + mtuPool sync.Pool //专门储存 长度为 MTU 的 []byte - // 作为参考对比,tcp默认是 16384, 16k,实际上范围是1k~128k之间 - // 而 udp则最大还不到 64k。(65535-20-8) - // io.Copy 内部默认buffer大小为 32k + // packetPool 专门储存 长度为 MaxPacketLen 的 []byte + // + // 参考对比: tcp默认是 16k,范围是1k~128k; + // 而 udp则最大还不到 64k (65535-20-8); + // io.Copy 内部默认buffer大小为 32k; // 总之 我们64k已经够了 - standardPacketPool sync.Pool // 专门储存 长度为 MaxBufLen 的 []byte + packetPool sync.Pool bufPool sync.Pool //储存 *bytes.Buffer ) -//即MTU, Maximum transmission unit, 参照的是 Ethernet v2 的MTU; -const StandardBytesLength int = 1500 +const ( + //即 Maximum transmission unit, 参照的是 Ethernet v2 的MTU; + MTU int = 1500 -//注意wifi信号MTU是 2304,我们并未考虑wifi,主要是因为就算用wifi传, 早晚还是要经过以太网,除非两个wifi设备互传 -// https://en.wikipedia.org/wiki/Maximum_transmission_unit + //注意wifi信号MTU是 2304,我们并未考虑wifi,主要是因为就算用wifi传, 早晚还是要经过以太网,除非两个wifi设备互传 + // https://en.wikipedia.org/wiki/Maximum_transmission_unit -//本作设定的最大buf大小,64k -const MaxBufLen = 64 * 1024 + //本作设定的最大包 长度大小,64k + MaxPacketLen = 64 * 1024 +) func init() { - standardBytesPool = sync.Pool{ + mtuPool = sync.Pool{ New: func() interface{} { - return make([]byte, StandardBytesLength) + return make([]byte, MTU) }, } - standardPacketPool = sync.Pool{ + packetPool = sync.Pool{ New: func() interface{} { - return make([]byte, MaxBufLen) + return make([]byte, MaxPacketLen) }, } bufPool = sync.Pool{ New: func() interface{} { - return &bytes.Buffer{} + return new(bytes.Buffer) }, } } @@ -60,31 +64,31 @@ func PutBuf(buf *bytes.Buffer) { //建议在 Read net.Conn 时, 使用 GetPacket函数 获取到足够大的 []byte(MaxBufLen) func GetPacket() []byte { - return standardPacketPool.Get().([]byte) + return packetPool.Get().([]byte) } // 放回用 GetPacket 获取的 []byte func PutPacket(bs []byte) { c := cap(bs) - if c < MaxBufLen { - if c >= StandardBytesLength { - standardBytesPool.Put(bs[:StandardBytesLength]) + if c < MaxPacketLen { + if c >= MTU { + mtuPool.Put(bs[:MTU]) } return } - standardPacketPool.Put(bs[:MaxBufLen]) + packetPool.Put(bs[:MaxPacketLen]) } -// 从Pool中获取一个 StandardBytesLength 长度的 []byte +// 从Pool中获取一个 MTU 长度的 []byte func GetMTU() []byte { - return standardBytesPool.Get().([]byte) + return mtuPool.Get().([]byte) } // 从pool中获取 []byte, 根据给出长度不同,来源于的Pool会不同. func GetBytes(size int) []byte { - if size <= StandardBytesLength { - bs := standardBytesPool.Get().([]byte) + if size <= MTU { + bs := mtuPool.Get().([]byte) return bs[:size] } @@ -95,12 +99,12 @@ func GetBytes(size int) []byte { // 根据bs长度 选择放入各种pool中, 只有 cap(bs)>=1500 才会被处理 func PutBytes(bs []byte) { c := cap(bs) - if c < StandardBytesLength { + if c < MTU { return - } else if c >= StandardBytesLength && c < MaxBufLen { - standardBytesPool.Put(bs[:StandardBytesLength]) - } else if c >= MaxBufLen { - standardPacketPool.Put(bs[:MaxBufLen]) + } else if c >= MTU && c < MaxPacketLen { + mtuPool.Put(bs[:MTU]) + } else if c >= MaxPacketLen { + packetPool.Put(bs[:MaxPacketLen]) } } diff --git a/utils/time.go b/utils/time.go index f19ad31..a626144 100644 --- a/utils/time.go +++ b/utils/time.go @@ -12,12 +12,12 @@ import ( func fixAndroidTimezone() { out, err := exec.Command("/system/bin/getprop", "persist.sys.timezone").Output() if err != nil { - log.Println("fixAndroidTimezone failed", err) + log.Println("fixAndroidTimezone failed when calling /system/bin/getprop,", err) return } z, err := time.LoadLocation(strings.TrimSpace(string(out))) if err != nil { - log.Println("fixAndroidTimezone failed", err) + log.Println("fixAndroidTimezone failed,", err) return } time.Local = z diff --git a/utils/utils.go b/utils/utils.go index d1e59fa..0484dea 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -2,7 +2,6 @@ package utils import ( - "errors" "flag" "math/rand" "strings" @@ -78,6 +77,6 @@ func WrapFuncForPromptUI(f func(string) bool) func(string) error { if f(s) { return nil } - return errors.New("not valid") + return ErrInvalidData } } diff --git a/utils/uuid.go b/utils/uuid.go index 5c5c36c..b827e93 100644 --- a/utils/uuid.go +++ b/utils/uuid.go @@ -3,17 +3,16 @@ package utils import ( "crypto/rand" "encoding/hex" - "errors" "strings" ) func StrToUUID(s string) (uuid [16]byte, err error) { if len(s) != 36 { - return uuid, errors.New("invalid UUID Str: " + s) + return uuid, ErrInErr{ErrDesc: "invalid UUID Str", ErrDetail: ErrInvalidData, Data: s} } b := []byte(strings.Replace(s, "-", "", -1)) if len(b) != 32 { - return uuid, errors.New("invalid UUID Str: " + s) + return uuid, ErrInErr{ErrDesc: "invalid UUID Str", ErrDetail: ErrInvalidData, Data: s} } _, err = hex.Decode(uuid[:], b) return