Skip to content

Commit

Permalink
ADD: NONE - Custom headers in GUI and #21
Browse files Browse the repository at this point in the history
  • Loading branch information
Pietro Bertera committed Jan 27, 2016
1 parent e3c5221 commit 58ea277
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 48 deletions.
62 changes: 57 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ SPLiT is developed with hackability in mind, the main target is to reproduce SIP
- SIP Registrar with challenge authentication
- SIP Stateless Proxy or Redirect server mode
- SIP UDP only suppport
- SIP custom headers (via CLI only at the moment)
- SIP custom headers
- Embedded SIP Multicast Plug&Play provisioining server
- Embedded DHCP server with 66 and 67 options support
- Embedded HTTP server
Expand Down Expand Up @@ -107,9 +107,26 @@ On windows (in a cmd.exe prompt):
--dhcp-leasesfile=DHCP_LEASESFILE
DHCP leases file store

The arguments *--sip-exposedport* and *--sip-exposedip* allows you to configure the *Record-Route* added to the forwarded request.
## General options

## Adding custom headers
* ***-t*** Run the software in terminal mode withouth graphical UI, **default:** run in GUI mode
* ***-d*** Run in debug mode, very verbose, **default:** debug is disabled
* ***-i <IP_address>*** Binds all the service on the local IP *<IP_Address>*, default: 127.0.0.1
* ***-l <log_file>*** Wite the logs into *<log_file>*, **default:** no logfile, logs are sent to stdout


## SIP Proxy options

* ***--sip-redirect*** Act as a SIP redirect: instead forwarding the requests reply with a SIP 302 Moved, the Contact contains the destination URI, **default:** disabled
* ***--sip-port <SIP_port>*** Specify the SIP port to use, **default:** 5060
* ***--sip-log <SIP_log_file>*** Write the SIP log into the file *<SIP_log_file>*, **default:** send the messages to stdout
* ***--sip-expires <expires_value>*** Default registration Expires header value, default: 3600
* ***--sip-password <SIP_password>*** SIP password, **default:** *protected*
* ***--sip-exposedip <IP_address>*** IP address to report into the *Record-Route* header, **default:** the local IP address
* ***--sip-exposedport <SIP_port>*** SIP port to report into the Record-Route* header, **default:** the local SIP port
* ***--sip-customheader <Custom_header_rule>*** Add a custom SIP header to all the request matching the filter defined into this option, see below, **defaut:** none

### Adding SIP custom headers

Using the switch *--sip-customheader* you can add one or more SIP headers to forwarded requests, you can filter on the SIP method and the destination URI.
The *--sip-customheader* value is a strig composed by 3 token separated by the *:* character:
Expand All @@ -118,16 +135,51 @@ The *--sip-customheader* value is a strig composed by 3 token separated by the *
- second token must contain a valid regex, the regex is evaluated against the request SIP URI
- the third token contains the SIP header (header name + header value)

### Example:
#### Example:

./SPLiT.py -i 172.16.18.15 -d -t --sip-customheader="INVITE:^.{4,}@.*$:Alert-Info: <http://www.notused.com>;info=alert-external" --sip-customheader="INVITE:^.{3,3}@.*$:Alert-Info: <http://www.notused.com>;info=alert-internal" --sip-customheader="*:.*:X-Forwarded-from: SPLiT Proxy"
./SPLiT.py -i 172.16.18.15 -d -t \
--sip-customheader="INVITE:^.{4,}@.*$:Alert-Info: <http://www.notused.com>;info=alert-external" \
--sip-customheader="INVITE:^.{3,3}@.*$:Alert-Info: <http://www.notused.com>;info=alert-internal" \
--sip-customheader="*:.*:X-Forwarded-from: SPLiT Proxy"

Using this command SPLiT will:

- add the custom header *Alert-Info: <http://www.notused.com>;info=alert-external* only to the *INVITE* requests sent to all the SIP URI with len greater or equal to 4.
- add the custom header *Alert-Info: <http://www.notused.com>;info=alert-internal* only to the *INVITE* requests sent to all the SIP URI with len lesser than 3.
- add the custom header *X-Forwarded-from: SPLiT Proxy* to all the requests sent to all the SIP URI

## SIP PnP Server options

* ***--pnp*** Enable the Plug&Play server, **default:** disabled
* ***--pnp-uri <PnP_URI>*** Plug&Play provisioning URI, **default:** *http://provisioning.snom.com/{model}/{model}.php?mac={mac}*

