CreateUDPStream creates a UDP stream to the given address using the client. This function should ONLY be called after calling |ConnectToProxy| successfully. If it succeeds, return a Conn struct that provides a pair of I/O interfaces. Users can then use the provided I/O interfaces to communicate wit
(addr string)
| 538 | // Otherwise, it returns nil and indicates the error. It refills the connection for |
| 539 | // the next call. |
| 540 | func (c *Client) CreateUDPStream(addr string) (*Conn, error) { |
| 541 | if c.tlsConn == nil { |
| 542 | newTr, err := c.dialProxyViaTLS() |
| 543 | if err == nil { |
| 544 | c.tlsConn = newTr |
| 545 | } else { |
| 546 | return nil, fmt.Errorf("TLS. Failed to start a new tls conn to proxy. %v", err) |
| 547 | } |
| 548 | } |
| 549 | |
| 550 | ipPort, err := netaddr.ParseIPPort(addr) |
| 551 | if err != nil { |
| 552 | return nil, errors.New("an invalid destination addr: not a IPPort") |
| 553 | } |
| 554 | |
| 555 | // Note: do not reuse a TLS connection. otherwise, the response is likely to be an error response. |
| 556 | tr := c.tlsConn |
| 557 | c.tlsConn = nil |
| 558 | |
| 559 | // Craft a HTTP/1.1 CONNECT-UDP request |
| 560 | req := fmt.Sprintf("CONNECT-UDP masque://%s/ HTTP/1.1\r\nHost: %s\r\nProxy-Authorization: PrivacyToken token=%s\r\n\r\n", |
| 561 | ipPort.String(), ipPort.String(), c.authToken) |
| 562 | |
| 563 | // Send the request and receive the CONNECT-UDP response |
| 564 | if _, err = tr.Write([]byte(req)); err != nil { |
| 565 | return nil, fmt.Errorf("failed to send a CONNECT-UDP request. %v", err) |
| 566 | } |
| 567 | |
| 568 | br := bufio.NewReader(tr) |
| 569 | resp, err := http.ReadResponse(br, nil) |
| 570 | |
| 571 | if err != nil { |
| 572 | if err := tr.Close(); err != nil { |
| 573 | c.logger.Error("Error from tr.Close", "err", err) |
| 574 | } |
| 575 | return nil, fmt.Errorf("reading HTTP response from CONNECT-UDP to %s via proxy %s failed: %v", |
| 576 | addr, c.proxyAddr, err) |
| 577 | } |
| 578 | if resp.StatusCode != 200 { |
| 579 | return nil, fmt.Errorf("proxy error from %s while dialing %s: %v", |
| 580 | c.proxyAddr, addr, resp.Status) |
| 581 | } |
| 582 | |
| 583 | c.mu.Lock() |
| 584 | // We have to use two pairs of I/O pipe here because of encoding/decoding. |
| 585 | // TODO: switch to use buffered writer for the input channel |
| 586 | inR, inW := io.Pipe() |
| 587 | outR, outW := io.Pipe() |
| 588 | ctx, cancel := context.WithCancel(context.Background()) |
| 589 | udp := &Conn{ |
| 590 | sid: getNextUdpStreamID(), |
| 591 | IoInc: inW, |
| 592 | IoOut: outR, |
| 593 | transport: tr, |
| 594 | alive: true, |
| 595 | connCtx: ctx, |
| 596 | connCancel: cancel, |
| 597 | isTcp: false, |