http.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  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. // Handle the legacy version
  123. //
  124. // We support two client message formats. The legacy format is for backwards
  125. // compatability and relies heavily on HTTP headers and status codes to convey
  126. // information.
  127. isLegacy := false
  128. if len(body) > 0 && body[0] == '{' {
  129. isLegacy = true
  130. req := messages.ClientPollRequest{
  131. Offer: string(body),
  132. NAT: r.Header.Get("Snowflake-NAT-Type"),
  133. }
  134. body, err = req.EncodeClientPollRequest()
  135. if err != nil {
  136. log.Printf("Error shimming the legacy request: %s", err.Error())
  137. w.WriteHeader(http.StatusInternalServerError)
  138. return
  139. }
  140. }
  141. arg := messages.Arg{
  142. Body: body,
  143. RemoteAddr: util.GetClientIp(r),
  144. RendezvousMethod: messages.RendezvousHttp,
  145. }
  146. var response []byte
  147. err = i.ClientOffers(arg, &response)
  148. if err != nil {
  149. log.Println(err)
  150. w.WriteHeader(http.StatusInternalServerError)
  151. return
  152. }
  153. if isLegacy {
  154. resp, err := messages.DecodeClientPollResponse(response)
  155. if err != nil {
  156. log.Println(err)
  157. w.WriteHeader(http.StatusInternalServerError)
  158. return
  159. }
  160. switch resp.Error {
  161. case "":
  162. response = []byte(resp.Answer)
  163. case messages.StrNoProxies:
  164. w.WriteHeader(http.StatusServiceUnavailable)
  165. return
  166. case messages.StrTimedOut:
  167. w.WriteHeader(http.StatusGatewayTimeout)
  168. return
  169. default:
  170. panic("unknown error")
  171. }
  172. }
  173. if _, err := w.Write(response); err != nil {
  174. log.Printf("clientOffers unable to write answer with error: %v", err)
  175. }
  176. }
  177. /*
  178. Expects snowflake proxies which have previously successfully received
  179. an offer from proxyHandler to respond with an answer in an HTTP POST,
  180. which the broker will pass back to the original client.
  181. */
  182. func proxyAnswers(i *IPC, w http.ResponseWriter, r *http.Request) {
  183. body, err := io.ReadAll(http.MaxBytesReader(w, r.Body, readLimit))
  184. if err != nil {
  185. log.Println("Invalid data.", err.Error())
  186. w.WriteHeader(http.StatusBadRequest)
  187. return
  188. }
  189. err = validateSDP(body)
  190. if err != nil {
  191. log.Println("Error proxy SDP: ", err.Error())
  192. w.WriteHeader(http.StatusBadRequest)
  193. return
  194. }
  195. arg := messages.Arg{
  196. Body: body,
  197. RemoteAddr: util.GetClientIp(r),
  198. }
  199. var response []byte
  200. err = i.ProxyAnswers(arg, &response)
  201. switch {
  202. case err == nil:
  203. case errors.Is(err, messages.ErrBadRequest):
  204. w.WriteHeader(http.StatusBadRequest)
  205. return
  206. case errors.Is(err, messages.ErrInternal):
  207. fallthrough
  208. default:
  209. log.Println(err)
  210. w.WriteHeader(http.StatusInternalServerError)
  211. return
  212. }
  213. if _, err := w.Write(response); err != nil {
  214. log.Printf("proxyAnswers unable to write answer response with error: %v", err)
  215. }
  216. }
  217. func validateSDP(SDP []byte) error {
  218. // TODO: more validation likely needed
  219. if !bytes.Contains(SDP, []byte("a=candidate")) {
  220. return fmt.Errorf("SDP contains no candidate")
  221. }
  222. return nil
  223. }