Sipexer - Modern and flexible SIP (RFC3261) command line tool

Overview

sipexer

Modern and flexible SIP (RFC3261) command line tool.

Overview

sipexer is a cli tool that facilitates sending SIP requests to servers. It uses a flexible template system to allow defining many parts of the SIP request via command line parameters. It has support for UDP, TCP, TLS and WebSocket transport protocols, being suitable to test modern WebRTC SIP servers.

sipexer is not a SIP cli softphone, but a tool for crafting SIP requests mainly for the purpose of testing SIP signaling routing or monitoring servers.

It is written in Go, aiming to be usable from Linux, MacOS or Windows.

The meaning of the name sipexer: randomly selected to be easy to write and pronounce, quickly after thought of it as the shortening of SIP EXEcutoR.

Installation

Install

Compile From Sources

First install Go. Once the Go environment is configured, clone sipexer git repository:

git clone https://github.com/miconda/sipexer

Download dependencies and build:

cd sipexer
go get ./...
go build .

The binary sipexer should be generated in the current directory.

Download Binary Release

TBA

Usage

Prototype:

sipexer [options] [target]

See sipexer -h for the command line options and arguments.

Defaults:

  • target address: sip:127.0.0.1:5060
  • SIP method: OPTIONS
  • From user: alice
  • From domain: localhost
  • To user: bob
  • To domain: localhost

Examples

Send an OPTIONS request over UDP to 127.0.0.1 and port 5060 - couple of variants:

sipexer
sipexer 127.0.0.1
sipexer 127.0.0.1 5060
sipexer udp 127.0.0.1 5060
sipexer udp:127.0.0.1:5060
sipexer sip:127.0.0.1:5060
sipexer "sip:127.0.0.1:5060;transport=udp"

Specify a different R-URI:

sipexer -ruri sip:[email protected] udp:127.0.0.1:5060

Send from UDP local port 55060:

sipexer -laddr 127.0.0.1:55060 udp:127.0.0.1:5060

Send REGISTER request with generated contact, expires as well as user and password authentication:

sipexer -register -cb -ex 600 -au alice -ap test123 udp:127.0.0.1:5060

Set fuser field to carol:

sipexer -sd -fv "fuser:carol" udp:127.0.0.1:5060

Set fuser field to carol and tuser field to david:

sipexer -sd -fv "fuser:carol"  -fv "tuser:david" udp:127.0.0.1:5060

Add extra headers:

sipexer -sd -xh "X-My-Key:abcdefgh" -xh "P-Info:xyzw" udp:127.0.0.1:5060

Send MESSAGE request with body:

sipexer -message -mb 'Hello!' -sd -su udp:127.0.0.1:5060

Message Template

Template Data

The message to be sent via the SIP connection is built from a template file and a fields file.

The template file can contain any any of the directives supported by Go package text/template - for more see:

Example:

;tag={{.fromtag}} To: {{if .tname}}"{{.tname}}" {{end}} Call-ID: {{.callid}} CSeq: {{.cseqnum}} {{.method}} {{if .subject}}Subject: {{.subject}}{{else}}$rmeol{{end}} {{if .date}}Date: {{.date}}{{else}}$rmeol{{end}} {{if .contacturi}}Contact: {{.contacturi}}{{if .contactparams}};{{.contactparams}}{{end}}{{else}}$rmeol{{end}} {{if .expires}}Expires: {{.expires}}{{else}}$rmeol{{end}} {{if .useragent}}User-Agent: {{.useragent}}{{else}}$rmeol{{end}} Content-Length: 0 ">
{{.method}} {{.ruri}} SIP/2.0
Via: SIP/2.0/{{.viaproto}} {{.viaaddr}}{{.rport}};branch=z9hG4bKSG.{{.viabranch}}
From: {{if .fname}}"{{.fname}}" {{end}}
    
     ;tag={{.fromtag}}
To: {{if .tname}}"{{.tname}}" {{end}}
     
      
Call-ID: {{.callid}}
CSeq: {{.cseqnum}} {{.method}}
{{if .subject}}Subject: {{.subject}}{{else}}$rmeol{{end}}
{{if .date}}Date: {{.date}}{{else}}$rmeol{{end}}
{{if .contacturi}}Contact: {{.contacturi}}{{if .contactparams}};{{.contactparams}}{{end}}{{else}}$rmeol{{end}}
{{if .expires}}Expires: {{.expires}}{{else}}$rmeol{{end}}
{{if .useragent}}User-Agent: {{.useragent}}{{else}}$rmeol{{end}}
Content-Length: 0


     
    

The internal template can be found at the top of sipexer.go file.

Template Fields

The fields file has to contain a JSON document with the fields to be replaced in the template file. The path to the JSON file is provided via -ff or --fields-file parameters.

When the --fields-eval of -fe cli option is provided, sipexer evaluates the values of the fields in the root structure of the JSON document. That means special tokens (expressions) are replaced if the value of the field is a string matching one of the next:

  • "$uuid" - replace with a UUID value
  • "$randseq" - replace with a random number from 1 to 1 000 000.
  • "$datefull" - replace with output of time.Now().String()
  • "$daterfc1123" - replace with output of time.Now().Format(time.RFC1123)
  • "$dateansic" - replace with output of time.Now().Format(time.ANSIC)
  • "$dateunix" - replace with output of time.Now().Format(time.UnixDate)
  • "$timestamp" - replace with output of time.Now().Unix()
  • "$cr" - replace with \r
  • "$lf" - replace with \n

When internal template is used, --fields-eval is turned on.

Example fields file:

{
	"method": "OPTIONS",
	"fuser": "alice",
	"fdomain": "localhost",
	"tuser": "bob",
	"tdomain": "localhost",
	"viabranch": "$uuid",
	"rport": ";rport",
	"fromtag": "$uuid",
	"callid": "$uuid",
	"cseqnum": "$randseq",
	"date": "$daterfc1123"
}

The internal fields data can be found at the top of sipexer.go file.

The values for fields can be also provided using --field-val or -fv cli parameter, in format name:value, for example:

sipexer --field-val="domain:openrcs.com" ...

The value provided via --field-val overwrites the value provided in the JSON fields file.

Alternatives

There are several alternatives that might be useful to consider:

  • sipp - SIP testing tool using XML-based scenarios
  • sipsak - SIP swiss army knife - SIP cli testing tool
  • wsctl - WebSocket cli tool with basic support for SIP
  • baresip - cli SIP softphone
  • pjsua - cli SIP softphone

License

GPLv3

Copyright: Daniel-Constantin Mierla (Asipto)

Contributions

Contributions are welcome!

Fork and do pull requests:

Issues
  • Via: branch in non-200 ACK differs from Via: branch in INVITE.

    Via: branch in non-200 ACK differs from Via: branch in INVITE.

    When receiving a non 200 reply for an INVITE sipexer sends a response, but the branch parameter of the via header does not match the branch parameter of the INVITE.

    Given this minimal Kamailio config:

    #!KAMAILIO
    
    loadmodule "tm.so"
    loadmodule "sl.so"
    loadmodule "pv.so"
    
    request_route {
        if ( $rm == "ACK" ) {
            if( !t_check_trans() ) {
                exit;
            }
        }
        t_newtran();
        t_reply("404","Not Found");
    }
    

    Sending an Invite from sipexer (where kamailio resolves to the host running the kamailio.cfg above):

    sipexer -invite kamailio
    

    Has Kamailio repeatedly sending the 404 reply after sipexer sends an ACK. This appears to be due to the ACK's via: header having a different branch than the INVITE.

    INVITE (minus SDP):

    INVITE sip:kamailio:5060 SIP/2.0
    Via: SIP/2.0/UDP 172.31.0.4:56595;rport;branch=z9hG4bKSG.f7f686a3-ce6d-45a1-95a7-42652740d210
    From: <sip:[email protected]>;tag=9dc18f12-c779-4b0e-b30e-681d0526c72e
    To: <sip:[email protected]>
    Call-ID: 0418f8d8-d472-43ab-9dc2-786129278a1c
    CSeq: 481768 INVITE
    Date: Fri, 01 Apr 2022 16:30:13 CDT
    User-Agent: SIPExer v1.0.0
    Content-Length: 210
    Content-Type: application/sdp
    

    ACK:

    ACK sip:kamailio:5060 SIP/2.0
    Via: SIP/2.0/UDP 172.31.0.4:56595;rport;branch=z9hG4bKSG.cda7e72d-892c-4bee-ad36-cec0734d8c59
    From: <sip:[email protected]>;tag=9dc18f12-c779-4b0e-b30e-681d0526c72e
    To: <sip:[email protected]>;tag=a6a1c5f60faecf035a1ae5b6e96e979a-f254
    Call-ID: 0418f8d8-d472-43ab-9dc2-786129278a1c
    CSeq: 481768 ACK
    Content-Length: 0
    

    Call flow:

                 172.31.0.4:56595              172.31.0.3:5060  
              ──────────┬─────────          ──────────┬─────────
      16:30:13.498783   │           INVITE            │ 
            +0.000956   │ ──────────────────────────> │ 
      16:30:13.499739   │        404 Not Found        │ 
            +0.000530   │ <────────────────────────── │ 
      16:30:13.500269   │             ACK             │ 
            +0.447913   │ ──────────────────────────> │ 
      16:30:13.948182   │        404 Not Found        │ 
            +0.999823   │ <<<──────────────────────── │ 
      16:30:14.948005   │        404 Not Found        │ 
            +2.000091   │ <<<──────────────────────── │ 
      16:30:16.948096   │        404 Not Found        │        
                        │ <<<──────────────────────── │
    
    opened by whosgonna 3
  • Route headers not properly reversed from RR set when RR URIs comma-separated

    Route headers not properly reversed from RR set when RR URIs comma-separated

    When using the -invite flow, a 200 OK is received by the sipexer UA with two RR hops:

    Record-Route: <sip:GW_2:5060;lr=on;ftag=37ff691d-7bf2-446b-abaa-9c09fe12deb1;vsf=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AA-;dlgcor=ccf1.7e73;proxy_media=yes>
    Record-Route: <sip:GW_1:5060;lr;ftag=37ff691d-7bf2-446b-abaa-9c09fe12deb1;dlgcor=ccf.8be1;fromcor=ejFwbUZxUmpUUFNBejFwbUZxU
    UUFNBejFwbUZxUmpUUFM-;proxy_media=yes>
    

    This is properly reversed in the order of the Route headers constructed in the e2e ACK / subsequent in-dialog messages:

    Route: <sip:GW_1:5060;lr;ftag=37ff691d-7bf2-446b-abaa-9c09fe12deb1;dlgcor=ccf.8be1;fromcor=ejFwbUZxUmpUUFNBejFwbUZxUmp
    FNBejFwbUZxUmpUUFM-;proxy_media=yes>
    Route: <sip:GW_2:5060;lr=on;ftag=37ff691d-7bf2-446b-abaa-9c09fe12deb1;vsf=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    -;dlgcor=ccf1.7e73;proxy_media=yes>
    

    However, if the RR set returned is compacted into a single value...

    Record-Route: <sip:GW_2:5060;lr=on;ftag=37ff691d-7bf2-446b-abaa-9c09fe12deb1;vsf=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AA-;dlgcor=ccf1.7e73;proxy_media=yes>, <sip:GW_1:5060;lr;ftag=37ff691d-7bf2-446b-abaa-9c09fe12deb1;dlgcor=ccf.8be1;fromcor=ejFwbUZxUmpUUFNBejFwbUZxU
    UUFNBejFwbUZxUmpUUFM-;proxy_media=yes>
    

    Then this order is imitated, rather than reversed, in the subsequent Route construction.

    opened by abalashov 2
  • Using CGO_ENABLED=0 for Alpine Linux, etc.

    Using CGO_ENABLED=0 for Alpine Linux, etc.

    I'm sorry that I'm not able to better define the reasoning, etc. on this, however the pre-compiled binary won't work on Alpine linux, and just compiling as indicated in the instructions doesn't work:

    git clone https://github.com/miconda/sipexer
    cd sipexer
    go get ./...
    go build .
    

    Setting the env var CGO_ENABLED to 0 when building creates a binary that works (for me) in both the default alpine container as well as my normal Debian distro:

    CGO_ENABLED=0 go build .
    

    Given that this works for me on both, I'm not sure if it's better to have the distributed binary and the instructions always reference this, OR to simply add a note in the installation section? Or maybe this is so obvious to someone working in Go that it's unnecessary?

    opened by whosgonna 2
  • Wrong transport name (?) in the generated SDP for INVITE

    Wrong transport name (?) in the generated SDP for INVITE

    The SDP in the generated INVITE contains transport RTP in the media description line: <m=audio 32962 RTP 0 8 101>

    I think that the transport should be RTP/AVP instead of just RTP.

    opened by leonid-xorcom 1
  • Support repeated extra-header

    Support repeated extra-header

    Thanks to @miconda for awesome tool,

    I just found a minor issue with the sipexer use the map to store the extra-header via -xh, that cause for only the latest one can be processed. Example:

    
    [info] [sipexer.go:1260] main.SIPExerDialogLoop(): local socket address: 127.0.0.1:53256 (udp)
    [info] [sipexer.go:1261] main.SIPExerDialogLoop(): local via address: 127.0.0.1:53256
    [info] [sipexer.go:1262] main.SIPExerDialogLoop(): sending to udp 127.0.0.1:5060: [[---
    INVITE sip:127.0.0.1:5060 SIP/2.0
    Via: SIP/2.0/UDP 127.0.0.1:53256;rport;branch=z9hG4bKSG.256b7e98-b824-47ae-bac8-a2c3aadf9281
    From: <sip:[email protected]>;tag=d3107dc4-7f35-481c-b58f-e351e2704027
    To: <sip:[email protected]>
    Call-ID: f5ccddd4-da36-48d7-b218-63cff5cdca0c
    CSeq: 275785 INVITE
    Date: Wed, 25 May 2022 16:23:20 +07
    User-Agent: SIPExer v1.0.0
    Content-Length: 208
    Diversion: <[email protected]>
    Content-Type: application/sdp
    
    v=0
    o=sipexer 1653470600 1653470600 IN IP4 127.0.0.1
    s=call
    c=IN IP4 127.0.0.1
    t=0 0
    m=audio 37009 RTP 0 8 101
    a=rtpmap:0 pcmu/8000
    a=rtpmap:8 pcma/8000
    a=rtpmap:101 telephone-event/8000
    a=sendrecv
    
    [info] [sipexer.go:1264] main.SIPExerDialogLoop(): ---]]
    

    only the [email protected] is processed. We expected that the message would be:

    ...
    Diversion: <[email protected]>
    Diversion: <[email protected]>
    ...
    

    Reference: https://datatracker.ietf.org/doc/html/rfc3261#section-7.3.1

    opened by hnimminh 1
  • Retransmission of INVITE on receipt of 100 using TCP

    Retransmission of INVITE on receipt of 100 using TCP

    When using TCP, sipexer seems to immediately re-transmit the INVITE upon receipt of 100 reply. This potentially results in a storm of packets. Note the lack of any wait time before retransmitting the INVITE.

               10.174.5.101:44330             10.174.5.11:5060 
             ──────────┬─────────          ──────────┬─────────
     11:27:56.826765   │           INVITE            │         
           +0.000889   │ ──────────────────────────> │         
     11:27:56.827654   │         100 Trying          │         
           +0.000728   │ <────────────────────────── │         
     11:27:56.828382   │           INVITE            │         
           +0.000100   │ ────────────────────────>>> │         
     11:27:56.828482   │         100 Trying          │         
           +0.000598   │ <<<──────────────────────── │         
     11:27:56.829080   │           INVITE            │         
           +0.000080   │ ────────────────────────>>> │         
     11:27:56.829160   │         100 Trying          │         
           +0.000506   │ <<<──────────────────────── │         
     11:27:56.829666   │           INVITE            │         
           +0.000086   │ ────────────────────────>>> │         
     11:27:56.829752   │         100 Trying          │         
           +0.000553   │ <<<──────────────────────── │         
     11:27:56.830305   │           INVITE            │         
           +0.000067   │ ────────────────────────>>> │         
     11:27:56.830372   │         100 Trying          │         
           +0.000632   │ <<<──────────────────────── │         
     11:27:56.831004   │           INVITE            │         
    . . .
    

    Tested against Kamailio 5.6, just returning a 100:

    #!KAMAILIO
    
    loadmodule "sl"
    
    request_route {
        send_reply("100", "Trying");
        exit;
    }
    

    Using the following sipexer command:

    sipexer -invite -co -com -cb -fu 15555551000 -tu 15555551234  -sd -su tcp:proxy
    

    Resulted in over 1,300 messages (sent and received so over 650 INVITEs retransmitted) in less than one second.

    Changing the target from tcp:proxy to udp:proxy gives the expected behavior - sipexer receives the 100 and waits.

    opened by whosgonna 1
  • Digest auth interop problem with FreeSWITCH

    Digest auth interop problem with FreeSWITCH

    I seem to have a digest auth interop problem with FreeSWITCH that I can't quite comprehend:

    Invocation:

    # ./sipexer -vl 3 -timer-t1 2000 -register -contact-uri sip:[email protected]:5060 -com -ex 120 -fuser abalashov -tuser abalashov -ha1 -au abalashov -ap xxx -sd -nagios udp:sip.evaristesys.com
    

    The 401 challenge:

    WWW-Authenticate: Digest realm="sip.evaristesys.com",nonce="634df9bc-54d5-431f-8dc7-66cb556fe12e",algorithm=MD5,qop="auth"
    

    The full REGISTER response + challenge answer:

    REGISTER sip:sip.evaristesys.com:5060 SIP/2.0
    Via: SIP/2.0/UDP 172.30.106.189:55414;rport;branch=z9hG4bKSG.be1f3a00-c393-4f3b-8d31-82fe0af34011
    From: <sip:[email protected]>;tag=6c5e9643-b2f8-4069-b756-9a731009cf3a
    To: <sip:[email protected]>
    Call-ID: 1b8fd706-ab3c-4c18-bfab-d39e7e382cd5
    CSeq: 841081 REGISTER
    Date: Tue, 15 Feb 2022 08:26:54 EST
    Contact: <sip:[email protected]:5060>
    Expires: 120
    User-Agent: SIPExer v1.0.0
    Content-Length: 0
    Authorization: Digest username="abalashov", realm="sip.evaristesys.com", nonce="634df9bc-54d5-431f-8dc7-66cb556fe12e", uri="sip:sip.evaristesys.com:5060", cnonce="I4VYGmfe8VC9g+Li", nc=00000001, qop=auth, opaque="", algorithm=MD5, response="fae65c5bd2d12f84bbeaa467f3dc092e"
    

    I stubbornly get a 403 Forbidden from FreeSWITCH, without explanation.

    I've compared with a working REGISTER flow from a Polycom; aside from the Authorization response itself, the requests are indistinguishable.

    As far as I can tell, the code does everything correctly:

           // build digest response
            cnonce := SIPExerRandomKey()
            response := SIPExerHMD5(strings.Join([]string{HA1, hparams["nonce"], "00000001", cnonce, hparams["qop"], HA2}, ":"))
            // build header body
            AuthHeader = fmt.Sprintf(`Digest username="%s", realm="%s", nonce="%s", uri="%s", cnonce="%s", nc=00000001, qop=%s, opaque="%s", algorithm=MD5, response="%s"`,
                username, hparams["realm"], hparams["nonce"], hparams["uri"], cnonce, hparams["qop"], hparams["opaque"], response)
    

    Any pointers would be helpful! Thank you very much again for writing this amazing tool.

    opened by abalashov 1
