Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save developerworks/7535799282777149bd26cf3e3433e5b5 to your computer and use it in GitHub Desktop.
Save developerworks/7535799282777149bd26cf3e3433e5b5 to your computer and use it in GitHub Desktop.
%% Author: Alex Radetsky <rad@pearlpbx.com>
%% Module: Simple SMPP Server on Erlang, gen_fsm, ranch, smpp34pdu.
%% It always authorize bind_transceiver and receives submit_sm
-module(smpp_protocol).
-behaviour(gen_fsm).
-behaviour(ranch_protocol).
-include_lib("smpp34pdu/include/smpp34pdu.hrl").
%% API
-export([start_link/4]).
-export([init/1]).
-export([unauthorized/2, authorized/2]).
-export([handle_event/3, handle_sync_event/4, handle_info/3, terminate/3, code_change/4]).
-define(SERVER, ?MODULE).
-record(state,{ref,socket,transport}).
start_link(Ref,Socket,Transport,Opts) ->
%% start the process syncronously to avoid deadlock
%% special did it for ranch. We encapsulate not only gen_fsm but ranch_protocol too.
%% So we need it.
proc_lib:start_link(?MODULE, init, [[Ref, Socket, Transport, Opts]]).
init([Ref, Socket, Transport, _Opts=[]]) ->
ok = proc_lib:init_ack({ok,self()}),
ok = ranch:accept_ack(Ref),
ok = Transport:setopts(Socket,[{active,once}]),
gen_fsm:enter_loop(?MODULE,[], unauthorized,
#state{ref=Ref, socket=Socket, transport=Transport}).
unauthorized({tcp,Port,Data}, #state{transport=Transport, socket=Socket}=State) ->
% lager:debug("Received from Port=~p data=~p",[Port, binary_to_list(Data)]),
{ok, PduList, _Rest} = smpp34pdu:unpack(Data),
[
process_unauthorized_pdu (Unpacked, Socket, Transport) || Unpacked <- PduList
],
{next_state, authorized, State}.
authorized({tcp,Port,Data}, #state{transport=Transport, socket=Socket}=State) ->
{ok, PduList, _Rest} = smpp34pdu:unpack(Data),
[
process_pdu( Unpacked, Socket, Transport) || Unpacked <- PduList
],
{next_state, authorized, State}.
handle_event(Event, StateName, State) ->
{next_state, StateName, State}.
handle_sync_event(Event, _From, StateName, State) ->
{reply, ok, StateName, State}.
handle_info({tcp_closed,_Port}, StateName, #state{transport=Transport, socket=Socket}=State) ->
lager:debug("StateName=~p, Connection closed by remote side.",[StateName]),
lager:debug("Transport: ~p, Socket: ~p",[Transport, Socket]),
%% the connection was closed
Transport:close(Socket),
{stop, exit, State};
handle_info(Info={tcp,_Port,Data},StateName,State) ->
lager:debug("TCP Received something from network: StateName= ~p, Receive data: ~p",[StateName, binary_to_list(Data)]),
%% got some data from the network, pass it to our gen_fsm event
gen_fsm:send_event(self(),Info),
Transport = State#state.transport,
Transport:setopts(State#state.socket,[{active,once}]),
{next_state, StateName, State}.
terminate(_Reason, _StateName, _State) ->
ok.
code_change(_Oldsvn, StateName, State, _Extra) ->
{ok, StateName, State}.
%% Internal functions
process_pdu (#pdu{command_id=?SUBMIT_SM} = Pdu, Socket, Transport ) ->
SubmitSm = Pdu#pdu.body,
lager:info("SUBMIT_SM recvd f:~s t:~s s:~p ",[
SubmitSm#submit_sm.source_addr,
SubmitSm#submit_sm.destination_addr,
SubmitSm#submit_sm.short_message
]
),
Reply = smpp34pdu:pack(
?ESME_ROK,
Pdu#pdu.sequence_number,
#submit_sm_resp {
message_id= "0123456789"
}
),
Transport:send(Socket, Reply);
process_pdu (Pdu, Socket, Transport) ->
lager:error("Unknown PDU recvd: ~p",[Pdu]),
Reply = smpp34pdu:pack(
?ESME_ROK,
Pdu#pdu.sequence_number,
#generic_nack{}
),
Transport:send(Socket, Reply).
process_unauthorized_pdu ( Unpacked, Socket, Transport ) ->
SeqNo = Unpacked#pdu.sequence_number,
Reply = smpp34pdu:pack(
?ESME_ROK,
SeqNo,
#bind_transceiver_resp {
system_id= "PearlSMS",
sc_interface_version=?VERSION
}
),
Transport:send(Socket, Reply).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment