Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WithClientAddr overwrite WithClientHostname (need to change the from domain) #64

Closed
danielchristianschroeter opened this issue Dec 31, 2023 · 5 comments
Labels
enhancement New feature or request question Further information is requested

Comments

@danielchristianschroeter

My preferred external sip registrar Placetel need always in the FROM header @fpbx.de. I can change this by using sipgo.WithClientHostname("fpbx.de")

client, err := sipgo.NewClient(ua, sipgo.WithClientHostname("fpbx.de"), sipgo.WithClientNAT())

Dec 31 21:42:39.543219 DBG UDP write to [::]:5061 -> 185.79.24.137:5060:
REGISTER sip:xxx@fpbx.de:5060 SIP/2.0
Via: SIP/2.0/UDP :::5061;rport;alias;branch=z9hG4bK.3wJ7yK1Bw5TOLjvk
Contact: <sip:xxx@fpbx.de>
P-Preferred-Identity: <sip:xxx@fpbx.de>
From: "xxx" <sip:xxx@fpbx.de>;tag=CrK1AR2OweJrIn1R
To: <sip:xxx@fpbx.de>
Call-ID: d2249932-b169-4c81-916b-fd516b9736e7
CSeq: 1 REGISTER
Max-Forwards: 70
Content-Length: 0

But If I need also to change the source IP and Port with WithClientAddr, the WithClientHostname value is no longer used. This means now the client ip is correct but it will decline the request because of the incorrect source domain.

client, err := sipgo.NewClient(ua, sipgo.WithClientAddr("192.168.0.118:5061"), sipgo.WithClientHostname("fpbx.de"), sipgo.WithClientNAT())

Dec 31 21:44:29.274552 DBG UDP write to 192.168.0.118:5061 -> 185.79.24.137:5060:
REGISTER sip:xxx@fpbx.de:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.0.118:5061;alias;branch=z9hG4bK.KC0IV1B063fpY98A;rport
Contact: <sip:xxx@fpbx.de>
P-Preferred-Identity: <sip:xxx@fpbx.de>
From: "xxx" <sip:xxx@192.168.0.118>;tag=n2SxuHt2U3sVCCCK
To: <sip:xxx@fpbx.de>
Call-ID: 8fc2c304-726a-4fab-962f-007f48ff6b0f
CSeq: 1 REGISTER
Max-Forwards: 70
Content-Length: 0

Dec 31 21:44:29.302124 DBG UDP read from 192.168.0.118:5061 <- 185.79.24.137:5060:
SIP/2.0 404 Not our domain
Via: SIP/2.0/UDP 192.168.0.118:5061;alias;branch=z9hG4bK.KC0IV1B063fpY98A;rport=5061;received=xx.xx.xx.xx
From: "xxx" <sip:xxx@192.168.0.118>;tag=n2SxuHt2U3sVCCCK
To: <sip:xxx@fpbx.de>;tag=00cf985326c1d88126535b2db992fff3.b816c500
Call-ID: 8fc2c304-726a-4fab-962f-007f48ff6b0f
CSeq: 1 REGISTER
Content-Length: 0

Maybe you have some idea how to change both (source ip and from domain)?

@danielchristianschroeter
Copy link
Author