Releases(v1.0.3)
Owner
Daniel-Constantin Mierla
Co-Founder Kamailio SIP Server Project (@kamailio). Working for Asipto (@asipto). Open source and RTC advocate.
Daniel-Constantin Mierla
Package command provide simple API to create modern command-line interface

Package command Package command provide simple API to create modern command-line interface, mainly for lightweight usage, inspired by cobra Usage pack

chenen 0 Jan 16, 2022
🚀 goprobe is a promising command line tool for inspecting URLs with modern and user-friendly way.

goprobe Build go build -o ./bin/goprobe Example > goprobe https://github.com/gaitr/goprobe > cat links.txt | goprobe > echo "https://github.com/gaitr/

null 3 Oct 24, 2021
An open-source GitLab command line tool bringing GitLab's cool features to your command line

GLab is an open source GitLab CLI tool bringing GitLab to your terminal next to where you are already working with git and your code without switching

Clement Sam 2k Jul 27, 2022
A command line tool to prompt for a value to be included in another command line.

readval is a command line tool which is designed for one specific purpose—to prompt for a value to be included in another command line. readval prints

Venky 0 Dec 22, 2021
git-xargs is a command-line tool (CLI) for making updates across multiple Github repositories with a single command.

Table of contents Introduction Reference Contributing Introduction Overview git-xargs is a command-line tool (CLI) for making updates across multiple

Gruntwork 622 Aug 7, 2022
git-xargs is a command-line tool (CLI) for making updates across multiple GitHub repositories with a single command

git-xargs is a command-line tool (CLI) for making updates across multiple GitHub repositories with a single command. You give git-xargs:

Maxar Infrastructure 1 Feb 5, 2022
A command line tool for simplified docker volume command built with go

dockervol A command line tool for simplified docker volume command built with go. Features: Remove anonymous volume (beta) Remove volume by matching n

Moh Achun Armando 0 Dec 18, 2021
Modern ls command with vscode like File Icon and Git Integrations. Written in Golang

logo-ls modern ls command with beautiful Icons and Git Integrations . Written in Golang Command and Arguments supported are listed in HELP.md Table of

Yash Handa 901 Jul 30, 2022
👀 A modern watch command. Time machine and pager etc.

Viddy Modern watch command. Viddy well, gopher. Viddy well. Demo Features Basic features of original watch command. Execute command periodically, and

Takumasa Sakao 3.5k Aug 4, 2022
A modern UNIX ed (line editor) clone written in Go

ed (the awesome UNIX line editor) ed is a clone of the UNIX command-line tool by the same name ed a line editor that was nortorious for being and most

James Mills 45 May 29, 2021
fofax is a fofa query tool written in go, positioned as a command-line tool and characterized by simplicity and speed.

