HTTP REST magic for IPython

  • ipython-restmagic
  • Usage examples
    • %rest usage example
    • Django login form
    • GitHub API usage example
    • Ethereum JSON RPC usage example
    • Sending SMS with HiLink API

Sending SMS with HiLink API¶

HiLink API is available for some of 3G/LTE modems and routers.

This is example of how it was used on a particular modem, for other devices it will work differently.

Some of the error API endpoints returns:

  • 100002 – SYSTEM NO SUPPORT

  • 100003 – SYSTEM NO RIGHTS

  • 100004 – SYSTEM BUSY

  • 108001 – LOGIN USERNAME WRONG

  • 108002 – LOGIN PASSWORD WRONG

  • 108003 – LOGIN ALREADY LOGIN

  • 108006 – LOGIN USERNAME PWD WRONG

  • 108007 – LOGIN USERNAME PWD ORERRUN

  • 120001 – VOICE BUSY

  • 125001 – WRONG TOKEN

  • 125002 – WRONG SESSION

  • 125003 – WRONG SESSION TOKEN

import re
%load_ext restmagic

Set device address:

%rest_root http://192.168.8.1
Requests defaults are set.

Start the session. SessionID cookie is required to use device functions:

%rest_session
%rest -q /
New session started.
<Response [200]>

Get the config:

r = %rest -q /config/global/config.xml
re.findall(r'^.*login.*$', r.text, re.MULTILINE)
[u'<login>1</login>']

login == 1 means that login is required to use device functions. Auth token is required to login, let’s get that token first:

r = %rest -v /api/webserver/SesTokInfo
token = re.search('<TokInfo>(.*)</TokInfo>', r.text).group(1) 
print("Token is: " + token)
< GET /api/webserver/SesTokInfo HTTP/1.1
< Host: 192.168.8.1
< Connection: keep-alive
< Accept-Encoding: gzip, deflate
< Accept: */*
< User-Agent: python-requests/2.19.1
< Cookie: SessionID=1OCMw61lUAc+N8Eiw6DhoD2jYp7y9jy05SLKoiH3m2UfjxWXMCa7+uVRhO3xmJhKe6Ito+m23ofB4Aaj7ORcQZOxzh3trp+VYjSCyStmC9AwYDK/gbtyrKVilRjgj1Hv
< 

> HTTP/1.1 200 OK
> Date: Thu, 01 Jan 1970 00:00:00 GMT
> Server: mini_httpd/1.19 19dec2003
> Connection: keep-alive
> Keep-Alive: timeout=10, max=100
> X-Download-Options: noopen
> X-Frame-Options: deny
> X-XSS-Protection: 1; mode=block
> Strict-Transport-Security: max-age=31536000; includeSubdomains
> Cache-Control: no-cache
> Content-Type: text/html
> Content-Length: 277
> 
<?xml version="1.0" encoding="UTF-8"?>
<response>
<SesInfo>SessionID=1OCMw61lUAc+N8Eiw6DhoD2jYp7y9jy05SLKoiH3m2UfjxWXMCa7+uVRhO3xmJhKe6Ito+m23ofB4Aaj7ORcQZOxzh3trp+VYjSCyStmC9AwYDK/gbtyrKVilRjgj1Hv</SesInfo>
<TokInfo>cXVEAoE1PKWAevq7XvupGewKDEieagYJ</TokInfo>
</response>

Token is: cXVEAoE1PKWAevq7XvupGewKDEieagYJ

Login state check:

%rest -v /api/user/state-login
< GET /api/user/state-login HTTP/1.1
< Host: 192.168.8.1
< Connection: keep-alive
< Accept-Encoding: gzip, deflate
< Accept: */*
< User-Agent: python-requests/2.19.1
< Cookie: SessionID=1OCMw61lUAc+N8Eiw6DhoD2jYp7y9jy05SLKoiH3m2UfjxWXMCa7+uVRhO3xmJhKe6Ito+m23ofB4Aaj7ORcQZOxzh3trp+VYjSCyStmC9AwYDK/gbtyrKVilRjgj1Hv
< 

> HTTP/1.1 200 OK
> Date: Thu, 01 Jan 1970 00:00:00 GMT
> Server: mini_httpd/1.19 19dec2003
> Connection: keep-alive
> Keep-Alive: timeout=10, max=100
> X-Download-Options: noopen
> X-Frame-Options: deny
> X-XSS-Protection: 1; mode=block
> Strict-Transport-Security: max-age=31536000; includeSubdomains
> Cache-Control: no-cache
> Content-Type: text/html
> Content-Length: 146
> 
<?xml version="1.0" encoding="UTF-8"?>
<response>
<State>-1</State>
<Username>admin</Username>
<password_type>4</password_type>
</response>
<Response [200]>

password_type == 4 means password should be encoded in a weird way

import hashlib
from base64 import b64encode
from binascii import hexlify


def sha256(data):
    return hexlify(hashlib.sha256(data).digest())


def encode_password(name, raw_password, token):
    return b64encode(sha256(
        name + b64encode(sha256(raw_password)) + token
    ))


