2002-11-18 21:39:47 +01:00
%%%----------------------------------------------------------------------
%%% File : ejabberd_listener.erl
2007-12-24 12:41:41 +01:00
%%% Author : Alexey Shchepin <alexey@process-one.net>
%%% Purpose : Manage socket listener
%%% Created : 16 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
2009-01-12 15:52:59 +01:00
%%% ejabberd, Copyright (C) 2002-2009 ProcessOne
2007-12-24 12:41:41 +01:00
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
%%% published by the Free Software Foundation; either version 2 of the
%%% License, or (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
%%% General Public License for more details.
2009-01-08 22:01:27 +01:00
%%%
2007-12-24 12:41:41 +01:00
%%% You should have received a copy of the GNU General Public License
%%% along with this program; if not, write to the Free Software
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
%%% 02111-1307 USA
%%%
2002-11-18 21:39:47 +01:00
%%%----------------------------------------------------------------------
- module ( ejabberd_listener ) .
2007-12-24 12:41:41 +01:00
- author ( 'alexey@process-one.net' ) .
2002-11-18 21:39:47 +01:00
2003-07-14 20:06:03 +02:00
- export ( [ start_link / 0 , init / 1 , start / 3 ,
init / 3 ,
2004-05-09 20:38:49 +02:00
init_ssl / 4 ,
start_listener / 3 ,
2004-06-17 23:29:24 +02:00
stop_listener / 1 ,
add_listener / 3 ,
delete_listener / 1
2003-02-06 20:09:22 +01:00
] ) .
2002-11-18 21:39:47 +01:00
2003-10-09 20:09:05 +02:00
- include ( " ejabberd.hrl " ) .
2009-01-08 22:01:27 +01:00
%% We do not block on send anymore.
- define ( TCP_SEND_TIMEOUT , 15000 ) .
2003-02-01 21:21:28 +01:00
start_link ( ) - >
2003-01-07 20:10:35 +01:00
supervisor : start_link ( { local , ejabberd_listeners } , ? MODULE , [ ] ) .
2002-11-18 21:39:47 +01:00
2003-01-07 20:10:35 +01:00
init ( _ ) - >
2003-01-21 21:36:55 +01:00
case ejabberd_config : get_local_option ( listen ) of
2003-01-07 20:10:35 +01:00
undefined - >
ignore ;
Ls - >
{ ok , { { one_for_one , 10 , 1 } ,
lists : map (
2003-07-14 20:06:03 +02:00
fun ( { Port , Module , Opts } ) - >
2003-01-07 20:10:35 +01:00
{ Port ,
2003-07-14 20:06:03 +02:00
{ ? MODULE , start , [ Port , Module , Opts ] } ,
2004-06-17 23:29:24 +02:00
transient ,
2003-01-07 20:10:35 +01:00
brutal_kill ,
worker ,
2003-07-20 22:35:35 +02:00
[ ? MODULE ] }
2003-01-07 20:10:35 +01:00
end , Ls ) } }
end .
2003-07-14 20:06:03 +02:00
start ( Port , Module , Opts ) - >
2007-12-17 12:03:22 +01:00
SSLError = " There is a problem with your ejabberd configuration file: the option 'ssl' for listening sockets is no longer available. To get SSL encryption use the option 'tls'. " ,
2003-02-06 20:09:22 +01:00
case lists : keysearch ( ssl , 1 , Opts ) of
2007-12-17 12:03:22 +01:00
{ value , { ssl , _ SSLOpts } } - >
%%{ok, proc_lib:spawn_link(?MODULE, init_ssl,
%% [Port, Module, Opts, SSLOpts])};
? ERROR_MSG ( SSLError , [ ] ) ,
{ error , SSLError } ;
2003-02-06 20:09:22 +01:00
_ - >
2004-05-08 21:30:38 +02:00
case lists : member ( ssl , Opts ) of
true - >
2007-12-17 12:03:22 +01:00
%%{ok, proc_lib:spawn_link(?MODULE, init_ssl,
%% [Port, Module, Opts, []])};
? ERROR_MSG ( SSLError , [ ] ) ,
{ error , SSLError } ;
2004-05-08 21:30:38 +02:00
false - >
{ ok , proc_lib : spawn_link ( ? MODULE , init ,
[ Port , Module , Opts ] ) }
end
2003-02-06 20:09:22 +01:00
end .
2003-01-07 20:10:35 +01:00
2003-07-14 20:06:03 +02:00
init ( Port , Module , Opts ) - >
2004-05-08 21:30:38 +02:00
SockOpts = lists : filter ( fun ( { ip , _ } ) - > true ;
( inet6 ) - > true ;
( inet ) - > true ;
( _ ) - > false
end , Opts ) ,
2004-06-17 23:29:24 +02:00
Res = gen_tcp : listen ( Port , [ binary ,
2009-01-08 22:01:27 +01:00
{ packet , 0 } ,
2004-06-17 23:29:24 +02:00
{ active , false } ,
{ reuseaddr , true } ,
2004-12-03 23:54:02 +01:00
{ nodelay , true } ,
2009-01-08 22:01:27 +01:00
{ send_timeout , ? TCP_SEND_TIMEOUT } ,
2004-12-03 23:54:02 +01:00
{ keepalive , true } |
2004-06-17 23:29:24 +02:00
SockOpts ] ) ,
case Res of
{ ok , ListenSocket } - >
accept ( ListenSocket , Module , Opts ) ;
{ error , Reason } - >
? ERROR_MSG ( " Failed to open socket for ~p : ~p " ,
[ { Port , Module , Opts } , Reason ] ) ,
error
end .
2002-11-18 21:39:47 +01:00
2003-07-14 20:06:03 +02:00
accept ( ListenSocket , Module , Opts ) - >
2002-11-18 21:39:47 +01:00
case gen_tcp : accept ( ListenSocket ) of
2003-03-12 20:48:05 +01:00
{ ok , Socket } - >
2003-10-09 20:09:05 +02:00
case { inet : sockname ( Socket ) , inet : peername ( Socket ) } of
{ { ok , Addr } , { ok , PAddr } } - >
? INFO_MSG ( " ( ~w ) Accepted connection ~w -> ~w " ,
[ Socket , PAddr , Addr ] ) ;
_ - >
ok
end ,
2006-09-10 01:33:00 +02:00
case Module of
2006-10-01 03:53:37 +02:00
{ frontend , Mod } - >
ejabberd_frontend_socket : start ( Mod , gen_tcp , Socket , Opts ) ;
2006-09-10 01:33:00 +02:00
_ - >
ejabberd_socket : start ( Module , gen_tcp , Socket , Opts )
end ,
2003-10-28 21:26:43 +01:00
accept ( ListenSocket , Module , Opts ) ;
{ error , Reason } - >
? INFO_MSG ( " ( ~w ) Failed TCP accept: ~w " ,
[ ListenSocket , Reason ] ) ,
2003-07-14 20:06:03 +02:00
accept ( ListenSocket , Module , Opts )
2003-02-06 20:09:22 +01:00
end .
2003-07-14 20:06:03 +02:00
init_ssl ( Port , Module , Opts , SSLOpts ) - >
2004-05-08 21:30:38 +02:00
SockOpts = lists : filter ( fun ( { ip , _ } ) - > true ;
( inet6 ) - > true ;
( inet ) - > true ;
( { verify , _ } ) - > true ;
( { depth , _ } ) - > true ;
( { certfile , _ } ) - > true ;
( { keyfile , _ } ) - > true ;
( { password , _ } ) - > true ;
( { cacertfile , _ } ) - > true ;
( { ciphers , _ } ) - > true ;
( _ ) - > false
end , Opts ) ,
2004-06-17 23:29:24 +02:00
Res = ssl : listen ( Port , [ binary ,
{ packet , 0 } ,
{ active , false } ,
{ nodelay , true } |
SockOpts ++ SSLOpts ] ) ,
case Res of
{ ok , ListenSocket } - >
accept_ssl ( ListenSocket , Module , Opts ) ;
{ error , Reason } - >
? ERROR_MSG ( " Failed to open socket for ~p : ~p " ,
[ { Port , Module , Opts } , Reason ] ) ,
error
end .
2003-02-06 20:09:22 +01:00
2003-07-14 20:06:03 +02:00
accept_ssl ( ListenSocket , Module , Opts ) - >
2003-10-28 21:26:43 +01:00
case ssl : accept ( ListenSocket , 200 ) of
2003-03-12 20:48:05 +01:00
{ ok , Socket } - >
2003-10-09 20:09:05 +02:00
case { ssl : sockname ( Socket ) , ssl : peername ( Socket ) } of
{ { ok , Addr } , { ok , PAddr } } - >
? INFO_MSG ( " ( ~w ) Accepted SSL connection ~w -> ~w " ,
[ Socket , PAddr , Addr ] ) ;
_ - >
ok
end ,
2003-10-29 21:09:09 +01:00
{ ok , Pid } = Module : start ( { ssl , Socket } , Opts ) ,
2004-09-29 23:10:40 +02:00
catch ssl : controlling_process ( Socket , Pid ) ,
2006-01-13 02:55:20 +01:00
Module : become_controller ( Pid ) ,
2003-10-28 21:26:43 +01:00
accept_ssl ( ListenSocket , Module , Opts ) ;
{ error , timeout } - >
accept_ssl ( ListenSocket , Module , Opts ) ;
{ error , Reason } - >
? INFO_MSG ( " ( ~w ) Failed SSL handshake: ~w " ,
[ ListenSocket , Reason ] ) ,
2003-07-14 20:06:03 +02:00
accept_ssl ( ListenSocket , Module , Opts )
2002-11-18 21:39:47 +01:00
end .
2004-05-09 20:38:49 +02:00
start_listener ( Port , Module , Opts ) - >
ChildSpec = { Port ,
{ ? MODULE , start , [ Port , Module , Opts ] } ,
2004-06-17 23:29:24 +02:00
transient ,
2004-05-09 20:38:49 +02:00
brutal_kill ,
worker ,
[ ? MODULE ] } ,
supervisor : start_child ( ejabberd_listeners , ChildSpec ) .
stop_listener ( Port ) - >
supervisor : terminate_child ( ejabberd_listeners , Port ) ,
supervisor : delete_child ( ejabberd_listeners , Port ) .
2004-06-17 23:29:24 +02:00
add_listener ( Port , Module , Opts ) - >
Ports = case ejabberd_config : get_local_option ( listen ) of
undefined - >
[ ] ;
Ls - >
Ls
end ,
Ports1 = lists : keydelete ( Port , 1 , Ports ) ,
Ports2 = [ { Port , Module , Opts } | Ports1 ] ,
ejabberd_config : add_local_option ( listen , Ports2 ) ,
start_listener ( Port , Module , Opts ) .
delete_listener ( Port ) - >
Ports = case ejabberd_config : get_local_option ( listen ) of
undefined - >
[ ] ;
Ls - >
Ls
end ,
Ports1 = lists : keydelete ( Port , 1 , Ports ) ,
ejabberd_config : add_local_option ( listen , Ports1 ) ,
stop_listener ( Port ) .