def waitForCstaData(self, timeout=None): self.wait_lock.acquire() bkp = self.socket.gettimeout() if timeout: self.socket.settimeout(timeout) try: # header = "" # while not header: header = self.socket.recv(4) if not header: # disconnected socket return None datalength = int(''.join(["%02X" % x for x in header]), base=16) - 4 data = b'' while len(data) < datalength: data += self.socket.recv(datalength - len(data)) # print('message:\n{}\nsize{}\n'.format(data,datalength)) debug("Received on port {} message of length {}:\n\n".format( self.port, datalength) + (header + data ).decode("utf8", "backslashreplace").replace("\r\n", "\n")) finally: self.socket.settimeout(bkp) self.wait_lock.release() return header + data
def service_connection(self, key, mask): sock = key.fileobj data = key.data address = "{}:{}".format(*data.addr) if mask & selectors.EVENT_READ: try: link = self.get_address_link(address) inbytes = self.sip_endpoint.link.waitForSipData(timeout=5.0, client=link) if inbytes is None: self.sip_endpoint.link.socket.close() self.sel.unregister(self.sip_endpoint.link.socket) return inmessage = parseSip(inbytes) in_dialog = inmessage.get_dialog() if not in_dialog["to_tag"] and { "Call-ID": in_dialog["Call-ID"], "from_tag": in_dialog["from_tag"] } in self.sip_endpoint.dialogs: self.sip_endpoint.dialogs.append(in_dialog) self.links.append((in_dialog, link)) if inmessage.get_status_or_method() in self.handlers: self.events[inmessage.get_status_or_method()].set() self.buffers[inmessage.get_status_or_method()].append( inmessage) else: self.sip_endpoint.message_buffer.append(inmessage) except UnicodeDecodeError: debug("Ignoring malformed data")
def setup(A, B, secondaryNumbers, user_address_pool, call_taker_address_pool, registration_expiration=360): B.connect(next(call_taker_address_pool), (params2["psap_thig_ip"], params2["psap_thig_port"]), params2["transport"]) # Semi-Ugly way to handle OPTIONS messages from OSV when we don't shut down correctly :TODO B.link.register_for_event(("OPTIONS", {}, message["200_OK_1"])) try: register_primary(B, secondaryNumbers, expiration_in_seconds=registration_expiration) except: B.registered = False #debug(traceback.format_exc()) traceback.print_exc() A.connect(next(user_address_pool), (params1["orig_osv_sipsm_ip"], params1["orig_osv_sipsm_port"]), params1["transport"]) A.link.register_for_event(("OPTIONS", {}, message["200_OK_1"])) try: A.register(expiration_in_seconds=registration_expiration, re_register_time=registration_expiration / 2) except: A.registered = False debug(traceback.format_exc())
def consume_sip_data(self): try: #debug("reading for sip data") content_length = -1 data = b"" data += self.sockfile.readline() while True: line = self.sockfile.readline() data += line if not line.strip(): break header, value = [x.strip() for x in line.split(b":", 1)] if header == b"Content-Length": content_length = int(value) if content_length > 0: data += self.sockfile.read(content_length) if content_length == -1: #debug(data.decode()) #raise Exception TODO print("No content length in message") except ValueError: debug(data.decode()) debug(line.decode()) print_exc() #raise except socket.timeout: #debug('Data received before timeout: "{}"'.format(data.decode())) print_exc() #raise self.handle_sip_message(data)
def get_address_link(self, address): for known_dialog, link in self.links: if "{}:{}".format(link.rip, link.rport) == address: return link debug( "No link found for address {}.\nAvailable dialogs and corresponding links:\n{}" .format(address, self.links))
def flow(users, call_taker_pool, call_takers, secondary_numbers): A = next(users) B = next(call_taker_pool) line_appearance_index = next(secondary_numbers) C = B.secondary_lines[line_appearance_index] all_device_appearances = [ device.secondary_lines[line_appearance_index] for device in call_takers ] appearance_invite_dialogs = {} print("{} will dial {} and device {} will pickup".format( A.number, C.number, B.number)) A.send_new(C, message["Invite_SDP_1"]) invite = C.waitForMessage("INVITE") assert "sip:" + C.number + "@" in invite["To"] C.reply(message["Trying_1"]) C.reply(message["Ringing_1"]) invited_lines = [] for appearance in all_device_appearances: if appearance is not C: try: dialog = appearance.waitForMessage("INVITE", timeout=0.1).get_dialog() appearance.reply(message["Trying_1"]) appearance.reply(message["Ringing_1"]) invited_lines.append((appearance, dialog)) except TimeoutError: pass A.waitForMessage("180 Ringing", ignore_messages="100 Trying") C.reply(message["200_OK_SDP_1"]) A.waitForMessage("200 OK") A.send(message["Ack_1"]) C.waitForMessage("ACK") for appearance, dialog in invited_lines: try: appearance.set_dialog(dialog) appearance.waitForMessage("CANCEL") appearance.reply(message["200_OK_1"]) appearance.send(message["487_Request_terminated"], dialog=dialog) appearance.waitForMessage("ACK") except TimeoutError: # Is it OK if some lines that got invite don't get CANCEL? Maybe if they get busy meanwhile. pass sleep(params1["call_duration"]) A.send(message["Bye_1"], expected_response="200 OK") C.waitForMessage("BYE") C.reply(message["200_OK_1"]) debug("SUCCESSFUL CALL: {} dial {} device {} pickup".format( A.number, C.number, B.number))
def get_dialog_link(self, dialog): for known_dialog, link in self.links: if known_dialog and (known_dialog == dialog or self.sip_endpoint. get_complete_dialog(known_dialog) == dialog): return link debug( "No link found for dialog {}.\nAvailable dialogs and corresponding links:\n{}" .format(dialog, self.links))
def waitForData(self, timeout=None, buffer=4096): debug("Waiting on port {}".format(self.port)) bkp = self.socket.gettimeout() if timeout: self.socket.settimeout(timeout) try: data = self.socket.recv(buffer) finally: self.socket.settimeout(bkp) debug("Received on port {}:\n\n".format(self.port) + data.decode("utf8", "backslashreplace").replace("\r\n", "\n")) return data
def send(self, data, encoding="utf8"): # self.socket.sendall(binascii.hexlify(bytes(data,"utf8"))) if type(data) == type(b''): self.socket.sendall(data) debug( "Sent from port {}:\n\n".format(self.port) + data.decode("utf8", "backslashreplace").replace("\r\n", "\n")) else: self.socket.sendall(bytes(data, encoding)) debug("Sent from port {}:\n\n".format(self.port) + data.replace("\r\n", "\n"))
def waitForCstaData(self): header = self.socket.recv(4) datalength = int(''.join(["%02X" % x for x in header]), base=16) - 4 data = b'' while len(data) < datalength: data += self.socket.recv(datalength - len(data)) # print('message:\n{}\nsize{}\n'.format(data,datalength)) debug("Received on port {}:\n\n".format(self.port) + (header + data).decode("utf8", "backslashreplace").replace("\r\n", "\n")) return header + data
def snapshot_device(csta_server, snapshot_device_message): user = snapshot_device_message["snapshotObject"] csta_server.csta_endpoint.get_user(user).update_incoming_transactions( snapshot_device_message) if not user: debug( "Invalid snapshot device user '{}'. Empty snapshotObject tag.". format(user)) return csta_server.send("SnapshotDeviceResponse", to_user=user) csta_server.csta_endpoint.get_user(user).update_outgoing_transactions( snapshot_device_message)
def register_secondary(primary, line, expiration_in_seconds=360): line.parameters["expires"] = expiration_in_seconds line.parameters["primary"] = primary.number line.parameters["primary_port"] = primary.port retry_count = 10 for _ in range(retry_count): try: line.send_new(message_string=message["Register_secondary"], expected_response="200 OK") debug("Registered " + line.number + " on " + primary.number) break except AssertionError: sleep(2)
def send(self, data, encoding="utf8"): # try: # data = self.out_queue.get_nowait() # except Empty: # return if type(data) == type(b''): debug( "Sending from port {}:\n\n".format(self.port) + data.decode("utf8", "backslashreplace").replace("\r\n", "\n")) self.internal_client.sendall(data) else: debug("Sending from port {}:\n\n".format(self.port) + data.replace("\r\n", "\n")) self.internal_client.sendall(bytes(data, encoding))
def register_primary(sip_ep, secondary_numbers, expiration_in_seconds=360): sip_ep.re_register_timer = Timer( expiration_in_seconds / 2, register_primary, (sip_ep, secondary_numbers, expiration_in_seconds)) sip_ep.re_register_timer.start() sip_ep.parameters["expires"] = expiration_in_seconds sip_ep.parameters["primary"] = sip_ep.number sip_ep.parameters["primary_port"] = sip_ep.port if not sip_ep.parameters["epid"]: sip_ep.parameters["epid"] = "SC" + util.randStr(6) debug("Sent register from " + sip_ep.number) sip_ep.send_new(message_string=message["Register_primary"], expected_response="200 OK") debug("Got 200OK to register from " + sip_ep.number) if not sip_ep.secondary_lines: # first registration for n in secondary_numbers: line = SipEndpoint(n) line.use_link(sip_ep.link) sip_ep.secondary_lines.append(line) for line in sip_ep.secondary_lines: sleep(0.1) register_secondary(sip_ep, line) subscribe_primary(sip_ep) for line in sip_ep.secondary_lines: try: subscribe_secondary(line) line.registered = True except: debug(traceback.format_exc()) raise debug("Subscribed " + line.number + " on " + sip_ep.number) sip_ep.registered = True
def monitor_user(csta_server, monitor_message): user = monitor_message["deviceObject"] if not user: debug( "Invalid monitor user '{}'. Empty deviceID tag.".format(user)) return csta_server.csta_endpoint.new_user(user).parameters = { "monitorCrossRefID": csta_server.refid, "deviceID": user, "CSTA_CREATE_MONITOR_CROSS_REF_ID": csta_server.refid, "CSTA_USE_MONITOR_CROSS_REF_ID": csta_server.refid } csta_server.refid += 1 csta_server.csta_endpoint.get_user(user).update_incoming_transactions( monitor_message) csta_server.send("MonitorStartResponse", to_user=user) csta_server.csta_endpoint.get_user(user).update_outgoing_transactions( monitor_message)
def service_connection(self, key, mask): # sock = key.fileobj # data = key.data if mask & selectors.EVENT_READ: try: inbytes = self.csta_endpoint.link.waitForCstaData(timeout=5.0) if inbytes is None: self.csta_endpoint.link.socket.close() self.sel.unregister(self.csta_endpoint.link.socket) return inmessage = parseBytes(inbytes) if inmessage.event in self.handlers: self.events[inmessage.event].set() self.buffers[inmessage.event].append(inmessage) elif inmessage.event == "SystemStatusResponse": # TODO: Ignoring incoming SystemStatusResponse for now return else: self.csta_endpoint.message_buffer.append(inmessage) except UnicodeDecodeError: debug("Ignoring malformed data")
def serve_forever(self): self.socket.bind((self.ip, self.port)) if not self.protocol.upper() == "UDP": self.socket.listen() else: self.make_client(self.socket, (self.ip, self.port)) self.port = self.socket.getsockname()[1] print("listening on", (self.ip, self.port)) self.socket.setblocking(False) self.sel.register(self.socket, selectors.EVENT_READ, data=None) self.continue_serving = True while self.continue_serving: try: events = self.sel.select(timeout=60) for key, mask in events: if key.data is None and self.protocol.upper() != "UDP": self.accept_wrapper(key.fileobj) else: self.service_connection(key, mask) except socket.timeout: print("timeout") traceback.print_exc() continue except (IOError, socket.error): print("Disconnected. Waiting to reconnect") # self.sel.unregister(self.csta_endpoint.link.socket) continue except KeyboardInterrupt: self.shutdown() except: debug(traceback.format_exc()) self.remove_address_link(key.fileobj) self.sel.unregister(key.fileobj) self.sel.unregister(self.socket) # self.socket.shutdown(socket.SHUT_RDWR) self.socket.close() self.sel.close() self.shutdown()
def wait_for_call(B, c_index): C = B.secondary_lines[c_index] C.link.register_for_event(("BYE", {}, message["200_OK_1"])) debug(C.number + " waiting for calls from device " + B.number) while not B.shutdown: try: invite = C.waitForMessage("INVITE") except TimeoutError: continue try: C.set_dialog(invite.get_dialog()) C.reply(message["Trying_1"]) C.reply(message["Ringing_1"]) other_lines = [] assert "sip:" + C.number + "@" in invite["To"] C.reply(message["200_OK_SDP_1"]) for line in B.secondary_lines: if line is not C: try: line.waitForMessage("INVITE", timeout=0.1) line.reply(message["Trying_1"]) line.reply(message["Ringing_1"]) other_lines.append(line) except TimeoutError: pass C.waitForMessage("ACK") for line in other_lines: try: line.waitForMessage("CANCEL", timeout=2) line.reply(message["200_OK_1"]) line.send(message["487_Request_terminated"], dialog=invite.get_dialog()) line.waitForMessage("ACK") except TimeoutError: # Is it OK if some lines that got invite don't get CANCEL? Maybe if they get busy meanwhile. pass # C.waitForMessage("BYE") # C.reply(message["200_OK_1"]) except: debug("FAILED CALL on B side: device "+B.number) debug(traceback.format_exc()) continue debug(B.number + " exited")
def flow(users, secondary_numbers): A = next(users) dial_number = next(secondary_numbers) try: print("{} will dial {}".format(A.number, dial_number)) debug(A.number + "Sends invite to " + dial_number) A.send_new(dial_number, message["Invite_SDP_1"]) debug(A.number + "Waiting 200 OK to INVITE from " + dial_number) A.waitForMessage("200 OK", ignore_messages=["100 Trying", "180 Ringing"]) A.send(message["Ack_1"]) sleep(params1["call_duration"]) A.send(message["Bye_1"], expected_response="200 OK") debug("SUCCESSFUL CALL: {} to {} ".format(A.number, dial_number)) except KeyboardInterrupt: print("Stopping test") raise except: debug("FAILED CALL on A side: {} to {} ".format(A.number, dial_number)) debug(traceback.format_exc())
def wait_for_data(self, timeout=None): debug("Waiting on address {}:{}".format(self.ip, self.port)) try: data = self.in_queue.get(timeout=timeout) if data == "Stop": raise KeyboardInterrupt except Empty: debug("No message received after timeout") raise if isinstance(data, tuple): dbg = data[0] else: dbg = data debug("Received on address {}:{}:\n\n".format(self.ip, self.port) + dbg.decode("utf8").replace("\r\n", "\n")) return data
def waitForSipData(self, timeout=None, client=None): if not client: client = self client.wait_lock.acquire() debug("Waiting on port {}".format(client.port)) bkp = client.socket.gettimeout() data = b"" if timeout: client.socket.settimeout(timeout) try: data = wait_for_sip_data(client.sockfile) except ValueError: debug(data.decode()) # debug(line.decode()) raise except socket.timeout: debug('Data received before timeout: "{}"'.format(data.decode())) raise finally: client.socket.settimeout(bkp) debug("Received on port {}:\n\n".format(client.port) + data.decode("utf8").replace("\r\n", "\n")) client.wait_lock.release() return data
def waitForSipData(self, timeout=None): debug("Waiting on port {}".format(self.port)) bkp = self.socket.gettimeout() if timeout: self.socket.settimeout(timeout) try: content_length = -1 data = b"" data += self.sockfile.readline() while True: line = self.sockfile.readline() data += line if not line.strip(): break header, value = [x.strip() for x in line.split(b":", 1)] if header == b"Content-Length": content_length = int(value) if content_length > 0: data += self.sockfile.read(content_length) if content_length == -1: debug(data.decode()) raise Exception("No content length in message") except ValueError: debug(data.decode()) debug(line.decode()) raise except socket.timeout: debug('Data received before timeout: "{}"'.format(data.decode())) raise finally: self.socket.settimeout(bkp) debug("Received on port {}:\n\n".format(self.port) + data.decode("utf8").replace("\r\n", "\n")) return data
def wait_for_csta_message(self, for_user, message, ignore_messages=(), timeout=5.0): """ Wait for CSTA message :param for_user: The message intended recipient :param message: The message to wait for :param ignore_messages: Messages to ignore :param timeout: Defined timeout in seconds. :return: the CstaMessage received """ inmessage = None user = self.users[for_user] other_users = [usr for usr in self.users if usr != user] while not inmessage: if self.message_buffer: # first get a message from the buffer inmessage = self.get_buffered_message(for_user) sleep(0.1) if self.server: continue if not inmessage: # no (more) buffered messages. try the network try: # print("Waiting for", message) # inbytes = self.link.waitForCstaData(timeout=0.2) inbytes = self.link.waitForCstaData(timeout=timeout) inmessage = parseBytes(inbytes) # except sock_timeout: # inmessage = None # timeout -= 0.2 # if timeout <= 0: # raise # else: # continue except UnicodeDecodeError: debug("Ignoring malformed data") inmessage = None continue except sock_timeout: exception(for_user + " No" + message + " " + str(self.message_buffer)) inmessage_type = inmessage.event if inmessage_type in ignore_messages: inmessage = None continue if message and \ ((isinstance(message, str) and message != inmessage_type) or (type(message) in (list, tuple) and not any([m == inmessage_type for m in message])) or (inmessage["monitorCrossRefID"] and "monitorCrossRefID" in user.parameters and inmessage["monitorCrossRefID"] != user.parameters["monitorCrossRefID"] and inmessage_type != "MonitorStartResponse") or inmessage.is_response() and inmessage.eventid not in user.out_transactions): # we have received an unexpected message. if inmessage["deviceID"] and any(usr in inmessage["deviceID"] for usr in other_users): # TODO apply here the deviceID fix as well self.message_buffer.append(inmessage) inmessage = None else: raise AssertionError( '{}: Got "{}" with callID {} and xrefid {} while expecting "{}" with ' 'callID {} and xrefid {}.\n{} '.format( for_user, inmessage_type, inmessage["callID"], inmessage["monitorCrossRefID"], message, user.parameters.get("callID", None), user.parameters.get("monitorCrossRefID", None), inmessage)) # Evaluate the invoke id user.update_incoming_transactions(inmessage) return inmessage
def wait_for_call(B, c_index): C = B.secondary_lines[c_index] debug(C.number + " waiting for calls from device " + B.number) invite_dialogs = {} while not B.shutdown: try: # wait for any of the following messages inmessage = B.wait_for_message([ "INVITE", "ACK", "CANCEL", "BYE", "NOTIFY", "SUBSCRIBE", "200 OK" ]) inmessage_type = inmessage.get_status_or_method() to_number = inmessage["To"].split("@")[0].split(":")[1] line = [ a for a in [B] + B.secondary_lines if a.number == to_number ][0] line.set_dialog(inmessage.get_dialog()) line.set_transaction(inmessage.get_transaction()) line.save_message(inmessage) if inmessage_type == "INVITE": line.reply(message["Trying_1"]) line.reply(message["Ringing_1"]) if line is C: if not C.registered: warning("Line {}@{}:{} is not registered".format( C.number, C.ip, C.port)) line.reply(message["200_OK_SDP_1"]) print(C.number, "on", B.number, "picked up call from", inmessage["From"].split("<")[0]) else: invite_dialogs[line.number] = inmessage.get_dialog() elif inmessage_type in ("ACK", "200 OK"): # Should we make sure all expected ACK have been received? continue elif inmessage_type == "CANCEL": line.reply(message["200_OK_1"]) line.send(message["487_Request_Terminated"], dialog=invite_dialogs[line.number]) invite_dialogs.pop(line.number) elif inmessage_type in ("BYE", "NOTIFY"): line.reply(message["200_OK_1"]) elif inmessage_type == "SUBSCRIBE": notify_after_subscribe(line, inmessage.get_dialog()) # line.reply(message["200_OK_1"]) # line.send(message_string=message["Notify_terminated_1"]) else: raise Exception("Unexpected message received: " + inmessage_type) except TimeoutError: continue except: debug("ERROR on B side: device " + B.number) debug(traceback.format_exc()) continue debug(B.number + " exited")