http.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. package main
  2. import (
  3. "bytes"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "log"
  8. "net/http"
  9. "os"
  10. "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/messages"
  11. "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/util"
  12. )
  13. const (
  14. readLimit = 100000 // Maximum number of bytes to be read from an HTTP request
  15. )
  16. // Implements the http.Handler interface
  17. type SnowflakeHandler struct {
  18. *IPC
  19. handle func(*IPC, http.ResponseWriter, *http.Request)
  20. }
  21. func (sh SnowflakeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  22. w.Header().Set("Access-Control-Allow-Origin", "*")
  23. w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Session-ID")
  24. // Return early if it's CORS preflight.
  25. if "OPTIONS" == r.Method {
  26. return
  27. }
  28. sh.handle(sh.IPC, w, r)
  29. }
  30. // Implements the http.Handler interface
  31. type MetricsHandler struct {
  32. logFilename string
  33. handle func(string, http.ResponseWriter, *http.Request)
  34. }
  35. func (mh MetricsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  36. w.Header().Set("Access-Control-Allow-Origin", "*")
  37. w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Session-ID")
  38. // Return early if it's CORS preflight.
  39. if "OPTIONS" == r.Method {
  40. return
  41. }
  42. mh.handle(mh.logFilename, w, r)
  43. }
  44. func robotsTxtHandler(w http.ResponseWriter, r *http.Request) {
  45. w.Header().Set("Content-Type", "text/plain; charset=utf-8")
  46. if _, err := w.Write([]byte("User-agent: *\nDisallow: /\n")); err != nil {
  47. log.Printf("robotsTxtHandler unable to write, with this error: %v", err)
  48. }
  49. }
  50. func metricsHandler(metricsFilename string, w http.ResponseWriter, r *http.Request) {
  51. w.Header().Set("Content-Type", "text/plain; charset=utf-8")
  52. if metricsFilename == "" {
  53. http.NotFound(w, r)
  54. return
  55. }
  56. metricsFile, err := os.OpenFile(metricsFilename, os.O_RDONLY, 0644)
  57. if err != nil {
  58. log.Println("Error opening metrics file for reading")
  59. http.NotFound(w, r)
  60. return
  61. }
  62. if _, err := io.Copy(w, metricsFile); err != nil {
  63. log.Printf("copying metricsFile returned error: %v", err)
  64. }
  65. }
  66. func debugHandler(i *IPC, w http.ResponseWriter, r *http.Request) {
  67. var response string
  68. err := i.Debug(new(interface{}), &response)
  69. if err != nil {
  70. log.Println(err)
  71. w.WriteHeader(http.StatusInternalServerError)
  72. return
  73. }
  74. if _, err := w.Write([]byte(response)); err != nil {
  75. log.Printf("writing proxy information returned error: %v ", err)
  76. }
  77. }
  78. /*
  79. For snowflake proxies to request a client from the Broker.
  80. */
  81. func proxyPolls(i *IPC, w http.ResponseWriter, r *http.Request) {
  82. body, err := io.ReadAll(http.MaxBytesReader(w, r.Body, readLimit))
  83. if err != nil {
  84. log.Println("Invalid data.", err.Error())
  85. w.WriteHeader(http.StatusBadRequest)
  86. return
  87. }
  88. arg := messages.Arg{
  89. Body: body,
  90. RemoteAddr: util.GetClientIp(r),
  91. }
  92. var response []byte
  93. err = i.ProxyPolls(arg, &response)
  94. switch {
  95. case err == nil:
  96. case errors.Is(err, messages.ErrBadRequest):
  97. w.WriteHeader(http.StatusBadRequest)
  98. return
  99. case errors.Is(err, messages.ErrInternal):
  100. fallthrough
  101. default:
  102. log.Println(err)
  103. w.WriteHeader(http.StatusInternalServerError)
  104. return
  105. }
  106. if _, err := w.Write(response); err != nil {
  107. log.Printf("proxyPolls unable to write offer with error: %v", err)
  108. }
  109. }
  110. /*
  111. Expects a WebRTC SDP offer in the Request to give to an assigned
  112. snowflake proxy, which responds with the SDP answer to be sent in
  113. the HTTP response back to the client.
  114. */
  115. func clientOffers(i *IPC, w http.ResponseWriter, r *http.Request) {
  116. body, err := io.ReadAll(http.MaxBytesReader(w, r.Body, readLimit))
  117. if err != nil {
  118. log.Printf("Error reading client request: %s", err.Error())
  119. w.WriteHeader(http.StatusBadRequest)
  120. return
  121. }
  122. err = validateSDP(body)
  123. if err != nil {
  124. log.Println("Error client SDP: ", err.Error())
  125. w.WriteHeader(http.StatusBadRequest)
  126. return
  127. }
  128. // Handle the legacy version
  129. //
  130. // We support two client message formats. The legacy format is for backwards
  131. // compatability and relies heavily on HTTP headers and status codes to convey
  132. // information.
  133. isLegacy := false
  134. if len(body) > 0 && body[0] == '{' {
  135. isLegacy = true
  136. req := messages.ClientPollRequest{
  137. Offer: string(body),
  138. NAT: r.Header.Get("Snowflake-NAT-Type"),
  139. }
  140. body, err = req.EncodeClientPollRequest()
  141. if err != nil {
  142. log.Printf("Error shimming the legacy request: %s", err.Error())
  143. w.WriteHeader(http.StatusInternalServerError)
  144. return
  145. }
  146. }
  147. arg := messages.Arg{
  148. Body: body,
  149. RemoteAddr: util.GetClientIp(r),
  150. RendezvousMethod: messages.RendezvousHttp,
  151. }
  152. var response []byte
  153. err = i.ClientOffers(arg, &response)
  154. if err != nil {
  155. log.Println(err)
  156. w.WriteHeader(http.StatusInternalServerError)
  157. return
  158. }
  159. if isLegacy {
  160. resp, err := messages.DecodeClientPollResponse(response)
  161. if err != nil {
  162. log.Println(err)
  163. w.WriteHeader(http.StatusInternalServerError)
  164. return
  165. }
  166. switch resp.Error {
  167. case "":
  168. response = []byte(resp.Answer)
  169. case messages.StrNoProxies:
  170. w.WriteHeader(http.StatusServiceUnavailable)
  171. return
  172. case messages.StrTimedOut:
  173. w.WriteHeader(http.StatusGatewayTimeout)
  174. return
  175. default:
  176. panic("unknown error")
  177. }
  178. }
  179. if _, err := w.Write(response); err != nil {
  180. log.Printf("clientOffers unable to write answer with error: %v", err)
  181. }
  182. }
  183. /*
  184. Expects snowflake proxies which have previously successfully received
  185. an offer from proxyHandler to respond with an answer in an HTTP POST,
  186. which the broker will pass back to the original client.
  187. */
  188. func proxyAnswers(i *IPC, w http.ResponseWriter, r *http.Request) {
  189. body, err := io.ReadAll(http.MaxBytesReader(w, r.Body, readLimit))
  190. if err != nil {
  191. log.Println("Invalid data.", err.Error())
  192. w.WriteHeader(http.StatusBadRequest)
  193. return
  194. }
  195. err = validateSDP(body)
  196. if err != nil {
  197. log.Println("Error proxy SDP: ", err.Error())
  198. w.WriteHeader(http.StatusBadRequest)
  199. return
  200. }
  201. arg := messages.Arg{
  202. Body: body,
  203. RemoteAddr: util.GetClientIp(r),
  204. }
  205. var response []byte
  206. err = i.ProxyAnswers(arg, &response)
  207. switch {
  208. case err == nil:
  209. case errors.Is(err, messages.ErrBadRequest):
  210. w.WriteHeader(http.StatusBadRequest)
  211. return
  212. case errors.Is(err, messages.ErrInternal):
  213. fallthrough
  214. default:
  215. log.Println(err)
  216. w.WriteHeader(http.StatusInternalServerError)
  217. return
  218. }
  219. if _, err := w.Write(response); err != nil {
  220. log.Printf("proxyAnswers unable to write answer response with error: %v", err)
  221. }
  222. }
  223. func validateSDP(SDP []byte) error {
  224. // TODO: more validation likely needed
  225. if !bytes.Contains(SDP, []byte("a=candidate")) {
  226. return fmt.Errorf("SDP contains no candidate")
  227. }
  228. return nil
  229. }