## TFTP Server options

* ***--tftp*** Enable the TFTP server, **default:** disabled
* ***--tftp-root <TFTP_root_dir>*** TFTP server root directory: all the file and folders contained in *<TFTP_root_dir>* will be accessible via TFTP. If an absolute path isn't provided the directory will be resolved starting from the process current path, **default:** *tftp*
* ***--tftp-port <TFTP_port>*** TFTP server port: the TFTP server will bind to the *<TFTP_port* UDP port, **default:** *69*

## HTTP Server options

* ***--http*** Enable the HTTP server, **default:** disabled
* ***--http-root <HTTP_root_dir>*** HTTP server root directory: all the file and folders contained in *<HTTP_root_dir>* will be accessible via HTTP. If an absolute path isn't provided the directory will be resolved starting from the process current path, **default:** *http*
* ***--http-port <HTTP_port>*** HTTP server port: the HTTP server will bind to the *<TFTP_port* TCP port, **default:** *80*

## DHCP Server options

* ***--dhcp*** Enable the DHCP server, **default:** disabled
* ***--dhcp-begin <DHCP_BEGIN>*** Start address, DHCP leases will be assigned starting from *<DHCP_BEGIN>*, **default:** none
* ***--dhcp-end <DHCP_END>*** End address, DHCP leases will be assigned up to the *<DHCP_END>*, **default:** none
* ***--dhcp-subnetmask <DHCP_Subnet_mask>*** Subnet mask to provided to the DHCP clients, **default:** none
* ***--dhcp-gateway <DHCP_Gateway>*** Gateway provided to the clients, **default:** none
* ***--dhcp-dns <DHCP_Dns>*** DNS server provided to the clients, **default:** none
* ***--dhcp-bcast <DHCP_Broadcast>*** Broadcast address provided to the clients, **default:** none
* ***--dhcp-fileserver <OPT-66_Value>*** DHCP option 66 value, **default:** none
* ***--dhcp-filename <OPT-67_Value>*** DHCP option 67 value, **default:** none
* ***--dhcp-leasesfile <DHCP_leasesfile>*** DHCP server will save the leases into the file specified by *<DHCP_leasesfile>*, **default:** *dhcp_leases.dat*



# Screenshots

**Main tab:**
Expand Down
44 changes: 25 additions & 19 deletions SPLiT.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,51 +63,51 @@
opt.add_option('--sip-password', dest='sip_password', type='string', default='protected',
help='Authentication password (default: protected)')
opt.add_option('--sip-exposedip', dest='sip_exposed_ip', type='string', default=None,
help='Exposed/Public IP to use into the Record-Route header')
help='Exposed/Public IP to use into the Record-Route header, default: the local IP')
opt.add_option('--sip-exposedport', dest='sip_exposed_port', type='int', default=None,
help='Exposed/Public port to use into the Record-Route header')
opt.add_option('--sip-customheader', dest='custom_headers', type='string', action='append', default=None,
help='Add a custom SIP header to the forwarded request: <method>:<URI-regex>:<SIP-Header')
help='Exposed/Public port to use into the Record-Route header, default: the local SIP port')
opt.add_option('--sip-customheader', dest='sip_custom_headers', type='string', action='append', default=[],
help='Add a custom SIP header to the forwarded request: <method>:<URI-regex>:<SIP-Header, default: none')

opt.add_option('--pnp', dest='pnp', default=False, action='store_true',
help='Enable the PnP server')
help='Enable the PnP server, default: disabled')
opt.add_option('--pnp-uri', dest='pnp_uri', default='http://provisioning.snom.com/{model}/{model}.php?mac={mac}', action='store',
help='Configure the PnP URL')

opt.add_option('--tftp', dest='tftp', default=False, action='store_true',
help='Enable the TFTP server')
help='Enable the TFTP server, default: disabled')
opt.add_option('--tftp-root', dest='tftp_root', type='string', default='tftp', action='store',
help='TFTP server root directory (default: tftp)')
opt.add_option('--tftp-port', dest='tftp_port', type='int', default=69, action='store',
help='TFTP server port (default: 69)')

opt.add_option('--http', dest='http', default=False, action='store_true',
help='Enable the HTTP server')
help='Enable the HTTP server, default: disabled')
opt.add_option('--http-root', dest='http_root', default='http', action='store',
help='HTTP server root directory (default: http)')
opt.add_option('--http-port', dest='http_port', default=80, action='store',
help='HTTP server port (default: 80)')

