tayler6000 / pyVoIP

Pure python VoIP/SIP/RTP library. Currently supports PCMA, PCMU, and telephone-event
https://pypi.org/project/pyVoIP/
GNU General Public License v3.0
226 stars 98 forks source link

Setup code will not run for me. Issue with a key when hashing Auth request. #56

Closed Cabarnacus closed 1 year ago

Cabarnacus commented 2 years ago

Not sure where to go from here. Tried to set up as a client of 3CX PBX (Self Hosted). pyVoIP installed from pip. Traceback is from the example quick start setup code. (KeyError: 'realm')?

The instance of VoIPPhone is set up as follows:

phone=VoIPPhone("10.0.50.100", 5060, "4004", "plOP1QNVuV", callCallback=answer, myIP="192.168.200.5")

All other code is copy and paste from readme file.

TODO: Add 500 Error on Receiving SIP Response
Traceback (most recent call last):
  File "/home/grant/python/voip/main.py", line 12, in <module>
    phone.start()
  File "/home/grant/python/voip/venv/lib/python3.10/site-packages/pyVoIP/VoIP.py", line 423, in start
    self.sip.start()
  File "/home/grant/python/voip/venv/lib/python3.10/site-packages/pyVoIP/SIP.py", line 579, in start
    register = self.register()
  File "/home/grant/python/voip/venv/lib/python3.10/site-packages/pyVoIP/SIP.py", line 906, in register
    regRequest = self.genRegister(response)
  File "/home/grant/python/voip/venv/lib/python3.10/site-packages/pyVoIP/SIP.py", line 631, in genRegister
    response = self.genAuthorization(request)
  File "/home/grant/python/voip/venv/lib/python3.10/site-packages/pyVoIP/SIP.py", line 623, in genAuthorization
    HA1 = hashlib.md5(self.username.encode('utf8')+b':'+request.authentication['realm'].encode('utf8')+b':'+self.password.encode('utf8')).hexdigest().encode('utf8')
KeyError: 'realm'
astrotrupti commented 2 years ago

@Cabarnacus Were you able to get resolve the issue? I am currently stuck in the same spot @tayler6000 Any chance we could get your input?

Cabarnacus commented 2 years ago

@astrotrupti I'm afraid not. I shelved my VoIP project for just now. I will probably look for an alternative package to this.

astrotrupti commented 2 years ago

@Cabarnacus Thanks! Could you please post here if you figure out how to solve it or if you find another package

Cabarnacus commented 2 years ago

@astrotrupti 👍 will do!

tayler6000 commented 2 years ago

Hard to know for sure without a packet capture, but it looks like the PBX may not be following the authentication RFC properly. If I can get a packet capture I can make a patch for this case.

Input-BDF commented 2 years ago

I somehow remember there was an issue with this in current master mostly caused by the fake request to non Asterisk. (I assume you are using Master) Think this was fixed with #10 in v1.6.0-dev branch. You could give it a try

Cabarnacus commented 2 years ago

I somehow remember there was an issue with this in current master mostly caused by the fake request to non Asterisk. (I assume you are using Master) Think this was fixed with #10 in v1.6.0-dev branch. You could give it a try

I will try another release this weekend if I get a moment. Thanks. 👍

astrotrupti commented 2 years ago

@tayler6000 Here's a screenshot of the patch. It says it binds but I get the same exact error as @Cabarnacus. new

astrotrupti commented 2 years ago

@Input-BDF I tried using v1.6.0-dev. My line doesn't have a password so I enter '' in the password section. I get invalid username and password and it throws "400 Malformed Form, fix your code"

tayler6000 commented 2 years ago

@astrotrupti This may actually be due to the fact that you don't have a password. I'll look into it.

Input-BDF commented 2 years ago

@astrotrupti This may actually be due to the fact that you don't have a password. I'll look into it.

Could also be something with 3CX PBX, with which I personaly have no experience. @astrotrupti if you have a working line without password you could sniff up these packages and compare with pyVoips register requests.

