Fax over SIP works only partially

I have installed an Asterisk, compiled from source, with pjsip, spandsp, res_fax_spandsp and res_fax (but not res_fax_digium) to send Fax via SIP. My intention is to use T.38 whenever possible, otherwise audio over VoIP (which is reported by my service provider to be known to work surprisingly well).

I do not understand, how this should work. This is what I have come to so far - I have 3 receiving terminals to test:

  • First terminal: There is a web site where I can send test faxes for free to. During negotiation, Asterisk explicitly logs that the remote end declines T.38 and continues with audio over VoIP successfully.
  • Second terminal: Our own fax at the office (it is a copier), which is an analogue fax connected to a SIP to analogue converter. We always use this machine to receive faxes, so the SIP line is no problem here. When I call it (also when I use my mobile phone to call it), it immediately answers with some tone (I am not sure if this is DCS of TCF or whatever). This is the where the problem arises: My Asterisk server does not know how to handle this. It goes on with BYE without sending the fax.
  • Third terminal: I do not understand what is going on there, I have to dig in deeper into this and may open another post about its behaviour. It is T.38 capable, but Asterisk does not start transmitting the fax.

This is my config for pjsip:

[simpletrans]
type=transport
protocol=udp
bind=0.0.0.0

[mytrunk]
type=registration
outbound_auth=mytrunk
transport=simpletrans
server_uri=sip:<PHONENR>@my.sip-provider.com:5060
client_uri=sip:<PHONENR>@my.sip-provider.com:5060

[mytrunk]
type=auth
auth_type=userpass
password=<PASSWORD>
username=<PHONENR>

[mytrunk]
type=aor
contact=sip:my.sip-provider.com:5060
 
[mytrunk]
type=endpoint
context=from-external
disallow=all
allow=gsm
allow=alaw
allow=g722
outbound_auth=mytrunk
aors=mytrunk
rtp_symmetric=no
force_rport=no
rewrite_contact=no
from_user=<PHONENR>
from_domain=my.sip-provider.com
t38_udptl=no
t38_udptl_ec=redundancy
t38_udptl_maxdatagram=300
fax_detect=yes

[mytrunk]
type=identify
endpoint=mytrunk
match=my.sip-provider.com

The application SendFax is called from the dial plan just with this along some noise to set variables:

exten => faxout,n,SendFAX(/var/spool/asterisk/tmp/${FAXFILE},dfsz)

You need to provide actual console output to show what is going on. Asterisk won’t just send a BYE because of a tone, unless an underlying application tells it to for some reason.