opt.add_option('--dhcp', dest='dhcp', default=False, action='store_true',
help='Enable the DHCP server')
help='Enable the DHCP server, default: disabled')
opt.add_option('--dhcp-begin', dest='dhcp_begin', default=DHCP_DEFAULT_BEGIN, action='store',
help='DHCP lease range start')
help='DHCP lease range start, default: none')
opt.add_option('--dhcp-end', dest='dhcp_end', default=DHCP_DEFAULT_END, action='store',
help='DHCP lease range end')
help='DHCP lease range end, default: none')
opt.add_option('--dhcp-subnetmask', dest='dhcp_subnetmask', default=DHCP_DEFAULT_SUBNETMASK, action='store',
help='DHCP lease subnet mask')
help='DHCP lease subnet mask, default: none')
opt.add_option('--dhcp-gateway', dest='dhcp_gateway', default=DHCP_DEFAULT_GW, action='store',
help='DHCP lease gateway')
help='DHCP lease gateway, default: none')
opt.add_option('--dhcp-dns', dest='dhcp_dns', default=DHCP_DEFAULT_DNS, action='store',
help='DHCP lease DNS')
help='DHCP lease DNS, default: none')
opt.add_option('--dhcp-bcast', dest='dhcp_bcast', default=DHCP_DEFAULT_BCAST, action='store',
help='DHCP lease broadcast')
help='DHCP lease broadcast, default: none')
opt.add_option('--dhcp-fileserver', dest='dhcp_fileserver', default='', action='store',
help='DHCP lease fileserver IP (option 66)')
help='DHCP lease fileserver IP (option 66), default: none')
opt.add_option('--dhcp-filename', dest='dhcp_filename', default='', action='store',
help='DHCP lease filename (option 67)')
help='DHCP lease filename (option 67), default: none')
opt.add_option('--dhcp-leasesfile', dest='dhcp_leasesfile', default='dhcp_leases.dat', action='store',
help='DHCP leases file store')
help='DHCP leases file store, default: dhcp_leases.dat')

options, args = opt.parse_args(sys.argv[1:])

Expand All @@ -119,10 +119,16 @@
main_logger.debug("SIP: Writing SIP messages in %s log file" % options.sip_logfile)
main_logger.debug("SIP: Authentication password: %s" % options.sip_password)
main_logger.debug("Logfile: %s" % options.logfile)


if not options.terminal:
try:
import Tkinter as tk
except ImportError:
main_logger.error("Tk library not installed, falling back to teminal mode")
options.terminal = True

if not options.terminal:
import gui
import Tkinter as tk

root = tk.Tk()
app = gui.MainApplication(root, options, main_logger)
Expand Down
12 changes: 10 additions & 2 deletions gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,14 @@ def __init__(self, root, options, main_logger):
tk.Label(self.settings_frame, text="Password:").grid(row=row, column=0, sticky=tk.W)
tk.Entry(self.settings_frame, textvariable=self.gui_sip_password, width=15).grid(row=row, column=1, sticky=tk.W)
row = row + 1


#self.gui_sip_custom_headers = tk.StringVar()
tk.Label(self.settings_frame, text="Custom Headers:").grid(row=row, column=0, sticky=tk.W)
self.gui_sip_custom_headers = ScrolledText(self.settings_frame, height=10, bg='beige')
self.gui_sip_custom_headers.insert(0.0, '\n'.join(self.options.sip_custom_headers).rstrip())
self.gui_sip_custom_headers.grid(row=row, column=1, columnspan=3, sticky=tk.W)
row = row + 1

self.sip_control_button = tk.Button(self.settings_frame, text="Start SIP Proxy", command=self.start_sip_proxy)
self.sip_control_button.grid(row=row, column=0, sticky=tk.N)

Expand Down Expand Up @@ -269,7 +276,8 @@ def start_sip_proxy(self):
self.options.sip_port = self.gui_sip_port.get()
self.options.sip_password = self.gui_sip_password.get()
self.options.sip_redirect = self.gui_sip_redirect.get()

self.options.sip_custom_headers = self.gui_sip_custom_headers.get(0.0, tk.END).splitlines()

