Fax2VoIP2eMail2Fax

Sending faxes over VoIP seems to be rather problematic. Most of the CODECs are optimized for voice, not for the frequencies required by fax machines. The solid answer seems to be time upon time that faxes simply does not work over VoIP. I can confirm this – especially when using the g729 codec as required by most of the carrier grade VoIP providers for which ULS is a reseller. Yet, many people still have stacks of pages which they don’t first want to scan and then send off to a email2fax service, so what to do?

As it turns out, some SIP gateways can detect when they’ve got a fax machine connected to them and immediately switches the channel to a more appropriate codec, which allows for transmitting the tones required for fax. Not to mention that in many cases there are asterisk servers available with the appropriate TDM kit installed in them (the case in my office). So is there any way to exploit this? It’s been bugging me a while now but I believe the solution exists. So the hack I’ll be describing here is what I’m using at the moment to make faxes work on a trixbox server, using either a SIP gateway to connect to the asterisk process, or directly via a FXS port on my TDM800P card.

The technologies

This is actually quite simple, we use some mechanism to provide an FXS (signalling port) to the analog fax machine, and then some VoIP channel to a small asterisk application that stores the fax in a file, and then transmits it via email2fax. Thus the Fax2VoIP2eMail2Fax title. So basically we have:

  1. A fax machine (analog, requiring an FXS port)
  2. Optionally a SIP gateway that connects the fax machine to the asterisk server via a SIP channel.
  3. Alternatively, a FXS card (either sangoma or digium should do just fine) running as part of the asterisk server.
  4. A small context that receives the fax, temporarily stores it in a .tif file and then uses an external process to send the email off to an email2fax service (optionally CC’ing some other address if we want to).

This blog will not be concerned with 1 as that is a well established technology, nor will we be concerned with 2 or 3 except to set the initial context for calls coming in from the fax machine.

email2fax

The specific email2fax service that I use require me to send the destination number in it’s full international (excluding leading zeroes) format. Eg, 0123466657 will be sent as 27123466657@theirdomain.co.za. It also requires me to send with a specific return path in order to actually have the fax delivered. Yes, I can spot a few security holes with this, and quite frankly – I can probably collect these almost as fast as ADSL usernames and passwords – but those is the topics for other blogs.

Asterisk RxFAX

Asterisk has an RxFAX application built into the system. This is usually (or Trixbox/FreePBX at least) used to detect fax machines on incoming calls and to redirect the call to a different trunk, which then eventually sends the fax as an email to some fixed recipient anyway.

The documentation for RxFAX says the following:

-= Info about application ‘RxFAX’ =-

[Synopsis]
Receive a FAX to a file

[Description]
RxFAX(filename[|caller][|debug]): Receives a FAX from the channel into the
given filename. If the file exists it will be overwritten. The file should be in TIFF/F format.
The “caller” option makes the application behave as a calling machine, rather than the answering machine. The default behaviour is to behave as an answering machine.
Uses LOCALSTATIONID to identify itself to the remote end.
LOCALHEADERINFO to generate a header line on each page.
Sets REMOTESTATIONID to the sender CSID.
FAXPAGES to the number of pages received.
FAXBITRATE to the transmition rate.
FAXRESOLUTION to the resolution.
Returns -1 when the user hangs up.
Returns 0 otherwise.

I’m not too worried about the details, the only thing that I absolutely need to pass to the application is a filename. The application is built upon spandsp available from http://www.soft-switch.org/, where app_rtxfax supposedly also lives, however, I could only locate the sources from http://www.voip-info.org/wiki/view/app_rxfax+and+app_txfax which directed me to http://sourceforge.net/project/showfiles.php?group_id=209138.

The faxmachine context

In order to accommodate fax machines, I’m going to create a faxmachine context in extensions.conf (extensions_custom.conf if you’re using some freepbx derivative):

[faxmachine]
; rewrite numbers starting with 0 to start with 27
exten => _0Z.,1,NoOP(Rewriting nationally formatted number: ${EXTEN})
exten => _0Z.,n,Goto(27${EXTEN:1},1)