This is with terminal #2, analogue fax terminal at far end.
This is the log from the Asterisk console:

    -- Attempting call on PJSIP/mytrunk/sip:<TOPHONENR>@my.sip-provider.com for faxout@fax_outgoing:1 (Retry 1)
    -- Called mytrunk/sip:<TOPHONENR>@my.sip-provider.com
    -- PJSIP/mytrunk-00000000 is ringing
       > 0x7f8bb4015be0 -- Strict RTP learning after remote address set to: 91.xxx.xxx.xxx:16310
    -- PJSIP/mytrunk-00000000 answered
    -- Executing [faxout@fax_outgoing:1] NoOp("PJSIP/mytrunk-00000000", "**** SENDING FAX ****") in new stack
    -- Executing [faxout@fax_outgoing:2] Wait("PJSIP/mytrunk-00000000", "2") in new stack
    -- Executing [faxout@fax_outgoing:3] Set("PJSIP/mytrunk-00000000", "FAXFILE=Asterisk_SendFax_res_fax.tif") in new stack
    -- Executing [faxout@fax_outgoing:4] Set("PJSIP/mytrunk-00000000", "FAXOPT(headerinfo)=blabla") in new stack
    -- Executing [faxout@fax_outgoing:5] Set("PJSIP/mytrunk-00000000", "FAXOPT(localstationid)=<FROMPHONENR>") in new stack
    -- Executing [faxout@fax_outgoing:6] SendFAX("PJSIP/mytrunk-00000000", "/var/spool/asterisk/tmp/Asterisk_SendFax_res_fax.tif,dfsz") in new stack
    -- Channel 'PJSIP/mytrunk-00000000' sending FAX:
    --    /var/spool/asterisk/tmp/Asterisk_SendFax_res_fax.tif
       > 0x7f8bb4015be0 -- Strict RTP switching to RTP target address 91.xxx.xxx.xxx:16310 as source
       > 0x7f8bb4015be0 -- Strict RTP learning complete - Locking on source address 91.xxx.xxx.xxx:16310
    -- Auto fallthrough, channel 'PJSIP/mytrunk-00000000' status is 'UNKNOWN'
    -- Executing [h@fax_outgoing:1] NoOp("PJSIP/mytrunk-00000000", "** Hung up **") in new stack
    -- Executing [h@fax_outgoing:2] GotoIf("PJSIP/mytrunk-00000000", "0?FaxDLR:hFail") in new stack
    -- Goto (fax_outgoing,h,9)
    -- Executing [h@fax_outgoing:9] NoOp("PJSIP/mytrunk-00000000", "Failed") in new stack
    -- Executing [h@fax_outgoing:10] Set("PJSIP/mytrunk-00000000", "pages=0") in new stack
    -- Executing [h@fax_outgoing:11] Set("PJSIP/mytrunk-00000000", "status=failed") in new stack
    -- Executing [h@fax_outgoing:12] Set("PJSIP/mytrunk-00000000", "statusstr=failed") in new stack
    -- Executing [h@fax_outgoing:13] Set("PJSIP/mytrunk-00000000", "error=Unexpected message received") in new stack
    -- Executing [h@fax_outgoing:14] NoOp("PJSIP/mytrunk-00000000", "** Sending Delivery Notification **") in new stack
    -- Executing [h@fax_outgoing:15] NoOp("PJSIP/mytrunk-00000000", "** READY **") in new stack
[May 29 12:34:14] NOTICE[46059][C-00000001]: pbx_spool.c:463 attempt_thread: Call completed to PJSIP/mytrunk/sip:<TOPHONENR>@my.sip-provider.com

This is tcpdump, less detailed (not the same call, hence not the same time stamps):

12:40:44.079474 IP 194.xxx.xxx.xxx.5060 > 193.xxx.xxx.xxx.5060: SIP: INVITE sip:<TOPHONENR>@my.sip-provider.com SIP/2.0
12:40:44.086073 IP 193.xxx.xxx.xxx.5060 > 194.xxx.xxx.xxx.5060: SIP: SIP/2.0 100 trying -- your call is important to us
12:40:44.089583 IP 193.xxx.xxx.xxx.5060 > 194.xxx.xxx.xxx.5060: SIP: SIP/2.0 407 Proxy Authentication Required
12:40:44.089803 IP 194.xxx.xxx.xxx.5060 > 193.xxx.xxx.xxx.5060: SIP: ACK sip:<TOPHONENR>@my.sip-provider.com SIP/2.0
12:40:44.089984 IP 194.xxx.xxx.xxx.5060 > 193.xxx.xxx.xxx.5060: SIP: INVITE sip:<TOPHONENR>@my.sip-provider.com SIP/2.0
12:40:44.096075 IP 193.xxx.xxx.xxx.5060 > 194.xxx.xxx.xxx.5060: SIP: SIP/2.0 100 trying -- your call is important to us
12:40:44.334242 IP 193.xxx.xxx.xxx.5060 > 194.xxx.xxx.xxx.5060: SIP: SIP/2.0 180 Ringing
12:40:47.096928 IP 193.xxx.xxx.xxx.5060 > 194.xxx.xxx.xxx.5060: SIP: SIP/2.0 200 OK
12:40:47.097874 IP 194.xxx.xxx.xxx.5060 > 193.xxx.xxx.xxx.5060: SIP: ACK sip:192.168.xx.xx:5083 SIP/2.0
12:40:49.670834 IP 193.xxx.xxx.xxx.5060 > 194.xxx.xxx.xxx.5060: SIP: OPTIONS sip:s@194.xxx.xxx.xxx:5060 SIP/2.0
12:40:49.671283 IP 194.xxx.xxx.xxx.5060 > 193.xxx.xxx.xxx.5060: SIP: SIP/2.0 404 Not Found
12:40:53.072532 IP 193.xxx.xxx.xxx.5060 > 194.xxx.xxx.xxx.5060: SIP: INVITE sip:<PHONENR>@194.xxx.xxx.xxx:5060 SIP/2.0
12:40:53.073576 IP 194.xxx.xxx.xxx.5060 > 193.xxx.xxx.xxx.5060: SIP: SIP/2.0 200 OK
12:40:53.082387 IP 193.xxx.xxx.xxx.5060 > 194.xxx.xxx.xxx.5060: SIP: ACK sip:<PHONENR>@194.xxx.xxx.xxx:5060 SIP/2.0
12:41:09.681681 IP 193.xxx.xxx.xxx.5060 > 194.xxx.xxx.xxx.5060: SIP: OPTIONS sip:s@194.xxx.xxx.xxx:5060 SIP/2.0
12:41:09.682165 IP 194.xxx.xxx.xxx.5060 > 193.xxx.xxx.xxx.5060: SIP: SIP/2.0 404 Not Found
12:41:11.874267 IP 194.xxx.xxx.xxx.5060 > 193.xxx.xxx.xxx.5060: SIP: INVITE sip:192.168.xx.xx:5083 SIP/2.0
12:41:11.880085 IP 193.xxx.xxx.xxx.5060 > 194.xxx.xxx.xxx.5060: SIP: SIP/2.0 100 trying -- your call is important to us
12:41:11.928312 IP 193.xxx.xxx.xxx.5060 > 194.xxx.xxx.xxx.5060: SIP: SIP/2.0 488 Not Acceptable Here
12:41:11.928646 IP 194.xxx.xxx.xxx.5060 > 193.xxx.xxx.xxx.5060: SIP: ACK sip:192.168.xx.xx:5083 SIP/2.0
12:41:16.090031 IP 194.xxx.xxx.xxx.5060 > 193.xxx.xxx.xxx.5060: SIP: BYE sip:192.168.xx.xx:5083 SIP/2.0
12:41:16.098987 IP 193.xxx.xxx.xxx.5060 > 194.xxx.xxx.xxx.5060: SIP: SIP/2.0 200 OK

