%% ``Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB. %% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings %% AB. All Rights Reserved.'' %% %% $Id: httpd_socket.erl,v 1.1 2008/12/17 09:53:34 mikpe Exp $ %% -module(httpd_socket). -export([start/1, listen/2, listen/3, accept/2, accept/3, deliver/3, send/3, recv/4, close/2, peername/2, resolve/1, config/1, controlling_process/3, active_once/2]). -include("httpd.hrl"). -define(VMODULE,"SOCKET"). -include("httpd_verbosity.hrl"). -include_lib("kernel/include/inet.hrl"). %% start -> ok | {error,Reason} start(ip_comm) -> case inet_db:start() of {ok,_Pid} -> ok; {error,{already_started,_Pid}} -> ok; Error -> Error end; start({ssl,_SSLConfig}) -> case ssl:start() of ok -> ok; {ok, _} -> ok; {error,{already_started,_}} -> ok; Error -> Error end. %% listen listen(SocketType,Port) -> listen(SocketType,undefined,Port). listen(ip_comm,Addr,Port) -> ?DEBUG("listening(ip_comm) to port ~p", [Port]), Opt = sock_opt(Addr,[{backlog,128},{reuseaddr,true}]), case gen_tcp:listen(Port,Opt) of {ok,ListenSocket} -> ListenSocket; Error -> Error end; listen({ssl,SSLConfig},Addr,Port) -> ?DEBUG("listening(ssl) to port ~p" "~n SSLConfig: ~p", [Port,SSLConfig]), Opt = sock_opt(Addr,SSLConfig), case ssl:listen(Port, Opt) of {ok,ListenSocket} -> ListenSocket; Error -> Error end. sock_opt(undefined,Opt) -> [{packet,0},{active,false}|Opt]; sock_opt(Addr,Opt) -> [{ip, Addr},{packet,0},{active,false}|Opt]. %% -define(packet_type_http,true). %% -define(packet_type_httph,true). %% -ifdef(packet_type_http). %% sock_opt(undefined,Opt) -> [{packet,http},{active,false}|Opt]; %% sock_opt(Addr,Opt) -> [{ip, Addr},{packet,http},{active,false}|Opt]. %% -elif(packet_type_httph). %% sock_opt(undefined,Opt) -> [{packet,httph},{active,false}|Opt]; %% sock_opt(Addr,Opt) -> [{ip, Addr},{packet,httph},{active,false}|Opt]. %% -else. %% sock_opt(undefined,Opt) -> [{packet,0},{active,false}|Opt]; %% sock_opt(Addr,Opt) -> [{ip, Addr},{packet,0},{active,false}|Opt]. %% -endif. %% active_once active_once(Type, Sock) -> active(Type, Sock, once). active(ip_comm, Sock, Active) -> inet:setopts(Sock, [{active, Active}]); active({ssl, _SSLConfig}, Sock, Active) -> ssl:setopts(Sock, [{active, Active}]). %% accept accept(A, B) -> accept(A, B, infinity). accept(ip_comm,ListenSocket, T) -> ?DEBUG("accept(ip_comm) on socket ~p", [ListenSocket]), case gen_tcp:accept(ListenSocket, T) of {ok,Socket} -> Socket; Error -> ?vtrace("accept(ip_comm) failed for reason:" "~n Error: ~p",[Error]), Error end; accept({ssl,_SSLConfig},ListenSocket, T) -> ?DEBUG("accept(ssl) on socket ~p", [ListenSocket]), case ssl:accept(ListenSocket, T) of {ok,Socket} -> Socket; Error -> ?vtrace("accept(ssl) failed for reason:" "~n Error: ~p",[Error]), Error end. %% controlling_process controlling_process(ip_comm, Socket, Pid) -> gen_tcp:controlling_process(Socket, Pid); controlling_process({ssl, _}, Socket, Pid) -> ssl:controlling_process(Socket, Pid). %% deliver deliver(SocketType, Socket, IOListOrBinary) -> case send(SocketType, Socket, IOListOrBinary) of % {error, einval} -> % ?vlog("deliver failed for reason: einval" % "~n SocketType: ~p" % "~n Socket: ~p" % "~n Data: ~p", % [SocketType, Socket, type(IOListOrBinary)]), % (catch close(SocketType, Socket)), % socket_closed; {error, _Reason} -> ?vlog("deliver(~p) failed for reason:" "~n Reason: ~p",[SocketType,_Reason]), (catch close(SocketType, Socket)), socket_closed; _ -> ok end. % type(L) when list(L) -> % {list, L}; % type(B) when binary(B) -> % Decoded = % case (catch binary_to_term(B)) of % {'EXIT', _} -> % %% Oups, not a term, try list % case (catch binary_to_list(B)) of % %% Oups, not a list either, give up % {'EXIT', _} -> % {size, size(B)}; % L -> % {list, L} % end; % T -> % {term, T} % end, % {binary, Decoded}; % type(T) when tuple(T) -> % {tuple, T}; % type(I) when integer(I) -> % {integer, I}; % type(F) when float(F) -> % {float, F}; % type(P) when pid(P) -> % {pid, P}; % type(P) when port(P) -> % {port, P}; % type(R) when reference(R) -> % {reference, R}; % type(T) -> % {term, T}. send(ip_comm,Socket,Data) -> ?DEBUG("send(ip_comm) -> ~p bytes on socket ~p",[data_size(Data),Socket]), gen_tcp:send(Socket,Data); send({ssl,SSLConfig},Socket,Data) -> ?DEBUG("send(ssl) -> ~p bytes on socket ~p",[data_size(Data),Socket]), ssl:send(Socket, Data). recv(ip_comm,Socket,Length,Timeout) -> ?DEBUG("recv(ip_comm) -> read from socket ~p",[Socket]), gen_tcp:recv(Socket,Length,Timeout); recv({ssl,SSLConfig},Socket,Length,Timeout) -> ?DEBUG("recv(ssl) -> read from socket ~p",[Socket]), ssl:recv(Socket,Length,Timeout). -ifdef(inets_debug). data_size(L) when list(L) -> httpd_util:flatlength(L); data_size(B) when binary(B) -> size(B); data_size(O) -> {unknown_size,O}. -endif. %% peername peername(ip_comm, Socket) -> case inet:peername(Socket) of {ok,{{A,B,C,D},Port}} -> PeerName = integer_to_list(A)++"."++integer_to_list(B)++"."++ integer_to_list(C)++"."++integer_to_list(D), ?DEBUG("peername(ip_comm) on socket ~p: ~p", [Socket,{Port,PeerName}]), {Port,PeerName}; {error,Reason} -> ?vlog("failed getting peername:" "~n Reason: ~p" "~n Socket: ~p", [Reason,Socket]), {-1,"unknown"} end; peername({ssl,_SSLConfig},Socket) -> case ssl:peername(Socket) of {ok,{{A,B,C,D},Port}} -> PeerName = integer_to_list(A)++"."++integer_to_list(B)++"."++ integer_to_list(C)++"."++integer_to_list(D), ?DEBUG("peername(ssl) on socket ~p: ~p", [Socket, {Port,PeerName}]), {Port,PeerName}; {error,_Reason} -> {-1,"unknown"} end. %% resolve resolve(_) -> {ok,Name} = inet:gethostname(), Name. %% close close(ip_comm,Socket) -> Res = case (catch gen_tcp:close(Socket)) of ok -> ok; {error,Reason} -> {error,Reason}; {'EXIT',{noproc,_}} -> {error,closed}; {'EXIT',Reason} -> {error,Reason}; Otherwise -> {error,Otherwise} end, ?vtrace("close(ip_comm) result: ~p",[Res]), Res; close({ssl,_SSLConfig},Socket) -> Res = case (catch ssl:close(Socket)) of ok -> ok; {error,Reason} -> {error,Reason}; {'EXIT',{noproc,_}} -> {error,closed}; {'EXIT',Reason} -> {error,Reason}; Otherwise -> {error,Otherwise} end, ?vtrace("close(ssl) result: ~p",[Res]), Res. %% config (debug: {certfile, "/var/tmp/server_root/conf/ssl_server.pem"}) config(ConfigDB) -> case httpd_util:lookup(ConfigDB,com_type,ip_comm) of ssl -> case ssl_certificate_file(ConfigDB) of undefined -> {error, ?NICE("Directive SSLCertificateFile " "not found in the config file")}; SSLCertificateFile -> {ssl, SSLCertificateFile++ ssl_certificate_key_file(ConfigDB)++ ssl_verify_client(ConfigDB)++ ssl_ciphers(ConfigDB)++ ssl_password(ConfigDB)++ ssl_verify_depth(ConfigDB)++ ssl_ca_certificate_file(ConfigDB)} end; ip_comm -> ip_comm end. ssl_certificate_file(ConfigDB) -> case httpd_util:lookup(ConfigDB,ssl_certificate_file) of undefined -> undefined; SSLCertificateFile -> [{certfile,SSLCertificateFile}] end. ssl_certificate_key_file(ConfigDB) -> case httpd_util:lookup(ConfigDB,ssl_certificate_key_file) of undefined -> []; SSLCertificateKeyFile -> [{keyfile,SSLCertificateKeyFile}] end. ssl_verify_client(ConfigDB) -> case httpd_util:lookup(ConfigDB,ssl_verify_client) of undefined -> []; SSLVerifyClient -> [{verify,SSLVerifyClient}] end. ssl_ciphers(ConfigDB) -> case httpd_util:lookup(ConfigDB,ssl_ciphers) of undefined -> []; Ciphers -> [{ciphers, Ciphers}] end. ssl_password(ConfigDB) -> case httpd_util:lookup(ConfigDB,ssl_password_callback_module) of undefined -> []; Module -> case httpd_util:lookup(ConfigDB, ssl_password_callback_function) of undefined -> []; Function -> case catch apply(Module, Function, []) of Password when list(Password) -> [{password, Password}]; Error -> error_report(ssl_password,Module,Function,Error), [] end end end. ssl_verify_depth(ConfigDB) -> case httpd_util:lookup(ConfigDB, ssl_verify_client_depth) of undefined -> []; Depth -> [{depth, Depth}] end. ssl_ca_certificate_file(ConfigDB) -> case httpd_util:lookup(ConfigDB, ssl_ca_certificate_file) of undefined -> []; File -> [{cacertfile, File}] end. error_report(Where,M,F,Error) -> error_logger:error_report([{?MODULE, Where}, {apply, {M, F, []}}, Error]).