Skip to content

SIPClient.invite() crashes with SIPParseError if an OPTIONS request arrives mid-transaction (master/1.6.x) #315

Description

@hainesdev

SIPClient.invite() crashes with SIPParseError if an OPTIONS request arrives mid-transaction (master/1.6.x)

Summary

pyVoIP.SIPCompatibleMethods (in pyVoIP/__init__.py) is ["INVITE", "ACK", "BYE", "CANCEL"] -- it doesn't include OPTIONS. SIPMessage.parse() raises SIPParseError for any request whose method isn't in that list.

SIPClient.recv() (the background receive loop) happens to survive this, since its except Exception in recv_loop/recv swallows the parse error. But SIPClient.invite()'s own blocking response-read loop does not:

with self.recvLock:
    self.out.sendto(invite.encode("utf8"), (self.server, self.port))
    response = SIPMessage(self.s.recv(8192))   # <-- no try/except here
    while (...):
        ...
        response = SIPMessage(self.s.recv(8192))  # <-- or here

If a server sends an unsolicited OPTIONS request (e.g. Asterisk's periodic qualify keepalive to a registered contact) at the exact moment invite() is blocked reading the INVITE transaction's response, that OPTIONS packet gets read by invite() instead of the background loop, SIPMessage() raises SIPParseError, and the exception propagates uncaught out of invite(), crashing the entire call attempt.

Reproduction

Register against any PBX that sends periodic OPTIONS to registered contacts (Asterisk with qualify_frequency set on the endpoint is the common case -- this is a default/common PJSIP endpoint setting), then place an outbound call. If the timing lines up, the call crashes instead of completing:

pyVoIP.SIP.SIPParseError: Unable to decipher SIP request: OPTIONS sip:101@x.x.x.x:5060 SIP/2.0

Confirmed live against a real Asterisk 22 / FreePBX 17 PBX, reproduced from a PBX-side pjsip set logger on trace showing the OPTIONS ping landing during the INVITE transaction window.

Related

#73 covers adding full OPTIONS support, and per the thread that's been done in development for 2.0. This issue is scoped more narrowly to master/1.6.x, which is still what's on PyPI today: it doesn't need full OPTIONS handling, just needs to not crash when one arrives. SIPClient.parse_message() already has a graceful fallback for unrecognized methods once parsing succeeds (else: debug("TODO: Add 400 Error on non processable request")), it's only SIPMessage.parse() itself that hard-fails first.

Proposed fix

Add "OPTIONS" to SIPCompatibleMethods so it parses via the generic message path instead of raising. I have this change ready as a small PR if useful.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions