package rpc import ( "bytes" "crypto/rand" "encoding/binary" "net" "github.com/juju/errors" "github.com/9seconds/mtg/mtproto" ) type ProxyRequest struct { Flags proxyRequestFlags ConnectionID []byte OurIPPort []byte ClientIPPort []byte ADTag []byte Options *mtproto.ConnectionOpts } func (r *ProxyRequest) MakeHeader(message []byte) *bytes.Buffer { bufferLength := len(TagProxyRequest) + 4 + // len(flags) len(r.ConnectionID) + len(r.ClientIPPort) + len(r.OurIPPort) + len(ProxyRequestExtraSize) + len(ProxyRequestProxyTag) + 1 + // len(AdTag) len(r.ADTag) bufferLength += bufferLength % 4 buf := &bytes.Buffer{} buf.Grow(bufferLength + len(message)) flags := r.Flags if r.Options.ReadHacks.QuickAck { flags |= proxyRequestFlagsQuickAck } if bytes.HasPrefix(message, proxyRequestFlagsEncryptedPrefix[:]) { flags |= proxyRequestFlagsEncrypted } buf.Write(TagProxyRequest) buf.Write(flags.Bytes()) buf.Write(r.ConnectionID) buf.Write(r.ClientIPPort) buf.Write(r.OurIPPort) buf.Write(ProxyRequestExtraSize) buf.Write(ProxyRequestProxyTag) buf.WriteByte(byte(len(r.ADTag))) buf.Write(r.ADTag) buf.Write(make([]byte, (4-buf.Len()%4)%4)) return buf } func NewProxyRequest(clientAddr, ownAddr *net.TCPAddr, opts *mtproto.ConnectionOpts, adTag []byte) (*ProxyRequest, error) { flags := proxyRequestFlagsHasAdTag | proxyRequestFlagsMagic | proxyRequestFlagsExtMode2 switch opts.ConnectionType { case mtproto.ConnectionTypeAbridged: flags |= proxyRequestFlagsAbdridged case mtproto.ConnectionTypeIntermediate: flags |= proxyRequestFlagsIntermediate } request := &ProxyRequest{ Flags: flags, ADTag: adTag, Options: opts, ConnectionID: make([]byte, 8), ClientIPPort: make([]byte, 16+4), OurIPPort: make([]byte, 16+4), } if _, err := rand.Read(request.ConnectionID); err != nil { return nil, errors.Annotate(err, "Cannot generate connection ID") } port := [4]byte{} copy(request.ClientIPPort[:16], clientAddr.IP.To16()) binary.LittleEndian.PutUint32(port[:], uint32(clientAddr.Port)) copy(request.ClientIPPort[16:], port[:]) copy(request.OurIPPort[:16], ownAddr.IP.To16()) binary.LittleEndian.PutUint32(port[:], uint32(ownAddr.Port)) copy(request.OurIPPort[16:], port[:]) return request, nil }