fofaX 0x00 Introduction fofax is a fofa query tool written in go, positioned as

null 419 Jul 30, 2022
A flexible commandline tool for template rendering. Supports lots of local and remote datasources.

Read the docs at docs.gomplate.ca, chat with developers and community in the #gomplate channel on Gophers Slack gomplate is a template renderer which

Dave Henderson 1.8k Aug 2, 2022
Watcher - A simple command line app to watch files in a directory for changes and run a command when files change!

Watcher - Develop your programs easily Watcher watches all the files present in the directory it is run from of the directory that is specified while

Geet Sethi 1 Mar 27, 2022
Go package to make lightweight ASCII line graph ╭┈╯ in command line apps with no other dependencies.

asciigraph Go package to make lightweight ASCII line graphs ╭┈╯. Installation go get github.com/guptarohit/asciigraph Usage Basic graph package main

Rohit Gupta 2k Aug 5, 2022
Robust, flexible and resource-efficient pipelines using Go and the commandline

Robust, flexible and resource-efficient pipelines using Go and the commandline Project links: Documentation & Main Website | Issue Tracker | Chat Why

SciPipe 946 Aug 4, 2022
Command-line tool to load csv and excel (xlsx) files and run sql commands

csv-sql supports loading and saving results as CSV and XLSX files with data processing with SQLite compatible sql commands including joins.

Dhamith Hewamullage 23 Jul 7, 2022
F2 is a cross-platform command-line tool for batch renaming files and directories quickly and safely. Written in Go!

F2 is a cross-platform command-line tool for batch renaming files and directories quickly and safely.

Ayooluwa 495 Jul 28, 2022
pgCenter is a command-line admin tool for observing and troubleshooting Postgres.

Command-line admin tool for observing and troubleshooting Postgres.

Lesovsky Alexey 1.4k Jul 20, 2022
textnote is a command line tool for quickly creating and managing daily plain text notes.

textnote is a command line tool for quickly creating and managing daily plain text notes. It is designed for ease of use to encourage the practice of daily, organized note taking. textnote intentionally facilitates only the management (creation, opening, organizing, and consolidated archiving) of notes, following the philosophy that notes are best written in a text editor and not via a CLI.

Daniel Kaslovsky 157 Jul 12, 2022