BUMP I just realised that terminal #2 uses a Cisco SPA112 converter. Since I am the admin of this device too, I turned off T.38 there, and now the scenario looks more promising, but there is still an error at the end of the transmission.

    -- Attempting call on PJSIP/mytrunk/sip:<TOPHONENR>@my.sip-provider.com for faxout@fax_outgoing:1 (Retry 1)
    -- Called mytrunk/sip:<TOPHONENR>@my.sip-provider.com
    -- PJSIP/mytrunk-00000001 is ringing
       > 0x7f1ac801d020 -- Strict RTP learning after remote address set to: 91.xxx.xxx.xxx:10270
    -- PJSIP/mytrunk-00000001 answered
    -- Executing [faxout@fax_outgoing:1] NoOp("PJSIP/mytrunk-00000001", "**** SENDING FAX ****") in new stack
    -- Executing [faxout@fax_outgoing:2] Wait("PJSIP/mytrunk-00000001", "2") in new stack
    -- Executing [faxout@fax_outgoing:3] Set("PJSIP/mytrunk-00000001", "FAXFILE=Asterisk_SendFax_res_fax.tif") in new stack
    -- Executing [faxout@fax_outgoing:4] SendFAX("PJSIP/mytrunk-00000001", "/var/spool/asterisk/tmp/Asterisk_SendFax_res_fax.tif,dfsz") in new stack
    -- Channel 'PJSIP/mytrunk-00000001' sending FAX:
    --    /var/spool/asterisk/tmp/Asterisk_SendFax_res_fax.tif
       > 0x7f1ac801d020 -- Strict RTP switching to RTP target address 91.xxx.xxx.xxx:10270 as source
       > 0x7f1ac801d020 -- Strict RTP learning complete - Locking on source address 91.xxx.xxx.xxx:10270