username = 'admin'
raw_password = 'admin'
password = encode_password(username, raw_password, token)
password
'MDg3Yjk2OTlmMGFmZDY2ZjZiYTg3OWRlZGY0ZGJlZWM4N2FkNWYzY2RhYmIyYjNmOWQ1NmRjZjc1ZTVjMDU4NA=='

SessionID is stored in cookie, Token in__RequestVerificationToken header, login and password in form fields.

Ready to login:

%%rest -v POST /api/user/login
__RequestVerificationToken: $token

<?xml version: "1.0" encoding="UTF-8"?>
    <request>
        <Username>${username}</Username>
        <Password>${password}</Password>
        <password_type>4</password_type>
    </request>
< POST /api/user/login HTTP/1.1
< Host: 192.168.8.1
< Connection: keep-alive
< Accept-Encoding: gzip, deflate
< Accept: */*
< User-Agent: python-requests/2.19.1
< __RequestVerificationToken: cXVEAoE1PKWAevq7XvupGewKDEieagYJ
< Cookie: SessionID=1OCMw61lUAc+N8Eiw6DhoD2jYp7y9jy05SLKoiH3m2UfjxWXMCa7+uVRhO3xmJhKe6Ito+m23ofB4Aaj7ORcQZOxzh3trp+VYjSCyStmC9AwYDK/gbtyrKVilRjgj1Hv
< Content-Length: 262
< 
< <?xml version: "1.0" encoding="UTF-8"?>
    <request>
        <Username>admin</Username>
        <Password>MDg3Yjk2OTlmMGFmZDY2ZjZiYTg3OWRlZGY0ZGJlZWM4N2FkNWYzY2RhYmIyYjNmOWQ1NmRjZjc1ZTVjMDU4NA==</Password>
        <password_type>4</password_type>
    </request>
> HTTP/1.1 200 OK
> Date: Thu, 01 Jan 1970 00:00:00 GMT
> Server: mini_httpd/1.19 19dec2003
> Connection: close
> X-Download-Options: noopen
> X-Frame-Options: deny
> X-XSS-Protection: 1; mode=block
> Strict-Transport-Security: max-age=31536000; includeSubdomains
> Cache-Control: no-cache
> Content-Type: text/html
> Content-Length: 61
> __RequestVerificationTokenone: hg5BM6tps+th5Izq6WDLglLzb0NSRKMh
> __RequestVerificationTokentwo: 1JZqnOWj3dC6pp+9YB/ncOCJbJXGvpi7
> __RequestVerificationToken: hg5BM6tps+th5Izq6WDLglLzb0NSRKMh#1JZqnOWj3dC6pp+9YB/ncOCJbJXGvpi7#DQufJyl4wgrGuEQNWmYM8mybypbiKY7g#nrDUvDqHRN+Fs+J+/487ZJbFYZdXMMvk#mrO5AGBmaT6q8M80ZHbLwsF11KEkgGdh#mGQ4eVNVF58yrCQB/a5cyyjqj0AXPdvL#qF28d0Mr4QmxHBbmIc2eYetdiW2NBJFl#pAw9tFNrGdL+k6gmg3Lp4jXeYFPcjcSc#7PGbHrb/kb+LL3BW48TxOsITURgfmt9l#0aaUlDksNku0pWqTYzkbeRnzNQJRsMqz#aENIosKeSTVC3cb89pEg/eVuA8H5h5ZH#YDrs55QSOmPjqc8GRp40t8pmd91JxVxH#GAUAhymo++ADCdl8JVdhOs+HbSQKlFAq#j1iNIqDMboby541Qo3AD9hGIm9vsOXQi#w2heSjgwrNkmaK8GC6U0SF2EVOtyWmLl#9IgCqd8b6PaIac3gxfRrcTJ44maCWcWm#JYib/4oA6MLMLRcUYAVqmYF5AxkJNev3#42a69lbLqT6tU3I5L2HZJyW0tdrhCXYc#/1NWh7e30aKr4xbB3iYEZ8mVKL61IyiK#L/wAKNJOlby/s5F2OefWxwn84/HkLabS#i1bjbub11S1HSeOx/yZThO6isduMjx0o#/85UG65so+49Fz32rWK30G2iKsBidW0Q#2CugWs/KKoKmHXHXjOhY4fzUS1N6yus8#keMr8u/phCa3Q2hRBDJpMbbEpex/05w1#qVoIa5aDThmvZO+SU8oepHFKGTacOokH#CtxWL9LRAUZ2c8DmGc+iIcMaTQvV3uzf#m1R++8ngoAeigdJiDM59dmZ0nf3RNhz1#7h+vC2HrpL0RO13mbY1QfISOG/8e6K9D#JHq2REF+apigGGCVOZ1VpEBftt4Ni6QU#SjIimGQFJmiubdb5D8R7pORrkEB2dY4q#ZkTwMIfDnmQdI+Z6/tequCSpFJMSlUlV#1fIL+GD9mU6wzGrjsEpn1U8j8RS2VoKY#
> Set-Cookie: SessionID=f7es/vn9NPRJ0ju5eGs7DBiCjiHZQFPTYsoMt9UQCjurky5TP3G+i0LywRaMZ3KG5TFFrQcciILIX3/XuL168NgvYf99xeN8exyxG0ODQBZGXudrjr2wk6tX+oEeeO2w;path =/;HttpOnly;
> 
<?xml version="1.0" encoding="UTF-8"?><response>OK</response>
<Response [200]>
r = _

Login endpoint returns a a bunch of tokens to use:

r.headers['__RequestVerificationToken']
'hg5BM6tps+th5Izq6WDLglLzb0NSRKMh#1JZqnOWj3dC6pp+9YB/ncOCJbJXGvpi7#DQufJyl4wgrGuEQNWmYM8mybypbiKY7g#nrDUvDqHRN+Fs+J+/487ZJbFYZdXMMvk#mrO5AGBmaT6q8M80ZHbLwsF11KEkgGdh#mGQ4eVNVF58yrCQB/a5cyyjqj0AXPdvL#qF28d0Mr4QmxHBbmIc2eYetdiW2NBJFl#pAw9tFNrGdL+k6gmg3Lp4jXeYFPcjcSc#7PGbHrb/kb+LL3BW48TxOsITURgfmt9l#0aaUlDksNku0pWqTYzkbeRnzNQJRsMqz#aENIosKeSTVC3cb89pEg/eVuA8H5h5ZH#YDrs55QSOmPjqc8GRp40t8pmd91JxVxH#GAUAhymo++ADCdl8JVdhOs+HbSQKlFAq#j1iNIqDMboby541Qo3AD9hGIm9vsOXQi#w2heSjgwrNkmaK8GC6U0SF2EVOtyWmLl#9IgCqd8b6PaIac3gxfRrcTJ44maCWcWm#JYib/4oA6MLMLRcUYAVqmYF5AxkJNev3#42a69lbLqT6tU3I5L2HZJyW0tdrhCXYc#/1NWh7e30aKr4xbB3iYEZ8mVKL61IyiK#L/wAKNJOlby/s5F2OefWxwn84/HkLabS#i1bjbub11S1HSeOx/yZThO6isduMjx0o#/85UG65so+49Fz32rWK30G2iKsBidW0Q#2CugWs/KKoKmHXHXjOhY4fzUS1N6yus8#keMr8u/phCa3Q2hRBDJpMbbEpex/05w1#qVoIa5aDThmvZO+SU8oepHFKGTacOokH#CtxWL9LRAUZ2c8DmGc+iIcMaTQvV3uzf#m1R++8ngoAeigdJiDM59dmZ0nf3RNhz1#7h+vC2HrpL0RO13mbY1QfISOG/8e6K9D#JHq2REF+apigGGCVOZ1VpEBftt4Ni6QU#SjIimGQFJmiubdb5D8R7pORrkEB2dY4q#ZkTwMIfDnmQdI+Z6/tequCSpFJMSlUlV#1fIL+GD9mU6wzGrjsEpn1U8j8RS2VoKY#'

Each API call require a new token:

tokens = (t for t in filter(None, r.headers['__RequestVerificationToken'].split('#')))
token = tokens.next()
print("Next token is: " + token)
Next token is: hg5BM6tps+th5Izq6WDLglLzb0NSRKMh
friend_phone = ''  # random phone number here 
%%rest POST /api/sms/send-sms
__RequestVerificationToken: $token
    
<?xml version='1.0' encoding='UTF-8'?>
    <request>
        <Index>-1</Index>
        <Phones><Phone>${friend_phone}</Phone></Phones>
        <Sca></Sca>
        <Content>Hello? Is there anybody in there?</Content>
        <Length>-1</Length>
        <Reserved>1</Reserved>
        <Date>-1</Date>
    </request>
OK
<Response [200]>

SMS was sended, let’s try to send more

%%rest POST /api/sms/send-sms
__RequestVerificationToken: $token
    
<?xml version='1.0' encoding='UTF-8'?>
    <request>
        <Index>-1</Index>
        <Phones><Phone>${friend_phone}</Phone></Phones>
        <Sca></Sca>
        <Content>Just nod if you can hear me.</Content>
        <Length>-1</Length>
        <Reserved>1</Reserved>
        <Date>-1</Date>
    </request>
125003
<Response [200]>

125003 error returned - SMS is not sended, need to use the next token:

token = tokens.next()
print("Next token is: " + token)
Next token is: 1JZqnOWj3dC6pp+9YB/ncOCJbJXGvpi7
%%rest POST /api/sms/send-sms
__RequestVerificationToken: $token
    
<?xml version='1.0' encoding='UTF-8'?>
    <request>
        <Index>-1</Index>
        <Phones><Phone>${friend_phone}</Phone></Phones>
        <Sca></Sca>
        <Content>Is there anyone at home?</Content>
        <Length>-1</Length>
        <Reserved>1</Reserved>
        <Date>-1</Date>
    </request>
OK
<Response [200]>
Ethereum JSON RPC usage example

This site is powered by Jupyter Book