args.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. package pt
  2. import (
  3. "bytes"
  4. "fmt"
  5. "sort"
  6. "strings"
  7. )
  8. // Key–value mappings for the representation of client and server options.
  9. // Args maps a string key to a list of values. It is similar to url.Values.
  10. type Args map[string][]string
  11. // Get the first value associated with the given key. If there are any values
  12. // associated with the key, the value return has the value and ok is set to
  13. // true. If there are no values for the given key, value is "" and ok is false.
  14. // If you need access to multiple values, use the map directly.
  15. func (args Args) Get(key string) (value string, ok bool) {
  16. if args == nil {
  17. return "", false
  18. }
  19. vals, ok := args[key]
  20. if !ok || len(vals) == 0 {
  21. return "", false
  22. }
  23. return vals[0], true
  24. }
  25. // Append value to the list of values for key.
  26. func (args Args) Add(key, value string) {
  27. args[key] = append(args[key], value)
  28. }
  29. // Return the index of the next unescaped byte in s that is in the term set, or
  30. // else the length of the string if no terminators appear. Additionally return
  31. // the unescaped string up to the returned index.
  32. func indexUnescaped(s string, term []byte) (int, string, error) {
  33. var i int
  34. unesc := make([]byte, 0)
  35. for i = 0; i < len(s); i++ {
  36. b := s[i]
  37. // A terminator byte?
  38. if bytes.IndexByte(term, b) != -1 {
  39. break
  40. }
  41. if b == '\\' {
  42. i++
  43. if i >= len(s) {
  44. return 0, "", fmt.Errorf("nothing following final escape in %q", s)
  45. }
  46. b = s[i]
  47. }
  48. unesc = append(unesc, b)
  49. }
  50. return i, string(unesc), nil
  51. }
  52. // Parse a name–value mapping as from an encoded SOCKS username/password.
  53. //
  54. // "If any [k=v] items are provided, they are configuration parameters for the
  55. // proxy: Tor should separate them with semicolons ... If a key or value value
  56. // must contain [an equals sign or] a semicolon or a backslash, it is escaped
  57. // with a backslash."
  58. func parseClientParameters(s string) (args Args, err error) {
  59. args = make(Args)
  60. if len(s) == 0 {
  61. return
  62. }
  63. i := 0
  64. for {
  65. var key, value string
  66. var offset, begin int
  67. begin = i
  68. // Read the key.
  69. offset, key, err = indexUnescaped(s[i:], []byte{'=', ';'})
  70. if err != nil {
  71. return
  72. }
  73. i += offset
  74. // End of string or no equals sign?
  75. if i >= len(s) || s[i] != '=' {
  76. err = fmt.Errorf("no equals sign in %q", s[begin:i])
  77. return
  78. }
  79. // Skip the equals sign.
  80. i++
  81. // Read the value.
  82. offset, value, err = indexUnescaped(s[i:], []byte{';'})
  83. if err != nil {
  84. return
  85. }
  86. i += offset
  87. if len(key) == 0 {
  88. err = fmt.Errorf("empty key in %q", s[begin:i])
  89. return
  90. }
  91. args.Add(key, value)
  92. if i >= len(s) {
  93. break
  94. }
  95. // Skip the semicolon.
  96. i++
  97. }
  98. return args, nil
  99. }
  100. // Parse a transport–name–value mapping as from TOR_PT_SERVER_TRANSPORT_OPTIONS.
  101. //
  102. // "<value> is a k=v string value with options that are to be passed to the
  103. // transport. Colons, semicolons, equal signs and backslashes must be escaped
  104. // with a backslash."
  105. // Example: trebuchet:secret=nou;trebuchet:cache=/tmp/cache;ballista:secret=yes
  106. func parseServerTransportOptions(s string) (opts map[string]Args, err error) {
  107. opts = make(map[string]Args)
  108. if len(s) == 0 {
  109. return
  110. }
  111. i := 0
  112. for {
  113. var methodName, key, value string
  114. var offset, begin int
  115. begin = i
  116. // Read the method name.
  117. offset, methodName, err = indexUnescaped(s[i:], []byte{':', '=', ';'})
  118. if err != nil {
  119. return
  120. }
  121. i += offset
  122. // End of string or no colon?
  123. if i >= len(s) || s[i] != ':' {
  124. err = fmt.Errorf("no colon in %q", s[begin:i])
  125. return
  126. }
  127. // Skip the colon.
  128. i++
  129. // Read the key.
  130. offset, key, err = indexUnescaped(s[i:], []byte{'=', ';'})
  131. if err != nil {
  132. return
  133. }
  134. i += offset
  135. // End of string or no equals sign?
  136. if i >= len(s) || s[i] != '=' {
  137. err = fmt.Errorf("no equals sign in %q", s[begin:i])
  138. return
  139. }
  140. // Skip the equals sign.
  141. i++
  142. // Read the value.
  143. offset, value, err = indexUnescaped(s[i:], []byte{';'})
  144. if err != nil {
  145. return
  146. }
  147. i += offset
  148. if len(methodName) == 0 {
  149. err = fmt.Errorf("empty method name in %q", s[begin:i])
  150. return
  151. }
  152. if len(key) == 0 {
  153. err = fmt.Errorf("empty key in %q", s[begin:i])
  154. return
  155. }
  156. if opts[methodName] == nil {
  157. opts[methodName] = make(Args)
  158. }
  159. opts[methodName].Add(key, value)
  160. if i >= len(s) {
  161. break
  162. }
  163. // Skip the semicolon.
  164. i++
  165. }
  166. return opts, nil
  167. }
  168. // Escape backslashes and all the bytes that are in set.
  169. func backslashEscape(s string, set []byte) string {
  170. var buf bytes.Buffer
  171. for _, b := range []byte(s) {
  172. if b == '\\' || bytes.IndexByte(set, b) != -1 {
  173. buf.WriteByte('\\')
  174. }
  175. buf.WriteByte(b)
  176. }
  177. return buf.String()
  178. }
  179. // Encode a name–value mapping so that it is suitable to go in the ARGS option
  180. // of an SMETHOD line. The output is sorted by key. The "ARGS:" prefix is not
  181. // added.
  182. //
  183. // "Equal signs and commas [and backslashes] must be escaped with a backslash."
  184. func encodeSmethodArgs(args Args) string {
  185. if args == nil {
  186. return ""
  187. }
  188. keys := make([]string, 0, len(args))
  189. for key := range args {
  190. keys = append(keys, key)
  191. }
  192. sort.Strings(keys)
  193. escape := func(s string) string {
  194. return backslashEscape(s, []byte{'=', ','})
  195. }
  196. var pairs []string
  197. for _, key := range keys {
  198. for _, value := range args[key] {
  199. pairs = append(pairs, escape(key)+"="+escape(value))
  200. }
  201. }
  202. return strings.Join(pairs, ",")
  203. }