[May 29 17:46:21] WARNING[13209][C-00000002]: res_fax.c:2412 sendfax_t38_init: channel 'PJSIP/mytrunk-00000001' refused to negotiate T.38
Channel 'PJSIP/mytrunk-00000001' fax session '1', [ 000.520115 ], stack sent 25 frames (500 ms) of energy.
Channel 'PJSIP/mytrunk-00000001' fax session '1', [ 002.378527 ], channel sent 118 frames (2360 ms) of silence.
Channel 'PJSIP/mytrunk-00000001' fax session '1', [ 004.698080 ], channel sent 116 frames (2320 ms) of energy.
Channel 'PJSIP/mytrunk-00000001' fax session '1', [ 004.760102 ], stack sent 212 frames (4240 ms) of silence.
Channel 'PJSIP/mytrunk-00000001' fax session '1', [ 006.740078 ], stack sent 99 frames (1980 ms) of energy.
Channel 'PJSIP/mytrunk-00000001' fax session '1', [ 006.800081 ], stack sent 3 frames (60 ms) of silence.
Channel 'PJSIP/mytrunk-00000001' fax session '1', [ 007.858076 ], channel sent 158 frames (3160 ms) of silence.
Channel 'PJSIP/mytrunk-00000001' fax session '1', [ 009.720097 ], stack sent 146 frames (2920 ms) of energy.
Channel 'PJSIP/mytrunk-00000001' fax session '1', [ 010.178572 ], channel sent 116 frames (2320 ms) of energy.
Channel 'PJSIP/mytrunk-00000001' fax session '1', [ 013.260088 ], stack sent 177 frames (3540 ms) of silence.
Channel 'PJSIP/mytrunk-00000001' fax session '1', [ 013.358695 ], channel sent 159 frames (3180 ms) of silence.
Channel 'PJSIP/mytrunk-00000001' fax session '1', [ 015.260092 ], stack sent 100 frames (2000 ms) of energy.
Channel 'PJSIP/mytrunk-00000001' fax session '1', [ 015.320163 ], stack sent 3 frames (60 ms) of silence.
Channel 'PJSIP/mytrunk-00000001' fax session '1', [ 015.657857 ], channel sent 115 frames (2300 ms) of energy.
Channel 'PJSIP/mytrunk-00000001' fax session '1', [ 018.240127 ], stack sent 146 frames (2920 ms) of energy.
Channel 'PJSIP/mytrunk-00000001' fax session '1', [ 018.838800 ], channel sent 159 frames (3180 ms) of silence.
Channel 'PJSIP/mytrunk-00000001' fax session '1', [ 021.158374 ], channel sent 116 frames (2320 ms) of energy.
Channel 'PJSIP/mytrunk-00000001' fax session '1', [ 021.220118 ], stack sent 149 frames (2980 ms) of silence.
Channel 'PJSIP/mytrunk-00000001' fax session '1', [ 023.220095 ], stack sent 100 frames (2000 ms) of energy.
Channel 'PJSIP/mytrunk-00000001' fax session '1', [ 023.280089 ], stack sent 3 frames (60 ms) of silence.
Channel 'PJSIP/mytrunk-00000001' fax session '1', [ 024.318631 ], channel sent 158 frames (3160 ms) of silence.
Channel 'PJSIP/mytrunk-00000001' fax session '1', [ 026.200088 ], stack sent 146 frames (2920 ms) of energy.
Channel 'PJSIP/mytrunk-00000001' fax session '1', [ 026.638164 ], channel sent 116 frames (2320 ms) of energy.
Channel 'PJSIP/mytrunk-00000001' fax session '1', [ 029.718265 ], channel sent 154 frames (3080 ms) of silence.
Channel 'PJSIP/mytrunk-00000001' fax session '1', [ 029.740124 ], stack sent 177 frames (3540 ms) of silence.
Channel 'PJSIP/mytrunk-00000001' fax session '1', [ 030.860086 ], stack sent 56 frames (1120 ms) of energy.
Channel 'PJSIP/mytrunk-00000001' fax session '1', [ 030.978659 ], channel sent 63 frames (1260 ms) of energy.
    -- Auto fallthrough, channel 'PJSIP/mytrunk-00000001' status is 'UNKNOWN'
    -- Executing [h@fax_outgoing:1] NoOp("PJSIP/mytrunk-00000001", "** Hung up **") in new stack
    -- Executing [h@fax_outgoing:2] GotoIf("PJSIP/mytrunk-00000001", "0?FaxDLR:hFail") in new stack
    -- Goto (fax_outgoing,h,9)
    -- Executing [h@fax_outgoing:9] NoOp("PJSIP/mytrunk-00000001", "Failed") in new stack
    -- Executing [h@fax_outgoing:10] Set("PJSIP/mytrunk-00000001", "pages=0") in new stack
    -- Executing [h@fax_outgoing:11] Set("PJSIP/mytrunk-00000001", "status=failed") in new stack
    -- Executing [h@fax_outgoing:12] Set("PJSIP/mytrunk-00000001", "statusstr=failed") in new stack
    -- Executing [h@fax_outgoing:13] Set("PJSIP/mytrunk-00000001", "error=Received no response to DCS or TCF") in new stack
    -- Executing [h@fax_outgoing:14] NoOp("PJSIP/mytrunk-00000001", "** Sending Delivery Notification **") in new stack
    -- Executing [h@fax_outgoing:15] NoOp("PJSIP/mytrunk-00000001", "** READY **") in new stack
