使用ejabberd挂钩建立传出SSL连接

时间:2013-12-31 21:47:22

标签: ssl erlang ejabberd

我正在编写一个ejabberd钩子来创建与Apple推送通知服务器的传出SSL连接。我已经测试了ejabberd之外的send方法(在erlang解释器中)并且可以验证它是否有效。我不确定为什么这个模块:

-module(mod_http_offline).

-author("Joseph Martin").

%% Every ejabberd module implements the gen_mod behavior
%% The gen_mod behavior requires two functions: start/2 and stop/1
-behaviour(gen_mod).

%% public methods for this module
-export([start/2, stop/1, create_message/3]).

%% included for writing to ejabberd log file
-include("ejabberd.hrl").

%% ejabberd functions for JID manipulation called jlib.
-include("jlib.hrl").

start(_Host, _Opt) -> 
        ?INFO_MSG("mod_http_offline loading", []),
        %send("Test Push"),
        ejabberd_hooks:add(offline_message_hook, _Host, ?MODULE, create_message, 50).   


stop (_Host) -> 
        ?INFO_MSG("stopping mod_http_offline", []),
        ejabberd_hooks:delete(offline_message_hook, _Host, ?MODULE, create_message, 50).


create_message(_From, _To, Packet) ->
        Type = xml:get_tag_attr_s("type", Packet),
        FromS = xml:get_tag_attr_s("from", Packet),
        ToS = xml:get_tag_attr_s("to", Packet),
        Body = xml:get_path_s(Packet, [{elem, "body"}, cdata]),
        if (Type == "chat") ->
            send("You did it!","10","Chime")
        end.

% All argument fields expect a string
send(Msg) ->
  send_pn([{alert, Msg}]).
% send a string and and a bagde number
send(Msg, Badge) ->
  send_pn([{alert, Msg}, {badge, Badge}]).
% send a string, a badge number and play a sound
send(Msg, Badge, Sound) ->
  send_pn([{alert, Msg}, {badge, Badge}, {sound, Sound}]).

send_pn(Msg) ->

  % start ssl
    ssl:start(),
  % application:start(ssl),

  % socket configuration, may need to increase the timeout if concurrency becomes an issue
  Address = "gateway.sandbox.push.apple.com",
  % Address = "gateway.push.apple.com",
  Port = 2195,
  Cert = "/Users/joemartin/Desktop/PushNotificationCertificates/PushChatCert.pem",
  Key  = "/Users/joemartin/Desktop/PushNotificationCertificates/PushChatKey.pem",
  Options = [{certfile, Cert}, {keyfile, Key}, {password, "mypassword"}, {mode, binary}, {verify, verify_none}],
  Timeout = 5000,

  case ssl:connect(Address, Port, Options, Timeout) of
    {ok, Socket} ->

      % Convert the device token from hex to int to binary
      Token = "3eca19d7...mydevicetokenstring",
      TokenNum = erlang:list_to_integer(Token, 16),
      TokenBin = <<TokenNum:32/integer-unit:8>>,

      % Construct the protocol packet
      PayloadString = create_json(Msg),
      Payload = list_to_binary(PayloadString),
      PayloadLength = byte_size(Payload),
      Packet = <<0:8, 32:16, TokenBin/binary, PayloadLength:16, Payload/binary>>,

      % Send the packet then close the socket
      ssl:send(Socket, Packet),
      ssl:close(Socket),

      % Return the PayloadString (for debugging purposes)
      PayloadString;
    {error, Reason} ->
        ?INFO_MSG("THE SSL CONNECTION FAILED", []),
      Reason
  end.

% helper for creating json
create_json(List) ->
  lists:append(["{\"aps\":{", create_keyvalue(List), "}}"]).

create_keyvalue([Head]) ->
  create_pair(Head);

create_keyvalue([Head|Tail]) ->
  lists:append([create_pair(Head), ",", create_keyvalue(Tail)]).

create_pair({Key, Value}) ->
  lists:append([add_quotes(atom_to_list(Key)), ":", add_quotes(Value)]).

add_quotes(String) ->
  lists:append(["\"", String, "\""]).

在我的错误日志中创建此堆栈跟踪:

=ERROR REPORT==== 2013-12-31 16:23:57 ===
E(<0.1558.0>:ejabberd_hooks:294) : {undef,
                                    [{tls,connect,
                                      ["gateway.sandbox.push.apple.com",2195,
                                       [{certfile,
                                         "/Users/joemartin/Desktop/PushNotificationCertificates/PushChatCert.pem"},
                                        {keyfile,
                                         "/Users/joemartin/Desktop/PushNotificationCertificates/PushChatKey.pem"},
                                        {password,"mypassword"},
                                        {mode,binary},
                                        {verify,verify_none}],
                                       5000],
                                      []},
                                     {mod_http_offline,send_pn,1,
                                      [{file,"mod_http_offline.erl"},
                                       {line,69}]},
                                     {ejabberd_hooks,run1,3,
                                      [{file,"ejabberd_hooks.erl"},
                                       {line,290}]},
                                     {ejabberd_sm,route,3,
                                      [{file,"ejabberd_sm.erl"},{line,87}]},
                                     {ejabberd_local,route,3,
                                      [{file,"ejabberd_local.erl"},
                                       {line,120}]},
                                     {ejabberd_router,route,3,
                                      [{file,"ejabberd_router.erl"},
                                       {line,68}]},
                                     {ejabberd_c2s,session_established2,2,
                                      [{file,"ejabberd_c2s.erl"},{line,1122}]},
                                     {p1_fsm,handle_msg,10,
                                      [{file,"p1_fsm.erl"},{line,544}]}]}

我已经读过ejabberd 2默认禁用了旧式SSL,但认为这仅适用于传入的客户端连接。我还没有找到一个很好的例子,可以建立一个传出SSL连接的钩子。我也意识到mod_http_offline这个名字用词不当。有什么想法吗?

谢谢你,    乔

2 个答案:

答案 0 :(得分:1)

我在ejabberd buglist on github上看到了它。

只是玩它降级一个或另一个。 为解决生产问题,可以根据需要定制依赖关系。

重新模块名称,确保它是唯一的并且与启动/停止回调一致。可能坚持常规并拥有mod前缀会很好。此新名称也转到ejabberd.cfg(在模块部分中)。

答案 1 :(得分:0)

感谢您指导我的buglist。问题是这个版本的ejabberd包含的tls模块会影响erlang的tls模块。我决定只使用ejabberd 2.1.13的二进制发行版来编译这个模块,而不是愚弄符号链接和修补它。

我的步骤如下:

1)从.app二进制文件安装ejabberd(我正在使用MAC OSX)

2)导航到/ Applications / ejabberd-directory / ebin

3)使用包含的erlc(不是我的路径中的那个)来编译我的模块:

./erlc -I /Applications/ejabberd-2.1.13/lib/ejabberd-2.1.13/include/ mod_http_offline.erl

4)使用所有其他光束文件将模块添加到ebin目录

5)修改ejabberd配置文件以包含 {mod_http_offline,[]}

6)重新启动我的安装