Adding SMS sending capability to a rooted Netgear Nighthawk M6 Pro / MR6500
The Problem
Netgear decided to remove the ability to send SMS messages on the Nighthawk M6. Having SMS sending capability is handy, especially for alerting on events such as Internet outages. This workaround implements a simple HTTP server using Perl that can be used to send messages through the device. Perl was chosen begrudgingly because it was the only capable scripting language that comes by default with the device.
Prerequisite
You must have access to the root telnet shell of the device. You can find details at the URL below how to do this. This is outside of the scope of this post and no assistance will be provided.
https://github.com/0xBAADF0OD/netgear_mr6400/tree/main/netgear-AT-commands#enable-root-telnet-port-23
https://sierra-keygen.uu.sg/ (Hint: Device Generation = SDX55)
Disclaimer
This was tested on an AT&T branded Nighthawk M6 Pro / MR6500 running the following software versions:
Firmware Version: NTGX65_10.04.22.01
Modem Version: MPSS.DE.1.0-02593-OLYMPIC_GENALL_PACK-1
GUI Version: MR6500-1A1NAS_05.00.63.00
There is no warranty included with this software so use at your own risk. If you brick your device it's on you.
The code / How to install
- Telnet to the root shell of the M6.
1$ telnet 192.168.1.1
2Trying 192.168.1.1...
3Connected to 192.168.1.1.
4Escape character is '^]'.
5
6QTI Linux reference nogplv3 distro targeting performance builds. fatal: not a git repository (or any of the parent directories): .git sdxlemur
7
8/ #
- Add the below code to a new file named /mnt/userrw/sms_server. Update the $password and $phone_number variables to reflect your desired password and phone number that messages will be sent to.
1#!/usr/bin/perl
2use IO::Socket::INET;
3
4# Automatically get the LAN GW and listen only on that IP
5my $listen_ip = `/usr/bin/dx router.GwIpAddr`;
6
7# TCP port to listen to
8my $listen_port = '767'; #SMS
9
10# Password required to send messages
11my $password = 'password';
12
13# Phone number messages should be sent to
14my $phone_number = '+12223334567';
15
16# Debug Mode - Output AT command status to browser
17my $debug_mode = 1; # 1 = On, 0 = Off
18
19# Create listening socket
20my $server = new IO::Socket::INET (
21 LocalHost => $listen_ip,
22 LocalPort => $listen_port,
23 Proto => 'tcp',
24 Listen => 5,
25 Reuse => 1
26);
27die "Error: Cannot create socket - $!\n" unless $server;
28
29$SIG{INT} = sub { $server->close(); exit 0; };
30
31# Main loop
32while ($client = $server->accept()) {
33 $client->autoflush;
34 my $request = <$client>;
35
36 # Show form if accessing /
37 if ($request =~ m|^GET / HTTP/1.[01]|) {
38 print $client "HTTP/1.0 200 OK\n";
39 print $client "Content-Type: text/html\n\n";
40 print $client "
41 <!DOCTYPE html>
42 <html>
43 <body>
44 <h2>SMS Server</h2>
45 <form action=\"/action\" method=\"get\">
46 <p>Password: <input type=\"password\" id=\"password\" name=\"password\" maxlength=\"64\"></p>
47 Message:<br>
48 <textarea id=\"message\" name=\"message\" rows=\"4\" cols=\"50\" maxlength=\"160\"></textarea><br>
49 <input type=\"submit\" value=\"Submit\">
50 </form>
51 <p>Note: Message length is limited to 160 characters.</p>
52 </body>
53 </html>";
54 } elsif ($request =~ m|^GET /action(.+) HTTP/1.[01]|) {
55 # Process input for /action
56 print $client "HTTP/1.0 200 OK\n";
57 print $client "Content-Type: text/plain\n\n";
58
59 # Get password and message from GET
60 my ($password_arg) = $request =~ /password=([^&\s]*)/;
61 my ($message_arg) = $request =~ /message=([^&\s]*)/;
62
63 # URLDecode input
64 $password_arg =~ s/\+/ /g;
65 $password_arg =~ s/%([A-Fa-f\d]{2})/chr hex $1/eg;
66 $message_arg =~ s/\+/ /g;
67 $message_arg =~ s/%([A-Fa-f\d]{2})/chr hex $1/eg;
68
69 if ($password_arg eq $password) {
70 if (length($message_arg) <= 160) {
71 if (open my $FH, '+<', '/dev/smd8') {
72 $FH->autoflush;
73 print $FH "\r";
74 print $FH "AT+CMGF=1\r";
75 sleep(0.1);
76 print $FH "AT+CMGS=\"$phone_number\"\r";
77 sleep(0.1);
78 print $FH "$message_arg";
79 sleep(0.1);
80 print $FH "\x1a";
81
82 if ($debug_mode) {
83 print $client "Debug log:\n";
84 }
85
86 while(my $response = <$FH>) {
87 if ($debug_mode) {
88 print $client $response;
89 }
90
91 if ($response =~ m/\+CMGS: /) {
92 print $client "Status: Message sent.\n";
93 last;
94 }
95
96 if ($response =~ m/ERROR/) {
97 print $client "Status: Error sending message: $response\n";
98 last;
99 }
100 }
101 close($FH);
102 } else {
103 print $client "Status: Error - Unable to open /dev/smd8.\n";
104 }
105 } else {
106 print $client "Status: Error - Message is longer than 160 characters.\n";
107 }
108 } else {
109 # Password does not match
110 print $client "Status: Error - Access denied.\n";
111 }
112 } else {
113 # Error if not accessing / or /action
114 print $client "HTTP/1.0 400 BAD REQUEST\n";
115 print $client "Content-Type: text/plain\n\n";
116 print $client "Status: Error - Bad request.\n";
117 }
118 close $client;
119}
- Make the sms_server file executable:
1/ # chmod +x /mnt/userrw/sms_server
2/ # ls -l /mnt/userrw/sms_server
3-rwxr-xr-x 1 root root 3351 Dec 19 17:12 /mnt/userrw/sms_server
- Add the following to the bottom of /etc/init/early_init.sh:
1/mnt/userrw/sms_server &
Make sure to add the & so the server will launch and the init script can exit.
-
Reboot the device.
-
Once it reboots you can go to the following address:
http://[ip of your M6 Pro]:767
Enter the password you provided in the sms_server script and a message (limited to 160 characters) and click submit. You should receive a SMS on the phone number you defined in the script. If not, by default the log of the communication with the modem built into the M6 will show and provide insight into why it didn't work.
Example using curl:
1$ curl "http://192.168.1.1:767/action?password=password&message=hello+world"
2Debug log:
3AT+CMGF=1
4OK
5AT+CMGS="+12223334567"
6> hello world
7
8+CMGS: 70
9Status: Message sent.
You may also be interested in the 3D printable wall mount I designed for the M6 Pro. You can download that here:
https://www.printables.com/model/686317-netgear-nighthawk-m6-pro-wall-mount