My phone rings, I glance at it and contemplate answering. It rings a second time and I reach for the handset, then, just as suddenly as it began the ringing stopped and I was left in peace. I smiled to myself and continued what I was doing. No robocalls today!
Bringing my phone into the Matrix
Those of you who are regular followers of this blog will know that I have fully embraced Matrix as my chat platform of choice. While Matrix is a fantastic standalone product that offers exceptional features like federation, end-to-end encryption and the ability to self-host; It’s true shining star is in the ability to add bridges to other chat systems. This means that it becomes theoretically possible to use Matrix to aggregate any number of disparate chat protocols into a single pane of glass. One chat to rule them all.
By the time I had finished my blog post describing my use case of Matrix I was already happily using it to communicate via Facebook messenger, multiple Slack workspaces, Signal and IRC. Despite my successes though, one thing remained elusive – SMS or traditional text messaging.
My initial goal with adopting Matrix was to totally eliminate the large number of disparate chat applications that were taking over my everyday life. After that was largely successful I realized that this new approach to messaging meant I could more easily switch between phones, such as when traveling, as all of my messaging history was accessible under a single login. The one outlier in all of this was of course SMS, and I became obsessed with integrating traditional SMS messaging into Matrix as well.
A swing and a miss
The most obvious solution to my problem was to port my cell phone number to a VoIP provider which supported an SMS API. I could then either find or write a bridge to integrate that API into Matrix. While this solution was solid it came with one big caveat – VoIP providers are generally treated as second-class citizens in the world of telecommunications. This problem is likely familiar to anyone who has tried to use a Google Voice number to sign up for a service like Twitter which unhelpfully returns an error message saying the number is invalid. Generally a VoIP number with any service that uses SMS verification is going to be hit-and-miss as to if your number will actually be accepted. Knowing this and wishing to avoid any potential future snafus I decided it was in my best interest to keep my phone number tied to a physical SIM card and real cellular provider.
I had already studied the list of Matrix Bridges and I already knew there was a suitable bridge available, assuming I could find hardware that would allow me to communicate in the Gammu format, Thus begun my quest to search for compatible hardware for my new venture.
Like most people, I have a single mobile number on which I receive both voice calls and text messages. Not wanting to subject my contacts to multiple phone numbers I focused my search loking for gatays which could support both SMS and voice call termination. Eventually I landed upon the Yeastar TG100 which, with both SMS and SIP support along with a documented API seemed like the perfect solution!
Once the device arrived I excitedly opened it up and was impressed with the build quality. This looked like the sort of device that belonged in an industrial setting and it seemed like it would have actual durability.
Unfortunately the good times came to a screeching halt after I discovered that the gateway only supported 2G connections, something that had been phased out a number of years ago in the North American market. An email to the manufacturer confirmed this device was essentially useless for my location. I had acquired a $300 paperweight which still sits on my windowsill as a reminder to always check the specs before ordering anything.
Time to look for new hardware
While the expensive paperweight might be enough to deter most people, I chocked it up to experience and began the search for something that might work better. After what amounted to literal days of investigation and scrolling though pages upon pages of hardware which I can only assume was purpose built for sending SMS-based spam, I eventually discovered that the device I wanted didn’t exist. There simply did not seem to exist a device which would handle SMS via an API and voice calls via SIP that would work with LTE in the North American market.
I was perplexed; how, in this modern age, could I have a use case that has not been filled by a manufacturer. Clearly I needed to get more creative, and inspiration struck as I looked at my parent’s analog phone line!
A few years ago, right when LTE gateways began gaining traction as alternative Internet source for rural areas, a flood of reasonably inexpensive hardware appeared on the market. All of these gateways were designed to provide Internet service, but some also supported SMS via a web interface and some even had an RJ11 port to plug, you guessed it, an analog phone into. I theorized that I should be able to get one of these devices, scrape the web interface for the SMS content and use the RJ11 jack with a reverse-ATA to connect to my SIP server. It turns out what I thought was most difficult work had already been done for me; an API to send and receive SMS messages from Huawei devices already existed on Github!
Eventually, I found a compatible device with an RJ11 jack in the Huawei B315. To convert the analog line back to SIP I identified a Grandstream HT813. The end result was a monstrosity that should never have existed, but you know what with a little bit of tweaking it actually worked! Digital-to-Analog-to-Digital!
In this configuration, I built a Python script which interfaced with the B315 using the API I found on Github. Received messages were dumped into the sms-bridge watch folder, and the script would pick up any outgoing messages in the bridge outbox. The matrix-sms bridge would handle message sin both folders, and they would then magically appear in my chat client.
On the voice side, when a call came in, the B315 would “ring” the analog phone line and the 813 would pick up the call after two “rings” and transfer it to my VoIP server. My server would then handle the incoming SIP call and ring my desk phone or my real cell phone depending on the configuration. The fact this setup even works is kinda ridiculous and I loved it.
After some basic validation I began using this system in my day-to-day life and, for the most part, everything worked as expected. Then one day something strange happened: my cell phone rang with an unknown number, but before I could answer it the call was disconnected. I thought the situation was strange, but shrugged it off as an anomaly. Then it happened again. And again. And again. Clearly something was going on.
As I puzzled over the strange occurrences it struck me that I hadn’t received spam calls in weeks, but I was still receiving calls from real people. As impossible as it seemed, the constant hang-up phone calls must have been the spam calls that I used receive on a regular basis prior to building out my new phone system.
I still don’t really understand why it works, but I’m more then happy to let my phone ring a couple of times before answering since it effectively weeds out the spam callers. The best explination I can propose is that the robocalls get tripped up when the analog call is picked up by the Grandstream device, when this occurs you can hear a barely perceptible click, and then the line immediately begins ringing again. This barely perceptible noise, coupled with the fact that the call was now successfully terminated must be enough to trip up the robodialers and have them end the call. In this case, I figured I would take the easy win.
MMS – the complicating factor
Everything was running along swimmingly; I was sending and receiving texts, answering and making phone calls and living a spam-free life to boot when all of a sudden the good times came to a screeching halt after I suddenly stopped receiving texts. I launched into troubleshooting mode and eventually discovered that the Huawei API script was crashing, but why? Turns out someone had sent me a picture message and the API script not only failed to handle the image, but then completely died.
I opened a ticket with the developer, but after it went unanswered for months like any good hacker I started to rip the code apart to try and figure out what the issue was and even managed to implement a horrible awful work-around which stopped the program from crashing. My ticket still has gone unanswered.
So, now I had fixed the immediate issue where one could essentially DOS my ability to receive SMS, but it didn’t solve the issue of actually being able to RECEIVE picture messages – something, as it turns out, is actually pretty common. Thus I began a journey of discovery into how MMS or picture messaging actually works.
Learning more about MMS then I ever wanted to
MMS stands for Multimedia Messaging Service and is an extension to the traditional SMS or text messages which are ubiquitous on modern phones. It was commercialized back in 2002 and since then has become indistinguishable from regular text messages (at least for users in North America). So, how do MMS messages actually work? Well it turns out that MMS really is a literal bolt-on to SMS. When a user sends an MMS message, the message is encoded into a special format and sent to a special type of store-and-forward server operated by the user’s carrier called an MMSC. This is one reason why you see so many threads of confused people on carrier support forums complaining of pictures messages not being delivered – they likely didn’t have the correct MMSC server configured on their phone.
Once the MMS message is delivered to the MMSC, a special type of SMS message is sent to the recipient’s phone which contains, among other things, a URL to retrieve the MMS content. The recipient phone needs to be able to parse the SMS control message, extract the MMS URL, download the content and then display it to the user. To be honest the whole thing seemed quite hacky, but considering the standard was developed in the early 2000’s before mobile internet it made sense.
Trying to download MMS messages
So now that I understood the MMS flow, I needed to be able to actually handle the MMS content. Digging into the code of the Huawei API project, I managed to rip apart enough of the message handling in order to get what I thought was the control message. Unfortunately, I was never able to extract more then what looked to be a partial URL – I presumed it was the filename which would appear after the server address. Try as I might, I was never able to get enough useful information from the message content to effectively download the MMS payload. As best I could tell, the Huawei device itself was consuming the SMS control message and only exposing a portion of the actual message. Buggy Huawei software? Who would have thought it!
Time to put my thinking cap on. I had what likely amounted to the MMS filename, but no idea what the server was which stored the file. I tried using my carrier’s documented MMSC URL, but only ever received a 404. Clearly my carrier was using a different address to expose received messages vs sent messages. I needed some way of intercepting the SMS control message BEFORE the Huawei device screwed it all up!
After several days of searching, and scrolling though more purpose-built spam-devices, I eventually came across NowSMS (https://nowsms.com/productinfo/now-sms-mms-gateway) which reportedly supported both sending and receiving MMS messages. The software works in one of two modes – either with a USB modem, or paired with a companion app on an Android device. MMS messages are received my the Android device and sent to the NowSMS server over the network. I grabbed a trial version of the software and installed the companion app on one of my spare phones and plugged my target SIM card into the phone. I then proceeded to spam myself with MMS messages, all the while closely examining the NowSMS debug logs. Eventually, I found it! The full URL path to download the MMS payload! Progress!
I re-installed my SIM into the B315 and went back to my hacked together Huawei API. Using the code I’d put together previously, I extracted one of the supposed file names and combined it with the server name I identified using NowSMS. I crossed my fingers and attempted to download the file, and wouldn’t you know it the damn thing worked! Holy shit! I was actually able to send myself messages and reliably download them!!!!
Decoding MMS messages
If you’ve followed along thus far, you’ll know that I was now able to download MMS payloads from my carrier’s server, however, these payloads were, at a glance, useless since I was unable to open them in any application I tried. Clearly, I managed to download the raw MMS payload, but I needed to convert it into some type of usable format. hmmmm.
It turns out there is not a lot of documentation on how MMS messages are actually built. I was close to giving up when I stumbled upon a git repo created by Jonatan Heyman as a class project all the way back in 2003. The MMS decoder repo hadn’t been touched in 14 years, but I figured there was a decent change that the MMS standard hadn’t been touched in about the same amount of time. Several PHP dependencies and one shell script later I had successfully parsed my first MMS message, and it even contained the original image I sent to myself. Hallelujah! Thanks Jonatan!
Putting it all together
I now had all the building blocks to receive MMS messages and just needed to put it all together. Not wanting to even try to re-implement Jonatan’s PHP code in Python I settled on building myself a super hacky shell script which my Python application called upon receiving an MMS. The script essentially parsed the filename out of the received SMS control message, downloaded the payload from the MMS server, called Jonatan’s PHP script to decode the file, uploaded the decoded message content to a webserver and sent me a notification with a URL to download it. The entire thing was, by all accounts, an awful mess but it worked and that was good enough for me. I still wasn’t able to send MMS messages, but that was fine – I could send people links to pictures like a respectable human being.
I used this ghetto-solution for months, and for the most part everything worked as expected. Voice calls were handled reliably and would ring both my desk phone and a softphone SIP client on my real cellphone which had been converted to a data-only plan. Text messages, both sending and receiving, worked reliably through Schildi-Chat which was my mobile Matrix client of choice and even received MMS messages seemed to work with my hacky MMS-download script. For the most part, I had minimal issues, even while using SIP over my carrier’s mobile data network – something that surprised the hell out of me! I’d still be using the setup, but for the simple fact that one day MMS messaged stopped working. I was still getting the notifications, but the download step was failing – likely because my carrier had changed their MMS server URL.
At this point, I was at a crossroads; while I could break out NowSMS again and determine the new download URL, it was becoming evident that this solution would require ongoing maintenance and tuning – something I wasn’t willing to commit time to. By this time I had stumbled across the SMSEagle – which is an ungodly expensive piece of hardware that can handle SMS, MMS and voice call forwarding. I made the call to retire my complicated ghetto solution and purchase the NXS-97004G and installed it in the rack with my phone server.
While it was initially painful to part with such a large chunk of money for a fairly basic device, I have to say it was the correct call overall. A little bit of API work to handle incoming and outgoing messages from Matrix and I have been reliably receiving all the MMS messages I could handle, along with sending and receiving of SMS.
Voice calls are not handled by the device, but it does support call forwarding, so I can simply forward calls for my mobile number directly to my VoIP system and handle them however I please, usually by ringing the softphone on my handset.
It was a long and expensive journey, but I learned a lot about the messaging protocols we all take for granted on a daily basis and ended up with a bunch of surplus hardware I’m sure I’ll find a use for some day. In the end, I managed to achieve my goal of integrating all the common communication platforms I use into a single app which I ultimately control and that, dear readers, is the ultimate win.