self.main_logger.debug("Writing SIP messages in %s log file" % self.options.sip_logfile)
self.main_logger.debug("Authentication password: %s" % self.options.sip_password)
self.main_logger.debug("Logfile: %s" % self.options.logfile)
Expand Down
49 changes: 27 additions & 22 deletions proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ def __init__(self, server_address, RequestHandlerClass, sip_logger, main_logger,
self.recordroute = "Record-Route: <sip:%s:%d;lr>" % (rr_ip, rr_port)
self.topvia = "Via: SIP/2.0/UDP %s:%d" % (server_address[0], server_address[1])
self.main_logger.info("NOTICE: SIP Proxy starting on %s:%d" % (server_address[0], server_address[1]))
#self.main_logger.debug("SIP: Config dump: %s" % self.options)

class UDPHandler(SocketServer.BaseRequestHandler):

Expand Down Expand Up @@ -417,28 +418,32 @@ def processRegister(self):

def add_headers(function):
def _add_headers(self, *args, **kwargs):
if len(self.server.options.custom_headers) > 0:
self.server.main_logger.debug('Adding custom headers')
for full_header in self.server.options.custom_headers:
if len(self.server.options.sip_custom_headers) > 0:
for full_header in self.server.options.sip_custom_headers:
md = rx_request_uri.search(self.data[0])
if md:
method = md.group(1)
uri = md.group(2)
else:
self.server.main_logger.debug("SIP: Received code, ignoring")
return
self.server.main_logger.debug("SIP: Custom headers: received code, ignoring")
return function(self)

conf_header_method = full_header.split(':')[0]
conf_header_uri_r = full_header.split(':')[1]
conf_header_value = ':'.join(full_header.split(':')[2:])

try:
conf_header_uri_r = full_header.split(':')[1]
conf_header_value = ':'.join(full_header.split(':')[2:])
except IndexError:
self.server.main_logger.error("SIP: Invalid custom header value: '%s'" % full_header)
continue

if conf_header_method.upper() == method.upper() or conf_header_method == '*':
self.server.main_logger.debug("SIP: Matched custom method '%s' against '%s'" % (conf_header_method, method))
try:
match = re.match(conf_header_uri_r, uri)
except:
self.server.main_logger.error("SIP: Invalid regex: '%s'" % conf_header_uri_r)
match = None
if match:
continue
if match:
self.server.main_logger.debug("SIP: Matched custom header regex '%s' against '%s' URI" % (conf_header_uri_r, uri))
self.server.main_logger.debug("SIP: Adding header '%s'" % conf_header_value)
self.data.insert(2, conf_header_value)
Expand Down Expand Up @@ -552,8 +557,8 @@ def processAck(self):

@add_headers
@is_redirect
def processNonInvite(self):
self.server.main_logger.info("SIP: NonInvite received: %s" % self.data[0])
def processGenericRequest(self):
self.server.main_logger.info("SIP: Request received: %s" % self.data[0])
origin = self.getOrigin()
if len(origin) == 0 or not self.server.registrar.has_key(origin):
self.server.main_logger.debug("SIP: Origin not found: %s" % origin)
Expand Down Expand Up @@ -603,29 +608,29 @@ def processRequest(self):
elif rx_ack.search(request_uri):
self.processAck()
elif rx_bye.search(request_uri):
self.processNonInvite()
self.processGenericRequest()
elif rx_cancel.search(request_uri):
self.processNonInvite()
self.processGenericRequest()
elif rx_options.search(request_uri):
self.processNonInvite()
self.processGenericRequest()
elif rx_message.search(request_uri):
self.processNonInvite()
self.processGenericRequest()
elif rx_refer.search(request_uri):
self.processNonInvite()
self.processGenericRequest()
elif rx_prack.search(request_uri):
self.processNonInvite()
self.processGenericRequest()
elif rx_update.search(request_uri):
self.processNonInvite()
self.processGenericRequest()
elif rx_info.search(request_uri):
#self.sendResponse("200 0K")
self.processNonInvite()
self.processGenericRequest()
elif rx_subscribe.search(request_uri):
self.processNonInvite()
self.processGenericRequest()
#self.sendResponse("200 0K")
elif rx_publish.search(request_uri):
self.sendResponse("200 0K")
elif rx_notify.search(request_uri):
self.processNonInvite()
self.processGenericRequest()
#self.sendResponse("200 0K")
elif rx_code.search(request_uri):
self.processCode()
Expand Down

0 comments on commit 58ea277

Please sign in to comment.