Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Vitor Hugo committed Jan 4, 2019
0 parents commit bff7af4
Show file tree
Hide file tree
Showing 10 changed files with 389 additions and 0 deletions.
144 changes: 144 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@

# Created by https://www.gitignore.io/api/python,virtualenv,visualstudiocode
# Edit at https://www.gitignore.io/?templates=python,virtualenv,visualstudiocode

### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

### Python Patch ###
.venv/

### VirtualEnv ###
# Virtualenv
# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/
[Bb]in
[Ii]nclude
[Ll]ib
[Ll]ib64
[Ll]ocal
[Ss]cripts
pyvenv.cfg
pip-selfcheck.json

### VisualStudioCode ###
.vscode/

### VisualStudioCode Patch ###
# Ignore all local history of files
.history

# End of https://www.gitignore.io/api/python,virtualenv,visualstudiocode
2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
include README.md
include pysip/templates/*
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# PySIP

CLI para realização de testes na area de VoIP.
4 changes: 4 additions & 0 deletions pysip/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from pysip.cli import cli


__all__ = ['cli']
93 changes: 93 additions & 0 deletions pysip/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import click
import socket
import logging
from time import time
from random import random
from pprint import pprint

from pysip.log import logger
from pysip.messages import Message, parse_header


@click.group()
def cli():
'''
Função que representa a CLI em si.
'''
...


@cli.group()
@click.option('--debug', type=bool, help='Enable debug mode.')
def server(debug):
'''
Command that starts the application in SERVER mode.
'''
logger.debug('DEBUG mode enabled.')
logger.setLevel(logging.DEBUG if debug else logging.INFO)
logger.info('Application started in SERVER mode.')

@cli.group()
@click.option('--debug', type=bool, help='Enable debug mode.')
def client(debug):
'''
Command that starts the application in CLIENT mode.
'''
logger.setLevel(logging.DEBUG if debug else logging.INFO)
logger.debug('DEBUG mode enabled.')
logger.info('Application started in CLIENT mode.')

@client.command('alg', help='Performs the ALG test with the host informed.')
@click.option('--host', type=str, required=True, help='Host of the test.')
@click.option('--username', type=str, required=True, help='Username of the test.')
@click.option('--domain', type=str, required=True, help='Domain of the test.')
@click.option('--port', type=int, default=5060, help='Port of the test.')
def aug(host, port, username, domain):
logger.info('Performing ALG test.')
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as interface:
interface.settimeout(5)
destiny_address = (host, port)
logger.info(f'Destiny IP: {host}')
logger.info(f'Destiny Port: {port}')
interface.connect(destiny_address)
in_host, in_port = interface.getsockname()
logger.info(f'Internal IP used: {in_host}')
logger.info(f'Internal selected port: {in_port}')
callid = Message.make_hash(str(time() * random()))
to_tag = Message.make_hash(str(time() * random()))
branch = Message.make_hash(str(time() * random()))
logger.debug(f'Call-ID generated for the package: {callid}')
logger.debug(f'To-Tag generated for the package: {to_tag}')
logger.debug(f'Branch-Tag generated for the package: {branch}')
logger.debug('Generating INVITE that will be forwarded to the Host.')
message = Message('invite', **{
'address': {'ip': in_host, 'port': in_port },
'branch': branch,
'from': {
'user': username,
'domain': domain,
'tag': to_tag
},
'callid': callid
})
try:
interface.send(message.render)
response = interface.recv(4096).decode('ASCII')
resp_list = [v for v in response.split('\r\n') if v]
result = dict(parse_header(value) for value in resp_list)
logger.info(f'Response title: {result["title"]}')
logger.debug(f'Call-ID response: {result["Call-ID"]}')
logger.debug(f'Response source server: {result["Server"]}')
_, title = result["title"].split('200')

if title.strip() == 'OK':
logger.info('ALG test completed successfully.')

else:
logger.warn('Router with ALG detected.')

except KeyError:
logger.error('Could not parse the response.')

except socket.timeout:
logger.error('Connection timeout with SipProxy.')
15 changes: 15 additions & 0 deletions pysip/log.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import logging


logger = logging.getLogger(__name__)

formatter = logging.Formatter(
'[%(asctime)s] - %(levelname)s - %(message)s',
datefmt='%d/%m/%Y %H:%M:%S'
)

ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
ch.setFormatter(formatter)

logger.addHandler(ch)
42 changes: 42 additions & 0 deletions pysip/messages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from os import path
from time import time
from hashlib import md5

from jinja2 import Environment, FileSystemLoader


here = path.abspath(path.dirname(__file__))

class Message:
def __init__(self, name, **kwargs):
self.loader = FileSystemLoader(path.join(here, 'templates'))
self.env = Environment(loader=self.loader)
self.name = name
self.params = kwargs

@staticmethod
def make_hash(text):
hasher = md5()
hasher.update(text.encode('utf-8'))
return hasher.hexdigest()

@property
def render(self):
if path.exists(path.join(here, 'templates', self.name + '.txt')):
file = self.name + '.txt'
template = self.env.get_template(file)
message = template.render(self.params)
return message.encode().replace(b'\n', b'\r\n')

else:
raise FileNotFoundError('Message not Found')

@render.setter
def render(self):
raise AttributeError('This message is not editable.')

def parse_header(text):
length = len(text.split(':')) - 1
result = text.split(':', 1) if length else ['title', text]
chave, valor = result
return (chave.strip(), valor.strip())
30 changes: 30 additions & 0 deletions pysip/templates/invite.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
INVITE sip:{{ from['user'] }}@{{ address['ip'] }}:{{ address['port'] }} SIP/2.0
Via: SIP/2.0/UDP {{ address['ip'] }}:{{ address['port'] }};rport;branch={{ branch }}
Max-Forwards: 100
From: <sip:{{ from['user'] }}@{{ address['ip'] }}>;tag={{ from['tag'] }}
To: <sip:{{ from['user'] }}@{{ from['domain'] }}>
Call-ID: {{ callid }}
Alg-test: 0
CSeq: 1 INVITE
Contact: <sip:{{ from['user'] }}@{{ address['ip'] }}:{{ address['port'] }}>
User-Agent: PySIP
Allow: INVITE, ACK, BYE, CANCEL, OPTIONS, MESSAGE, INFO, UPDATE, REGISTER, REFER, NOTIFY, PUBLISH, SUBSCRIBE
Supported: path, replaces
Allow-Events: talk, hold, conference, presence, as-feature-event, dialog, line-seize, call-info, sla, include-session-description, presence.winfo, message-summary, refer
Content-Type: application/sdp
Content-Disposition: session
Content-Length: 281

v=0
o=SIPPulse 1546586511 1546586512 IN IP4 {{ address['ip'] }}
s=SIPPulse
c=IN IP4 {{ address['ip'] }}
t=0 0
m=audio 17828 RTP/AVP 18 0 8 101
a=rtpmap:18 G729/8000
a=fmtp:18 annexb=no
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-16
a=ptime:20
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
click
Loading

0 comments on commit bff7af4

Please sign in to comment.