mpg123 -w
; convert the output wav file to format that Asterisk can recognize
sox -v 0.9 input.wav -c 1 -r 8000 output.wav
; -v: reduce volume
|
# apt-get install bluez-utils bluez-hcidump libbluetooth-devthen, go to your Asterisk source directory and use make menuselect to enable chan_mobile. It's in Add-ons -> chan_mobile:
# cd /usr/src/asterisk-1.8.11.0 # ./configure && make menuselectWhilst it compiled and installed OK, I had to make a modification to the chan_mobile source before it would recognise my phone:
# vi /usr/src/asterisk-1.8.11.0/addons/chan_mobile.c Find this: addr.rc_channel = (uint8_t) 1; Replace with: addr.rc_channel = (uint8_t) 0;Build Asterisk and (re)install:
# make && make installIn order to use a bluetooth-connected phone as a GSM gateway, it's necessary to pair the phone with the Asterisk server. In Debian, this can be accomplished painlessly through the CLI. First, make your phone discoverable and then scan for it:
# hcitool scan Scanning ... EC:1B:6B:64:C2:88 TrollphoneMake a note of the MAC address. In order to pair, a helper is required to handle the PIN. Run the helper in the background and begin the pairing process:
# bluetooth-agent 7472 & # rfcomm connect hci0 EC:1B:6B:64:C2:88Once the pairing has succeeded, make sure your phone is configured to automatically accept connections for this paring in future. You can verify that the paring is working at any time by running:
# hcitool con Connections: < ACL EC:1B:6B:64:C2:88 handle 41 state 1 lm MASTER AUTH ENCRYPTNow, Asterisk needs to be configured to use the paired phone. We need to know which rfcomm channel offers the voice service. The easiest way is to use chan_mobile:
# rasterisk *CLI> module load chan_mobile.soDon't worry about any errors loading the module, it'll do for now:
*CLI> mobile search EC:1B:6B:64:C2:88 Trollphone Yes Phone 2In this case it is rfcomm channel 2. In addition, we need to know the MAC address of the bluetooth dongle installed in the Asterisk server. Exit the Asterisk CLI and use hcitool:
# hcitool dev Devices: hci0 00:81:C5:33:25:A4At last we have all the information needed. Edit or create the chan_mobile configuration file:
# vi /etc/asterisk/chan_mobile.conf [Adapter] address = 00:81:C5:33:25:A4 id = pabx [Trollphone] address = EC:1B:6B:64:C2:88 port = 2 context = from-trollphone adapter = pabxYou will need something in the dialplan to handle this, at minimum something like:
# vi /etc/asterisk/extensions.conf [from-trollphone] exten => s,1,Dial(SIP/100) [my-phones] exten => *12,1,Dial(MOBILE/Trollphone/150)When the mobile rings, you should get a call on SIP extension 100. Dialling *12 will cause the phone to dial 150, which in my case gives me Orange customer services. I'm sure you get the idea.
[from-trollphone] exten => sms,1,Verbose(Incoming SMS from ${SMSSRC} ${SMSTXT}) exten => sms,n,System(echo "To: stocksy@stocksy.co.uk" > /tmp/smsmail) exten => sms,n,System(echo "Subject: SMS from ${SMSSRC}" >> /tmp/smsmail) exten => sms,n,System(echo "${SMSTXT}" >> /tmp/smsmail) exten => sms,n,System(sendmail -t -f ${SMSSRC}@sms.stocksy.co.uk < /tmp/smsmail) exten => sms,n,Hangup()At first, incoming messages were all arriving with a blank ${SMSSRC}, the easy solution was to apply a patch and re-compile:
# cd /usr/src/asterisk-1.8* # wget --no-check-certificate https://issues.asterisk.org/jira/secure/attachment/42026/sms-sender-fix.diff # patch -p0 < sms-sender-fix.diff # ./configure && make && make installNow, incoming messages are delivered to me as emails claiming to be from +MOBILENUMBER@sms.stocksy.co.uk. Obviously, this requires the Asterisk system to have a working MTA, the setup of which I won't cover here. If you don't have an MTA at present, take a look at postfix.
#!/usr/bin/env python # (:? YOUR SCRIPT IS BAD AND YOU SHOULD FEEL BAD! (:? # I'M NOT A DEVELOPER AND THIS IS PROBABLY VERY, VERY BAD, but it does work. # email2sms.py James Stocks # based upon emailspeak.py by sysadminman - http://sysadminman.net # v0.0 2012-04-28 # Import libs we need import sys, time, email, email.Message, email.Errors, email.Utils, smtplib, os, socket, random, re from datetime import date from email.Iterators import typed_subpart_iterator from time import sleep # Asterisk Manager connection details HOST = '127.0.0.1' PORT = 5038 # Asterisk Manager username and password USER = 'your-ast-man-user' SECRET = 'dysmsdvsa' # Generate a random number as a string. We'll use this for file names later on callnum = str(random.randint(1, 100000000)) # Taken from here, with thanks - # http://ginstrom.com/scribbles/2007/11/19/parsing-multilingual- # email-with-python/ def get_charset(message, default="ascii"): """Get the message charset""" if message.get_content_charset(): return message.get_content_charset() if message.get_charset(): return message.get_charset() return default # Taken from here, with thanks - # http://ginstrom.com/scribbles/2007/11/19/parsing-multilingual- # email-with-python/ def get_body(message): """Get the body of the email message""" if message.is_multipart(): #get the plain text version only text_parts = [part for part in typed_subpart_iterator(message, 'text', 'plain')] body = [] for part in text_parts: charset = get_charset(part, get_charset(message)) body.append(unicode(part.get_payload(decode=True), charset, "replace")) return u"\n".join(body).strip() else: # if it is not multipart, the payload will be a string # representing the message body body = unicode(message.get_payload(decode=True), get_charset(message), "replace") return body.strip() # Read the e-mail message that has been piped to us by Postfix raw_msg = sys.stdin.read() emailmsg = email.message_from_string(raw_msg) # Extract database Fields from mail msgfrom = emailmsg['From'] msgto = emailmsg['To'] msgsubj = emailmsg['Subject'] msgbody = get_body(emailmsg) # Find the part of the 'To' field that is the phone number phonenum = re.match( r'\+?([0-9]+)', msgto, re.M) # Whose mobile is this? mobile = sys.argv[1] # Write a log file in /tmp with a record of the e-mails currtime = date.today().strftime("%B %d, %Y") logfile = open('/tmp/email2sms.log', 'a') logfile.write(currtime + "\n") logfile.write("Call Number: " + callnum + "\n") logfile.write("From: " + msgfrom + "\n") logfile.write("To: " + msgto + "\n") logfile.write("Subject: " + msgsubj + "\n") logfile.write("Body: " + msgbody + "\n\n") logfile.close() # Send the call details to the Asterisk manager interface s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((HOST, PORT)) sleep(1) s.send('Action: login\r\n') s.send('Username: ' + USER + '\r\n') s.send('Secret: ' + SECRET + '\r\n\r\n') sleep(1) s.send('Action: originate\r\n') # Dummy channel - I don't actually want any phones to ring s.send('Channel: LOCAL/1@sms-dummy\r\n') s.send('Context: mobiles\r\n') s.send('Exten: ' + mobile + '\r\n') s.send('WaitTime: 30\r\n') # This is a bogus value, but the field is required s.send('CallerId: 5555\r\n') # Do not wait for response from dummy channel s.send('Async: true\r\n') s.send('Priority: 1\r\n') # The variables ${SMSTO} and ${SMSBODY} are used in the dialplan s.send('Variable: SMSTO=' + phonenum.group(1) + ',SMSBODY=\"' + msgbody + '\"\r\n\r\n') sleep(1) s.send('Action: Logoff\r\n\r\n') #Omitting this causes "ast_careful_fwrite: fwrite() returned error: Broken pipe" sleep(3) s.close()Copy the above script to /usr/sbin/email2sms.py and make executable:
# chmod +x /usr/sbin/email2sms.pyThe script uses the Asterisk Manager Interface, so it will need an AMI user. Append this to
manager.conf
:
# vi /etc/asterisk/manager.conf [your-ast-man-user] secret=dysmsdvsa read=call,user,originate write=call,user,originateand also make sure it is enabled in the general section:
# vi /etc/asterisk/manager.conf [general] enabled = yes webenabled = yes port = 5038You'll note that I'm using the context 'mobiles'. You'll need to make sure that the extensions you'll be using exist in this context in extensions.conf:
# vi /etc/asterisk/extensions.conf exten => stocksy,1,MobileSendSMS(JS6021,${SMSTO},${SMSBODY}) exten => karen,1,MobileSendSMS(trollphone,${SMSTO},${SMSBODY})Secondly, there is a dummy extension which the 'call' needs to connect to. A NoOp isn't quite sufficient, I could only get it to work if the extension answered and then did something, in this case answer and wait 10 seconds:
# vi /etc/asterisk/extensions.conf [sms-dummy] exten => 1,1,Answer() exten => 1,n,Wait(10) exten => 1,n,HangupReload Asterisk to pick up the changes.
# vi /etc/postfix/master.cf sms-stocksy unix - n n - - pipe flags=FR user=stocksy argv=/usr/sbin/email2sms.py stocksy sms-karen unix - n n - - pipe flags=FR user=stocksy argv=/usr/sbin/email2sms.py karenpostfix needs to know that it must use these transports for SMS domains:
# vi /etc/postfix/transport ; postmap /etc/postfix/transport sms.stocksy.co.uk sms-stocksy sms.herdomain.co.uk sms-karenIf postfix doesn't already have a transport_maps setting, create one. Obviously this could break any existing postfix setup you might have, but if so I'm expecting you to know what you're doing:
# postconf -e transport_maps=hash:/etc/postfix/transportRestart postfix and that should be all that's necessary.
# /etc/init.d/postfix restartYou need to satisfy yourself that you are not allowing the entire world to relay through your SMS gateway! Understand and make use of postfix's security features! Don't wait until you've racked up a collosal SMS bill! Loud noises!
# tail -f /var/log/mail.logYou can do a packet trace to see what's happening on the Asterisk Manager Interface:
# tcpdump -A -i lo port 5038Try talking to the AMI directly:
$ nc localhost 5038 Action: login Username: your-ast-man-user Secret: dysmsdvsa Action: originate Channel: LOCAL/1@sms-dummy Context: mobiles Exten: stocksy WaitTime: 30 CallerId: 5555 Async: true Priority: 1 Variable: SMSTO=5555555555,SMSBODY="foo" Action: LogoffWatch out for whitespace in the AMI - exten 'stocksy' != 'stocksy '.
#!/usr/bin/env ruby require "socket" s = UDPSocket.new s.bind("0.0.0.0", 5060) while true packet = s.recvfrom(1024) via = packet[0].match(/Via: (.+);rport/)[1] from = packet[0].match(/From: (.+)/)[1] to = packet[0].match(/To: (.+)/)[1] call_id = packet[0].match(/Call-ID: (.+)/)[1] cseq = packet[0].match(/CSeq: (\d+) REGISTER/)[1] remote_ip = packet[1][3] remote_port = packet[1][1].to_i puts packet.inspect if packet[0].match(/^REGISTER /) ret = "SIP/2.0 200 OK\r\n" + "Via: #{via};received=#{remote_ip}\r\n" + "From: #{from}\r\n" + "To: #{to}\r\n" + "Call-ID: #{call_id}\r\n" + "CSeq: #{cseq.to_i + 1} REGISTER\r\n" + "\r\n" puts "sending to #{remote_ip}:#{remote_port}:\n#{ret}" s.send(ret, 0, remote_ip, remote_port) end end
You can insert your own server’s IP address in the s.bind parameters if you want, but 0.0.0.0 should just bind to all interfaces. Also remember to make the file executable, for example using the Linux command chmod 755 spoof_sip_ok
user@asterisk:~ $ netstat -lunp Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name udp 0 0 0.0.0.0:5060 0.0.0.0:* 16301/asterisk
wget -r -Nc -mk http://bacutin.blogspot.com/-r Turn on recursive retrieving
A skeletal model for the client-proxy-server systemClientThe client should do the following things:
ProxyThe proxy, being basically both a kind of a server and a client, should do the following things:
ServerThe server should do the following things:
Additional notesAfter implementing the basic model described above, you can easily start adding more features. For example, you could add a sleep in the proxy between steps 3 and 4 and instead of directly sending the data received from the client to the server, you could read the file and send the contents - this way you can manually change the file before it gets sent to the server.I also suggest you read Beej's Guide to Network Programming if you want to really learn the basics of network programming. The examples are in C, but the basic principles also apply in Python (and other languages as well). |