; strip off leading double-zeroes which would indicate international dialing
exten => _00Z.,1,NoOP(Rewriting internationally formatted number: ${EXTEN})
exten => _00Z.,n,Goto(${EXTEN:2},1)

; Normal fax procedure, first line just prints some diagnostics.
exten => _Z.,1,NoOP(Fax headed out to ${EXTEN})
exten => _Z.,n,Set(FAXFNAME=/var/lib/asterisk/fax/${username}-${EXTEN}-${UNIQUEID}.tif)
exten => _Z.,n,Set(FAXDST=${EXTEN})
exten => _Z.,n,Set(LOCALSTATIONID=ULS VoIP Fax Service)
; Actually answer the line and receive the fax (the fax machine will hang up)
exten => _Z.,n,Answer()
exten => _Z.,n,RxFAX(${FAXFNAME})

; Once the sender has hung up.
exten => h,1,System(/usr/local/sbin/uls_send_fax ${faxfrom} ${faxdomain} ${FAXDST} ${FAXFNAME})

; Invalid/Timed out calls comes here
exten => i,1,Hangup()
exten => t,1,Hangup()

That seems almost too simple, and is really all that is required. The rest of the magic will be worked from the uls_send_fax script. Maybe a quick rundown is in order though. The first two blocks performs some rewriting of the target extension. This results in rewriting national numbers to start with “27” instead of “0”, so “012…” becomes “2712…”, and any number starting with “00” just gets it stripped of, eg “0027…” just becomes “27…”. This is results in us always using the internationally formatted number. The working extension now works with Z. as none of the legal numbers we have can now possibly begin with a zero, and if it does, I’d rather just hang it up than even attempt to figure out what to do with it.

The working bit itself simply answers the “line”, then creates a filename (FAXFNAME) and stores the extension in FAXDST for later re-use. Specifically the uls_send_fax script will pick up on these variables and use them to construct the email that’s to be sent out. The filename is constructed from the incoming username (see the section below on how to set it), the destination number and the call unique id.

The uls_send_fax script

Finally, the last piece to the puzzle, the script used to mail the file to the email2fax service. Here we go (store this in /usr/local/sbin/ as uls_send_fax):

#! /bin/bash

fromaddr=$1
faxdomain=$2
faxnumber=$3
faxfile=$4

[ -r “${faxfile}” ] || exit 1

(
echo “From: ULS VoIP Fax <${fromaddr}>”
echo “To: Fax2Email <${faxnumber}@${faxdomain}>”
echo “Subject: ULS VoIP Fax to ${faxnumber}”
echo “”
uuencode “${faxfile}” “$(basename “${faxfile}”)”
) | /usr/sbin/sendmail -f “${fromaddr}” “${faxnumber}@${faxdomain}”

rm “${faxfile}”

The “friendly” names are really only there for anti-spam filters, configuring sendmail is up to you. You must be able to send mail from the system you’re going to be running this on.

Please note that you need the sharutils package installed for this script to function.

How to use this

Simply set the context of the user to “faxmachine”, example of a SIP/IAX2 user:

[fax]
type=user
context=faxmachine
secret=somethingsecure
host=dynamic
qualify=yes
setvar=username=jkroon
setvar=faxfrom=jaco@uls.co.za
setvar=faxdomain=email2faxdomain.co.za

I can find no equivalent for zaptel channels on the setvar above, thus we need an intermediate context to actually set up these variables, for example (extensions.conf, or extensions_custom.conf for freepbx based systems):

[faxmachine-uls]
exten => _0X.,1,Set(username=uls)
exten => _0X.,n,Set(faxfrom=jaco@uls.co.za)
exten => _0X.,n,Set(faxdomain=email2faxdomain.co.za)
exten => _0X.,n,Goto(faxmachine,${EXTEN},1)

And then just put the specific ZAP channel into that context (eg, channel 1, in zapata.conf):

context = faxmachine-uls
channel => 1

And that concludes this hack.

Comments are closed.