Skip to content

Commit

Permalink
[ADD] Added basic Network Address Translation.
Browse files Browse the repository at this point in the history
[ADD] Added receiver thread to VoIPCall for handling future requests.
[ADD] Added SIP State DB location global variable for debugging.
[ADD] Added AddressType enum to support NAT-based determine_tags functions.
[ADD] Added check_host function in NAT to check the AddressType of a host.
[ADD] Added filtering to func test if on Windows.
[ADD] Added helper variables to ensure func tests run correctly on linux.
[CHANGE] Cleaned up response checks in SIP client.
[CHANGE] Broke up VoIPCall init into multiple functions.
[CHANGE] Changed determine_tags to use NAT.
[CHANGE] Changed SIPMessage to always raise SIPParseError upon any error.
[CHANGE] Added pprint option to get_database_dump.
[FIX] Fixed port not showing when not 5060 in Contact header.
[FIX] Fixed invites not working for UDP.
[FIX] Fixed missing NSD update in VoIPPhone stop.
[FIX] Fixed messages not sending when they are a UDP response.
[FIX] Fixed UDP socket recv function not receiving messages missing tags.
[FIX] Fixed SQL connection issues.
  • Loading branch information
tayler6000 committed Oct 31, 2023
1 parent e26fa04 commit 0b3f1b3
Show file tree
Hide file tree
Showing 8 changed files with 300 additions and 153 deletions.
36 changes: 26 additions & 10 deletions pyVoIP/SIP/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@
debug = pyVoIP.debug


UNAUTORIZED_RESPONSE_CODES = [
SIPStatus.UNAUTHORIZED,
SIPStatus.PROXY_AUTHENTICATION_REQUIRED,
]
INVITE_OK_RESPONSE_CODES = [SIPStatus.TRYING, SIPStatus.RINGING, SIPStatus.OK]


class SIPClient:
def __init__(
self,
Expand Down Expand Up @@ -185,6 +192,7 @@ def start(self) -> None:
self.transport_mode,
self.bind_ip,
self.bind_port,
self.nat,
self.cert_file,
self.key_file,
self.key_password,
Expand All @@ -198,9 +206,11 @@ def start(self) -> None:
self.s.start()
# TODO: Check if we need to register with a server or proxy.
self.register()
"""
t = Timer(1, self.recv)
t.name = "SIP Receive"
t.start()
"""

def stop(self) -> None:
self.NSD = False
Expand Down Expand Up @@ -533,6 +543,7 @@ def gen_first_request(self, deregister=False) -> str:
method,
self.user,
self.nat.get_host(self.server),
port=self.bind_port,
uriparams=f";transport={trans_mode}",
params=[f'+sip.instance="<urn:uuid:{self.urnUUID}>"'],
)
Expand Down Expand Up @@ -568,6 +579,7 @@ def gen_subscribe(self, response: SIPMessage) -> str:
method,
self.user,
self.nat.get_host(self.server),
port=self.bind_port,
uriparams=f";transport={trans_mode}",
params=[f'+sip.instance="<urn:uuid:{self.urnUUID}>"'],
)
Expand Down Expand Up @@ -602,6 +614,7 @@ def gen_register(self, request: SIPMessage, deregister=False) -> str:
method,
self.user,
self.nat.get_host(self.server),
port=self.bind_port,
uriparams=f";transport={trans_mode}",
params=[f'+sip.instance="<urn:uuid:{self.urnUUID}>"'],
)
Expand Down Expand Up @@ -727,6 +740,7 @@ def gen_answer(
method,
self.user,
self.nat.get_host(self.server),
port=self.bind_port,
)
# TODO: Add Supported
regRequest += self.__gen_user_agent()
Expand Down Expand Up @@ -787,6 +801,7 @@ def gen_invite(
method,
self.user,
self.nat.get_host(self.server),
port=self.bind_port,
)
invRequest += self.__gen_from_to(
"To", number, self.server, port=self.port
Expand Down Expand Up @@ -831,6 +846,7 @@ def _gen_bye_cancel(self, request: SIPMessage, cmd: str) -> str:
method,
self.user,
self.nat.get_host(self.server),
port=self.bind_port,
)
byeRequest += self.__gen_user_agent()
byeRequest += f"Allow: {(', '.join(pyVoIP.SIPCompatibleMethods))}\r\n"
Expand Down Expand Up @@ -905,10 +921,8 @@ def invite(
response = SIPMessage(conn.recv(8192))

while (
response.status != SIPStatus(401)
and response.status != SIPStatus(407)
and response.status != SIPStatus(100)
and response.status != SIPStatus(180)
response.status
not in UNAUTORIZED_RESPONSE_CODES + INVITE_OK_RESPONSE_CODES
) or response.headers["Call-ID"] != call_id:
if not self.NSD:
break
Expand All @@ -918,14 +932,16 @@ def invite(

debug(f"Received Response: {response.summary()}")

if response.status == SIPStatus(100) or response.status == SIPStatus(
180
):
debug("Invite status OK")
return SIPMessage(invite.encode("utf8")), call_id, sess_id
if response.status in INVITE_OK_RESPONSE_CODES:
debug("Invite Accepted")
if response.status is SIPStatus.OK:
return response, call_id, sess_id, conn
return SIPMessage(invite.encode("utf8")), call_id, sess_id, conn
debug("Invite Requires Authorization")
ack = self.gen_ack(response)
conn.send(ack)
debug("Acknowledged")
conn.close() # End of Dialog
auth = self.gen_authorization(response)

invite = self.gen_invite(
Expand All @@ -935,7 +951,7 @@ def invite(
"\r\nContent-Length", f"\r\n{auth}Content-Length"
)

conn.send(invite)
conn = self.sendto(invite)

return SIPMessage(invite.encode("utf8")), call_id, sess_id, conn

Expand Down
7 changes: 6 additions & 1 deletion pyVoIP/SIP/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,12 @@ def __init__(self, data: bytes):
"v": "Via",
}

self.parse(data)
try:
self.parse(data)
except Exception as e:
if type(e) is not SIPParseError:
raise SIPParseError(e)
raise

def summary(self) -> str:
data = ""
Expand Down
Loading

0 comments on commit 0b3f1b3

Please sign in to comment.