github.com/sagernet/sing-box@v1.9.0-rc.20/transport/sip003/args.go (about) 1 package sip003 2 3 import ( 4 "bytes" 5 "fmt" 6 ) 7 8 // mod from https://github.com/shadowsocks/v2ray-plugin/blob/master/args.go 9 10 // Args maps a string key to a list of values. It is similar to url.Values. 11 type Args map[string][]string 12 13 // Get the first value associated with the given key. If there are any values 14 // associated with the key, the value return has the value and ok is set to 15 // true. If there are no values for the given key, value is "" and ok is false. 16 // If you need access to multiple values, use the map directly. 17 func (args Args) Get(key string) (value string, ok bool) { 18 if args == nil { 19 return "", false 20 } 21 vals, ok := args[key] 22 if !ok || len(vals) == 0 { 23 return "", false 24 } 25 return vals[0], true 26 } 27 28 // Add Append value to the list of values for key. 29 func (args Args) Add(key, value string) { 30 args[key] = append(args[key], value) 31 } 32 33 // Return the index of the next unescaped byte in s that is in the term set, or 34 // else the length of the string if no terminators appear. Additionally return 35 // the unescaped string up to the returned index. 36 func indexUnescaped(s string, term []byte) (int, string, error) { 37 var i int 38 unesc := make([]byte, 0) 39 for i = 0; i < len(s); i++ { 40 b := s[i] 41 // A terminator byte? 42 if bytes.IndexByte(term, b) != -1 { 43 break 44 } 45 if b == '\\' { 46 i++ 47 if i >= len(s) { 48 return 0, "", fmt.Errorf("nothing following final escape in %q", s) 49 } 50 b = s[i] 51 } 52 unesc = append(unesc, b) 53 } 54 return i, string(unesc), nil 55 } 56 57 // ParsePluginOptions Parse a name–value mapping as from SS_PLUGIN_OPTIONS. 58 // 59 // "<value> is a k=v string value with options that are to be passed to the 60 // transport. semicolons, equal signs and backslashes must be escaped 61 // with a backslash." 62 // Example: secret=nou;cache=/tmp/cache;secret=yes 63 func ParsePluginOptions(s string) (opts Args, err error) { 64 opts = make(Args) 65 if len(s) == 0 { 66 return 67 } 68 i := 0 69 for { 70 var key, value string 71 var offset, begin int 72 73 if i >= len(s) { 74 break 75 } 76 begin = i 77 // Read the key. 78 offset, key, err = indexUnescaped(s[i:], []byte{'=', ';'}) 79 if err != nil { 80 return 81 } 82 if len(key) == 0 { 83 err = fmt.Errorf("empty key in %q", s[begin:i]) 84 return 85 } 86 i += offset 87 // End of string or no equals sign? 88 if i >= len(s) || s[i] != '=' { 89 opts.Add(key, "1") 90 // Skip the semicolon. 91 i++ 92 continue 93 } 94 // Skip the equals sign. 95 i++ 96 // Read the value. 97 offset, value, err = indexUnescaped(s[i:], []byte{';'}) 98 if err != nil { 99 return 100 } 101 i += offset 102 opts.Add(key, value) 103 // Skip the semicolon. 104 i++ 105 } 106 return opts, nil 107 } 108 109 // Escape backslashes and all the bytes that are in set. 110 func backslashEscape(s string, set []byte) string { 111 var buf bytes.Buffer 112 for _, b := range []byte(s) { 113 if b == '\\' || bytes.IndexByte(set, b) != -1 { 114 buf.WriteByte('\\') 115 } 116 buf.WriteByte(b) 117 } 118 return buf.String() 119 }