Got my local vanilla Kamailio back working and tested latest 1.6.0-dev with the basic example and passwd='' Worked but (at least in my case) I had to specify myIP. Without I get 403: Not relaying.

@tayler6000 The original problem with the key error came somehow from the fake request in register() before sending user auth which was replaced with genFirstRequest/gen_first_response in 1.6.0 . From there the SIP-server replied without a realm in the response under some conditions.

astrotrupti commented 2 years ago

@Input-BDF Unfortunately I get the same error - Invalid username or Password for SIP server when I do what you did with 1.6.0-dev. Thanks for checking though

astrotrupti commented 2 years ago

@tayler6000 @Input-BDF @Cabarnacus I got it to register successfully and it also says Busy when the callback is set to None. To do that I commented out both "realm" and "nonce" wherever they appeared. However, now, probably but I am not sure due to lack of knowledge, because I got rid of "realm" and "nonce" the algorithm doesn't enter def answer() function from the example or the call goes to unknown and never gets answered. Do you all have any suggestions here?

tayler6000 commented 2 years ago

@astrotrupti do have a password for your phone setup?

tayler6000 commented 2 years ago

@Cabarnacus Can you confirm if this is still an issue in v1.6.2?

cfpena commented 2 years ago

I'm starting with this package (v1.6.2), trying to make a call with Yeaster tg100. But I can't register, I always have - Invalid username or Password for SIP server. I tested the device's config with zoiper client and everything works well.

tayler6000 commented 2 years ago

@cfpena Can you please install pyVoIP from master and then add

import pyVoIP
pyVoIP.DEBUG = True

to the beginning of your code? Then reply with the debug output minus the packet that says "(DO NOT SHARE THIS PACKET)"? This will help us to debug the regex to fix the issue for your server.

cfpena commented 2 years ago

@tayler6000

==================================================
Unauthorized, SIP Message Log:

SENT
REGISTER sip:192.168.10.210 SIP/2.0
Via: SIP/2.0/UDP 192.168.10.185:5060;branch=z9hG4bK4ccbf433c29e45b4b26853666;rport
From: "20001" <sip:20001@192.168.10.210>;tag=9d84de77
To: "20001" <sip:20001@192.168.10.210>
Call-ID: 6b86b273ff34fce19d6b804eff5a3f57@192.168.10.185:5060
CSeq: 1 REGISTER
Contact: <sip:20001@192.168.10.185:5060;transport=UDP>;+sip.instance="<urn:uuid:7AB37954-1EC7-4C0D-AB4C-B0DD6C0876F4>"
Allow: INVITE, ACK, BYE, CANCEL
Max-Forwards: 70
Allow-Events: org.3gpp.nwinitdereg
User-Agent: pyVoIP 1.6.2
Expires: 120
Content-Length: 0

RECEIVED
Status: 401 Unauthorized

Headers:
Via: [{'type': 'SIP/2.0/UDP', 'address': ('192.168.10.185', '5060'), 'branch': 'z9hG4bK4ccbf433c29e45b4b26853666', 'received': '192.168.10.185', 'rport': '5060'}]
From: {'raw': '"20001" <sip:20001@192.168.10.210>', 'tag': '9d84de77', 'address': '20001@192.168.10.210', 'number': '20001', 'caller': '20001" ', 'host': '192.168.10.210'}
To: {'raw': '"20001" <sip:20001@192.168.10.210>', 'tag': 'as226f9890', 'address': '20001@192.168.10.210', 'number': '20001', 'caller': '20001" ', 'host': '192.168.10.210'}
Call-ID: 6b86b273ff34fce19d6b804eff5a3f57@192.168.10.185:5060
CSeq: {'check': '1', 'method': 'REGISTER'}
Server: TG100
Allow: ['INVITE', 'ACK', 'CANCEL', 'OPTIONS', 'BYE', 'REFER', 'SUBSCRIBE', 'NOTIFY', 'INFO']
Supported: ['replaces', 'timer']
WWW-Authenticate: {'algorithm': 'MD5', 'realm': 'asterisk', 'nonce': '03bbeb46'}
Content-Length: 0

Body:

Raw:
b'SIP/2.0 401 Unauthorized\r\nVia: SIP/2.0/UDP 192.168.10.185:5060;branch=z9hG4bK4ccbf433c29e45b4b26853666;received=192.168.10.185;rport=5060\r\nFrom: "20001" <sip:20001@192.168.10.210>;tag=9d84de77\r\nTo: "20001" <sip:20001@192.168.10.210>;tag=as226f9890\r\nCall-ID: 6b86b273ff34fce19d6b804eff5a3f57@192.168.10.185:5060\r\nCSeq: 1 REGISTER\r\nServer: TG100\r\nAllow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO\r\nSupported: replaces, timer\r\nWWW-Authenticate: Digest algorithm=MD5, realm="asterisk", nonce="03bbeb46"\r\nContent-Length: 0\r\n\r\n'

SENT (DO NOT SHARE THIS PACKET)
*********************

RECEIVED
Status: 401 Unauthorized

Headers:
Via: [{'type': 'SIP/2.0/UDP', 'address': ('192.168.10.185', '5060'), 'branch': 'z9hG4bKeddd669af1c44200aded2be25', 'received': '192.168.10.185', 'rport': '5060'}]
From: {'raw': '"20001" <sip:20001@192.168.10.210>', 'tag': '9d84de77', 'address': '20001@192.168.10.210', 'number': '20001', 'caller': '20001" ', 'host': '192.168.10.210'}
To: {'raw': '"20001" <sip:20001@192.168.10.210>', 'tag': 'as00b6278f', 'address': '20001@192.168.10.210', 'number': '20001', 'caller': '20001" ', 'host': '192.168.10.210'}
Call-ID: d4735e3a265e16eee03f59718b9b5d03@192.168.10.185:5060
CSeq: {'check': '2', 'method': 'REGISTER'}
Server: TG100
Allow: ['INVITE', 'ACK', 'CANCEL', 'OPTIONS', 'BYE', 'REFER', 'SUBSCRIBE', 'NOTIFY', 'INFO']
Supported: ['replaces', 'timer']
WWW-Authenticate: {'algorithm': 'MD5', 'realm': 'asterisk', 'nonce': '4256fa21'}
Content-Length: 0

Body:

Raw:
b'SIP/2.0 401 Unauthorized\r\nVia: SIP/2.0/UDP 192.168.10.185:5060;branch=z9hG4bKeddd669af1c44200aded2be25;received=192.168.10.185;rport=5060\r\nFrom: "20001" <sip:20001@192.168.10.210>;tag=9d84de77\r\nTo: "20001" <sip:20001@192.168.10.210>;tag=as00b6278f\r\nCall-ID: d4735e3a265e16eee03f59718b9b5d03@192.168.10.185:5060\r\nCSeq: 2 REGISTER\r\nServer: TG100\r\nAllow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO\r\nSupported: replaces, timer\r\nWWW-Authenticate: Digest algorithm=MD5, realm="asterisk", nonce="4256fa21"\r\nContent-Length: 0\r\n\r\n'
==================================================
Traceback (most recent call last):
  File "/Users/cfpena/projects/test/pyVoIP/main.py", line 84, in <module>
    phone.start()
  File "/Users/cfpena/projects/test/pyVoIP/pyVoIP/VoIP.py", line 656, in start
    self.sip.start()
  File "/Users/cfpena/projects/test/pyVoIP/pyVoIP/SIP.py", line 949, in start
    self.register()
  File "/Users/cfpena/projects/test/pyVoIP/pyVoIP/SIP.py", line 1743, in register
    raise InvalidAccountInfoError(
pyVoIP.SIP.InvalidAccountInfoError: Invalid Username or Password for SIP server 192.168.10.210:5060

I really appreciate your help. Thanks in advance

tayler6000 commented 2 years ago

@tayler6000

==================================================
Unauthorized, SIP Message Log:

SENT
REGISTER sip:192.168.10.210 SIP/2.0
Via: SIP/2.0/UDP 192.168.10.185:5060;branch=z9hG4bK4ccbf433c29e45b4b26853666;rport
From: "20001" <sip:20001@192.168.10.210>;tag=9d84de77
To: "20001" <sip:20001@192.168.10.210>
Call-ID: 6b86b273ff34fce19d6b804eff5a3f57@192.168.10.185:5060
CSeq: 1 REGISTER
Contact: <sip:20001@192.168.10.185:5060;transport=UDP>;+sip.instance="<urn:uuid:7AB37954-1EC7-4C0D-AB4C-B0DD6C0876F4>"
Allow: INVITE, ACK, BYE, CANCEL
Max-Forwards: 70
Allow-Events: org.3gpp.nwinitdereg
User-Agent: pyVoIP 1.6.2
Expires: 120
Content-Length: 0

RECEIVED
Status: 401 Unauthorized

Headers:
Via: [{'type': 'SIP/2.0/UDP', 'address': ('192.168.10.185', '5060'), 'branch': 'z9hG4bK4ccbf433c29e45b4b26853666', 'received': '192.168.10.185', 'rport': '5060'}]
From: {'raw': '"20001" <sip:20001@192.168.10.210>', 'tag': '9d84de77', 'address': '20001@192.168.10.210', 'number': '20001', 'caller': '20001" ', 'host': '192.168.10.210'}
To: {'raw': '"20001" <sip:20001@192.168.10.210>', 'tag': 'as226f9890', 'address': '20001@192.168.10.210', 'number': '20001', 'caller': '20001" ', 'host': '192.168.10.210'}
Call-ID: 6b86b273ff34fce19d6b804eff5a3f57@192.168.10.185:5060
CSeq: {'check': '1', 'method': 'REGISTER'}
Server: TG100
Allow: ['INVITE', 'ACK', 'CANCEL', 'OPTIONS', 'BYE', 'REFER', 'SUBSCRIBE', 'NOTIFY', 'INFO']
Supported: ['replaces', 'timer']
WWW-Authenticate: {'algorithm': 'MD5', 'realm': 'asterisk', 'nonce': '03bbeb46'}
Content-Length: 0

Body:

Raw:
b'SIP/2.0 401 Unauthorized\r\nVia: SIP/2.0/UDP 192.168.10.185:5060;branch=z9hG4bK4ccbf433c29e45b4b26853666;received=192.168.10.185;rport=5060\r\nFrom: "20001" <sip:20001@192.168.10.210>;tag=9d84de77\r\nTo: "20001" <sip:20001@192.168.10.210>;tag=as226f9890\r\nCall-ID: 6b86b273ff34fce19d6b804eff5a3f57@192.168.10.185:5060\r\nCSeq: 1 REGISTER\r\nServer: TG100\r\nAllow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO\r\nSupported: replaces, timer\r\nWWW-Authenticate: Digest algorithm=MD5, realm="asterisk", nonce="03bbeb46"\r\nContent-Length: 0\r\n\r\n'

SENT (DO NOT SHARE THIS PACKET)
*********************

RECEIVED
Status: 401 Unauthorized

Headers:
Via: [{'type': 'SIP/2.0/UDP', 'address': ('192.168.10.185', '5060'), 'branch': 'z9hG4bKeddd669af1c44200aded2be25', 'received': '192.168.10.185', 'rport': '5060'}]
From: {'raw': '"20001" <sip:20001@192.168.10.210>', 'tag': '9d84de77', 'address': '20001@192.168.10.210', 'number': '20001', 'caller': '20001" ', 'host': '192.168.10.210'}
To: {'raw': '"20001" <sip:20001@192.168.10.210>', 'tag': 'as00b6278f', 'address': '20001@192.168.10.210', 'number': '20001', 'caller': '20001" ', 'host': '192.168.10.210'}
Call-ID: d4735e3a265e16eee03f59718b9b5d03@192.168.10.185:5060
CSeq: {'check': '2', 'method': 'REGISTER'}
Server: TG100
Allow: ['INVITE', 'ACK', 'CANCEL', 'OPTIONS', 'BYE', 'REFER', 'SUBSCRIBE', 'NOTIFY', 'INFO']
Supported: ['replaces', 'timer']
WWW-Authenticate: {'algorithm': 'MD5', 'realm': 'asterisk', 'nonce': '4256fa21'}
Content-Length: 0

Body:

Raw:
b'SIP/2.0 401 Unauthorized\r\nVia: SIP/2.0/UDP 192.168.10.185:5060;branch=z9hG4bKeddd669af1c44200aded2be25;received=192.168.10.185;rport=5060\r\nFrom: "20001" <sip:20001@192.168.10.210>;tag=9d84de77\r\nTo: "20001" <sip:20001@192.168.10.210>;tag=as00b6278f\r\nCall-ID: d4735e3a265e16eee03f59718b9b5d03@192.168.10.185:5060\r\nCSeq: 2 REGISTER\r\nServer: TG100\r\nAllow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO\r\nSupported: replaces, timer\r\nWWW-Authenticate: Digest algorithm=MD5, realm="asterisk", nonce="4256fa21"\r\nContent-Length: 0\r\n\r\n'
==================================================
Traceback (most recent call last):
  File "/Users/cfpena/projects/test/pyVoIP/main.py", line 84, in <module>
    phone.start()
  File "/Users/cfpena/projects/test/pyVoIP/pyVoIP/VoIP.py", line 656, in start
    self.sip.start()
  File "/Users/cfpena/projects/test/pyVoIP/pyVoIP/SIP.py", line 949, in start
    self.register()
  File "/Users/cfpena/projects/test/pyVoIP/pyVoIP/SIP.py", line 1743, in register
    raise InvalidAccountInfoError(
pyVoIP.SIP.InvalidAccountInfoError: Invalid Username or Password for SIP server 192.168.10.210:5060

I really appreciate your help. Thanks in advance

To be honest, I see nothing wrong with this... Does anyone else see an issue here?

cfpena commented 2 years ago

@tayler6000

I was trying to check the problem with debugging and wireshark but couldn't find anything. But when I changed to pip version 1.5.6, everything worked fine.

For version 1.5.6 to work I had to disable the 'qualify' option. The reason is that the "option" method is not allowed. But in the current master branch and pip version 1.6 I couldn't find the problem.

tayler6000 commented 2 years ago

@tayler6000

I was trying to check the problem with debugging and wireshark but couldn't find anything. But when I changed to pip version 1.5.6, everything worked fine.

For version 1.5.6 to work I had to disable the 'qualify' option. The reason is that the "option" method is not allowed. But in the current master branch and pip version 1.6 I couldn't find the problem.

This is very strange. If you're comfortable, could you send another debug log like you did before and the password you use for the sip account? My email is taylerporter@gmail.com this is really the only thing I can think of to debug this further.

cfpena commented 2 years ago

@tayler6000 I was trying to check the problem with debugging and wireshark but couldn't find anything. But when I changed to pip version 1.5.6, everything worked fine. For version 1.5.6 to work I had to disable the 'qualify' option. The reason is that the "option" method is not allowed. But in the current master branch and pip version 1.6 I couldn't find the problem.

This is very strange. If you're comfortable, could you send another debug log like you did before and the password you use for the sip account? My email is taylerporter@gmail.com this is really the only thing I can think of to debug this further.

I've already made a separate script to compare the MD5 hash response and it's exactly the same. I will send you an email tomorrow with the requested information and some more data.

Cabarnacus commented 2 years ago

@cfpena Can you please install pyVoIP from master and then add

import pyVoIP
pyVoIP.DEBUG = True

to the beginning of your code? Then reply with the debug output minus the packet that says "(DO NOT SHARE THIS PACKET)"? This will help us to debug the regex to fix the issue for your server.

As requested see below. There was no section titled "(DO NOT SHARE THIS PACKET)"?

I have tested this configuration out with a softphone (Grandstream wave) and it works fine. One thing to note is that GS Wave gives the options of the "SIP User ID" which is typically the extension number, and a "SIP Authentication ID" which is a randomly generated username (3CX endorse this for security reasons). Both have to be provided for registration to work.

For the purposes of this test I have configured the Authentication ID to the same as the User ID (server side). I have done this as the class VoIPPhone takes the argument "username". I am unsure if this is meant to be the extension (user id) or the authentication ID.

I have not used Asterisk based SIP servers before but i'm sure I read somewhere that there no distinction between a User ID and and Authentication ID. Maybe since the intended use of pyVoIP is to be paired with Asterisk then it is not compatible with 3CX at this time.

Proxy auth required
Status: 407 Proxy Authentication Required

Headers:
Via: [{'type': 'SIP/2.0/UDP', 'address': ('192.168.1.115', '5060'), 'branch': 'z9hG4bKa204b49c872e4536ad20ab0fe', 'rport': '5060'}]
Proxy-Authenticate: Digest nonce="414d53596337438754:e4245f22523b9adb224fe90d7c52e239",algorithm=MD5,realm="3CXPhoneSystem"
To: {'raw': '"4004"<sip:4004@10.0.50.100>', 'tag': 'c61b1d0b', 'address': '4004@10.0.50.100', 'number': '4004', 'caller': '4004', 'host': '10.0.50.100'}
From: {'raw': '"4004" <sip:4004@10.0.50.100>', 'tag': 'a07c805b', 'address': '4004@10.0.50.100', 'number': '4004', 'caller': '4004" ', 'host': '10.0.50.100'}
Call-ID: 6b86b273ff34fce19d6b804eff5a3f57@192.168.1.115:5060
CSeq: {'check': '1', 'method': 'REGISTER'}
Content-Length: 0

Body:

Raw:
b'SIP/2.0 407 Proxy Authentication Required\r\nVia: SIP/2.0/UDP 192.168.1.115:5060;branch=z9hG4bKa204b49c872e4536ad20ab0fe;rport=5060\r\nProxy-Authenticate: Digest nonce="414d53596337438754:e4245f22523b9adb224fe90d7c52e239",algorithm=MD5,realm="3CXPhoneSystem"\r\nTo: "4004"<sip:4004@10.0.50.100>;tag=c61b1d0b\r\nFrom: "4004" <sip:4004@10.0.50.100>;tag=a07c805b\r\nCall-ID: 6b86b273ff34fce19d6b804eff5a3f57@192.168.1.115:5060\r\nCSeq: 1 REGISTER\r\nContent-Length: 0\r\n\r\n'
b'SIP/2.0 407 Proxy Authentication Required\r\nVia: SIP/2.0/UDP 192.168.1.115:5060;branch=z9hG4bKa204b49c872e4536ad20ab0fe;rport=5060\r\nProxy-Authenticate: Digest nonce="414d53596337438754:e4245f22523b9adb224fe90d7c52e239",algorithm=MD5,realm="3CXPhoneSystem"\r\nTo: "4004"<sip:4004@10.0.50.100>;tag=c61b1d0b\r\nFrom: "4004" <sip:4004@10.0.50.100>;tag=a07c805b\r\nCall-ID: 6b86b273ff34fce19d6b804eff5a3f57@192.168.1.115:5060\r\nCSeq: 1 REGISTER\r\nContent-Length: 0\r\n\r\n'
Traceback (most recent call last):
  File "/home/grant/python/voip/main.py", line 17, in <module>
    phone.start()
  File "/home/grant/python/voip/venv/lib/python3.10/site-packages/pyVoIP/VoIP.py", line 656, in start
    self.sip.start()
  File "/home/grant/python/voip/venv/lib/python3.10/site-packages/pyVoIP/SIP.py", line 949, in start
    self.register()
  File "/home/grant/python/voip/venv/lib/python3.10/site-packages/pyVoIP/SIP.py", line 1794, in register
    raise InvalidAccountInfoError(
pyVoIP.SIP.InvalidAccountInfoError: Invalid Username or Password for SIP server 10.0.50.100:5060
tayler6000 commented 2 years ago

@Cabarnacus You would have needed to install from source to get that extra level of debug, however this explains the issue. PyVoIP does not currently support proxies. Reference #69