I found some workaround:

	// Setup UAC
	ua, err := sipgo.NewUA(
		sipgo.WithUserAgent(*username),
	)
	if err != nil {
		log.Fatal().Err(err).Msg("Fail to setup user agent")
	}

	client, err := sipgo.NewClient(ua, sipgo.WithClientAddr(*addr), sipgo.WithClientNAT())
	if err != nil {
		log.Fatal().Err(err).Msg("Fail to setup client handle")
	}
	defer client.Close()

	// Create basic REGISTER request structure
	recipient := &sip.Uri{}
	sip.ParseUri(fmt.Sprintf("sip:%s@%s", *username, *dst), recipient)
	req := sip.NewRequest(sip.REGISTER, recipient)
	from := sip.FromHeader{
		DisplayName: *username,
		Address: sip.Uri{
			User:      *username,
			Host:      "fpbx.de",
			UriParams: sip.NewParams(),
			Headers:   sip.NewParams(),
		},
		Params: sip.NewParams(),
	}
	from.Params.Add("tag", sip.GenerateTagN(16))
	req.AppendHeader(&from)
	req.AppendHeader(
		sip.NewHeader("Contact", fmt.Sprintf("<sip:%s@fpbx.de>", *username)),
	)
	req.AppendHeader(
		sip.NewHeader("P-Preferred-Identity", fmt.Sprintf("<sip:%s@fpbx.de>", *username)),
	)
	req.SetTransport(strings.ToUpper(*tran))

	// Send request and parse response
	req.SetDestination(*dst)
	log.Info().Msg(req.StartLine())
	ctx, _ := context.WithTimeout(context.Background(), 60*time.Second)
	tx, err := client.TransactionRequest(ctx, req)
	if err != nil {
		log.Fatal().Err(err).Msg("Fail to create transaction")
	}
	defer tx.Terminate()

	res, err := getResponse(tx)
	if err != nil {
		log.Fatal().Err(err).Msg("Fail to get response")
	}

	log.Info().Int("status", int(res.StatusCode)).Msg("Received status")
	if res.StatusCode == 401 {
		// Get WwW-Authenticate
		wwwAuth := res.GetHeader("WWW-Authenticate")
		chal, err := digest.ParseChallenge(wwwAuth.Value())
		if err != nil {
			log.Fatal().Str("wwwauth", wwwAuth.Value()).Err(err).Msg("Fail to parse challenge")
		}

		// Reply with digest
		cred, _ := digest.Digest(chal, digest.Options{
			Method:   req.Method.String(),
			URI:      recipient.Host,
			Username: *username,
			Password: *password,
		})

		newReq := sip.NewRequest(sip.REGISTER, recipient)
		from := sip.FromHeader{
			DisplayName: *username,
			Address: sip.Uri{
				User:      *username,
				Host:      "fpbx.de",
				UriParams: sip.NewParams(),
				Headers:   sip.NewParams(),
			},
			Params: sip.NewParams(),
		}
		from.Params.Add("tag", sip.GenerateTagN(16))
		newReq.AppendHeader(&from)
		newReq.AppendHeader(sip.NewHeader("Authorization", cred.String()))
		newReq.AppendHeader(
			sip.NewHeader("Contact", fmt.Sprintf("<sip:%s@fpbx.de>", *username)),
		)
		newReq.AppendHeader(
			sip.NewHeader("P-Preferred-Identity", fmt.Sprintf("<sip:%s@fpbx.de>", *username)),
		)
		newReq.SetTransport(strings.ToUpper(*tran))

		// Send request and parse response
		newReq.SetDestination(*dst)

		ctx, _ := context.WithTimeout(context.Background(), 60*time.Second)
		tx, err := client.TransactionRequest(ctx, newReq)
		if err != nil {
			log.Fatal().Err(err).Msg("Fail to create transaction")
		}
		defer tx.Terminate()

		res, err = getResponse(tx)
		if err != nil {
			log.Fatal().Err(err).Msg("Fail to get response")
		}
	}

	if res.StatusCode != 200 {
		log.Fatal().Msg("Fail to register")
	}

	log.Info().Msg("Client registered")

@emiago
Copy link
Owner

emiago commented Jan 1, 2024

@danielchristianschroeter I see your point. Currently those options are also used for routing or enforcing source IP, that should be in Via. Maybe here is probably confusion. WithClientAddr and WithClientHostname should not be used together.

Yes you can always add custom any header and it will be skipped during TransactionRequest.

Consider seperation of From and Source IP

@emiago emiago reopened this Jan 1, 2024
@danielchristianschroeter
Copy link
Author

The current implemented logic for getting the correct source IPs and listening interfaces is not working very well on my Windows 11 development environment. It will listen by default on IPv6 interface. Even if I try to disable IPv6. Also this internal IP is not reachable from outside. IPv6 is also not supported by the remote SIP registrar and the external IP is not detected.

I‘m now in the situation with IPv4 in a local Ubuntu VM to register and answer a call but no RTP media can be received.

I think I will not have these issues when I‘m outside of a NAT but maybe it makes more sense to get a more flexible and consistent configuration for the internal and external IPs / listing interfaces / used ports.

@emiago
Copy link
Owner

emiago commented Jan 2, 2024

@danielchristianschroeter thnx for feedback. Can you only be a bit specific.
THere is some things in TODO, like ListenAndServe where you will be able to pass udp4, tcp4, but it may also go as server option to force IPV4 or IPV6

This actually should be quick to fix. For client side I think it needs more work, but this is mostly because initially lib focus was on server.

@emiago emiago added enhancement New feature or request question Further information is requested labels Jan 2, 2024
@emiago emiago added this to the Stable API design milestone Jan 2, 2024
@emiago
Copy link
Owner

emiago commented Jan 13, 2024

@danielchristianschroeter added WithUserAgentHostname option for building UA.

In more customization needed, passing manually set headers as you here described is needed.
Still in most cases I see this needs to be set and then reused.

Check main branch.
Closing...

@emiago emiago closed this as completed Jan 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants