// This example registers an endpoint (user authentication) in a server
// and starts a call (only the SIP related part).
package main

import (
	sipgo "github.com/emiago/sipgo/sip"
	"github.com/icholy/digest"
	"github.com/vulncheck-oss/go-exploit/output"
	"github.com/vulncheck-oss/go-exploit/protocol"
	"github.com/vulncheck-oss/go-exploit/protocol/sip"
)

const (
	host     = "127.0.0.1"
	fromUser = "john.doe"
	pass     = "password"
	toUser   = "dembele"
)

func main() {
	port := sip.DefaultPorts[sip.UDP]
	conn, ok := protocol.UDPConnect(host, port)
	if !ok {
		output.PrintfFrameworkError("Connecting to server %s:%d", host, port)

		return
	}
	defer conn.Close()
	reqOpts := sip.NewSipRequestOpts{
		Port:   port,
		User:   fromUser,
		ToUser: fromUser,
	}
	req, ok := sip.NewSipRequest(sipgo.REGISTER, host, &reqOpts)
	if !ok {
		output.PrintfFrameworkError("Creating REGISTER request to %s with options %+v", host, reqOpts)

		return
	}
	resp, ok := sip.SendAndReceiveUDP(conn, req)
	if !ok {
		output.PrintfFrameworkError("Sending REGISTER request %s", req.String())

		return
	}
	if resp.StatusCode != sipgo.StatusUnauthorized {
		output.PrintfFrameworkError("Unexpected response %d (%s) to REGISTER request", resp.StatusCode, resp.Reason)

		return
	}
	wwwAuth := resp.GetHeader("WWW-Authenticate")
	chal, err := digest.ParseChallenge(wwwAuth.Value())
	if err != nil {
		output.PrintfFrameworkError("Parsing WWW-Authenticate header: %s", err)

		return
	}
	cred, _ := digest.Digest(chal, digest.Options{
		Method:   req.Method.String(),
		URI:      host,
		Username: fromUser,
		Password: pass,
	})
	newReq := req.Clone()
	authHeader := sipgo.NewHeader("Authorization", cred.String())
	newReq.AppendHeader(authHeader)
	cseqHeader := sipgo.CSeqHeader{SeqNo: uint32(2), MethodName: sipgo.REGISTER}
	newReq.ReplaceHeader(&cseqHeader)
	resp, ok = sip.SendAndReceiveUDP(conn, newReq)
	if !ok {
		output.PrintfFrameworkError("Sending REGISTER request with Authorization header %s", newReq.String())

		return
	}
	if resp.StatusCode != sipgo.StatusOK {
		output.PrintfFrameworkError("Unexpected response %d (%s) to REGISTER request with Authorization header", resp.StatusCode, resp.Reason)

		return
	}
	reqOpts = sip.NewSipRequestOpts{
		Port:   port,
		ToUser: toUser,
		User:   fromUser,
	}
	req, ok = sip.NewSipRequest(sipgo.INVITE, host, &reqOpts)
	if !ok {
		output.PrintfFrameworkError("Creating INVITE request to %s with options %+v", host, reqOpts)

		return
	}
	cred, _ = digest.Digest(chal, digest.Options{
		Method:   sipgo.INVITE.String(),
		URI:      host,
		Username: fromUser,
		Password: pass,
	})
	authHeader = sipgo.NewHeader("Authorization", cred.String())
	req.AppendHeader(authHeader)
	resp, ok = sip.SendAndReceiveUDP(conn, req)
	if !ok {
		output.PrintfFrameworkError("Sending INVITE request %s", req.String())

		return
	}
	// Not found is expected here, as we are not actually calling a real user.
	if resp.StatusCode == sipgo.StatusNotFound {
		output.PrintfFrameworkSuccess("Response (INVITE): %s", resp.String())
	} else {
		output.PrintfFrameworkError("Unexpected response (INVITE): %s", resp.String())
	}
}