[May 29 17:46:56] NOTICE[13209][C-00000002]: pbx_spool.c:463 attempt_thread: Call completed to PJSIP/mytrunk/sip:<TOPHONENR>@my.sip-provider.com

After two weeks trying, there is nothing new. I also tried chan_sip in the meanwhile. The behaviour remains exactly the same.

  • First terminal (G711): Always works perfectly
  • Second terminal (G711): Works never. Error: Received no response to DCS or TCF.
  • Third terminal (T38): Negotiation successful, but Asterisk does not start transmitting. Remote end hangs up after 30 seconds of silence.

Has anyone ever got Asterisk with T.38 and G711 on a SIP running successfully?

I got FAX working but with Asterisk forwarding T.38, but not being the Fax endpoint. For the pjsip extension it requires to explicitly enable T.38, e.g. with: “t38_udptl=yes”. Can this be missing?

On the other hand: using G.711 is good on low packet loss communication (which we usually have). T.38 is otherwise save, but relies on more complex implementations to interwork at the T.38 relay.

@cogweg Thanks for the hint. In fact, the config here was one of my first variants, and t38_udptl is set to yes in my current config.

Funny fact aside: This changes only the behaviour if it tries T.38 at all. So, Terminal 1 works with t38_udptl set to yes and to no as well, and all others do not work independent from this setting.

Currently I switch to chan_sip with this config, and it behaves the same:

[general]
transport=udp
t38pt_udptl=yes,redundancy,maxdatagram=40
faxdetect=yes
disallow=all
allow=alaw
allow=gsm
qualify=yes
canreinvite=yes
register=><PHONENR>:<PASSWORD>@my.sipprovider.org:5060
directmedia=yes
bind=<IP>

[mysiptrunk]
type=peer
remotesecret=<PASSWORD>
secret=<PASSWORD>
defaultuser=<PHONENR>
user=<PHONENR>
host=my.sipprovider.org
fromuser=<PHONENR>
fromdomain=my.sipprovider.org
port=5060
canreinvite=yes
insecure=invite,port
qualify=10000
nat=no
context=faxout

Interesting post but doesn’t sound promising. I just installed Asterisk 16.1 with built-in pjsip 2.8. Same exact setup. All I want to do is be able to send an occasional fax since I dropped my POTS line. Does anyone know if there is a cli interface that might allow you to simply type SendAFax “nameoffaxfile” on the Linux command line and “hook” into the dial application? If it could do it even with a script that would be great. One thing I’m worried about is whether or not I have the correct version of spandsp. I got it right from Debian using apt-get but soft-switch.org seemed to be dead and it looks like there could be a bunch of different versions out there. I haven’t tested yet. Need to figure out what extensions.conf should look like. I want to check with my ITSP first to try to get their recommended settings for error correction and the datagram size. I thought I read somewhere that the setting could be critical and must match the ITSP.

Although Asterisk ought to disable direct media if required to support fax, I would try disabling it on both sides. Also note that canreinvite is an obsoilete synonym for directmedia, so you you should replace it by the latter and remove any resulting duplication.

You also have another bit of cut and paste configuration, which may not be needed here, although does not affect your problem namely insecure=port.

Also, setting secret and remotesecret to the same value is pointless, and, if you are using remotesecret anyway, simply removing secret has the same effect as insecure=invite.