]> granicus.if.org Git - ejabberd/commitdiff
* src/ejabberd_listener.erl: New way to configure IP address and
authorBadlop <badlop@process-one.net>
Mon, 12 Jan 2009 20:03:02 +0000 (20:03 +0000)
committerBadlop <badlop@process-one.net>
Mon, 12 Jan 2009 20:03:02 +0000 (20:03 +0000)
IP version of listener. Support for definition of IP address in
string format, and implicit definition of IP
version (EJAB-388). Support for defining several listeners: all
with same port number but different IP addresses (EJAB-389)(thanks
to Fabrice Colliot and Sergei Golovan). Better report in WebAdmin
of problem when starting a listener. The old configuration method
of ip tuple and inet6 is fully supported for backwards
compatibility, but is not documented in the Guide anymore.
* src/ejabberd_config.erl: Likewise
* src/mod_proxy65/mod_proxy65_stream.erl: Likewise
* src/mod_proxy65/mod_proxy65_service.erl: Likewise
* src/web/ejabberd_web_admin.erl: Likewise
* doc/guide.tex: Document the new way to configure IP address and
IP version of listener, undocument options ip and inet6
* doc/guide.html: Likewise

SVN Revision: 1812

ChangeLog
doc/guide.html
doc/guide.tex
src/ejabberd_config.erl
src/ejabberd_listener.erl
src/mod_proxy65/mod_proxy65_service.erl
src/mod_proxy65/mod_proxy65_stream.erl
src/web/ejabberd_web_admin.erl

index beee3a770dbfbfb06949fd92940cbd792eb2f5ef..6270afdc30e38d5f9dfa2a0222d5c3ed0facd9c2 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,22 @@
 2009-01-12  Badlop  <badlop@process-one.net>
 
+       * src/ejabberd_listener.erl: New way to configure IP address and
+       IP version of listener. Support for definition of IP address in
+       string format, and implicit definition of IP
+       version (EJAB-388). Support for defining several listeners: all
+       with same port number but different IP addresses (EJAB-389)(thanks
+       to Fabrice Colliot and Sergei Golovan). Better report in WebAdmin
+       of problem when starting a listener. The old configuration method
+       of ip tuple and inet6 is fully supported for backwards
+       compatibility, but is not documented in the Guide anymore.
+       * src/ejabberd_config.erl: Likewise
+       * src/mod_proxy65/mod_proxy65_stream.erl: Likewise
+       * src/mod_proxy65/mod_proxy65_service.erl: Likewise
+       * src/web/ejabberd_web_admin.erl: Likewise
+       * doc/guide.tex: Document the new way to configure IP address and
+       IP version of listener, undocument options ip and inet6
+       * doc/guide.html: Likewise
+
        * src/web/ejabberd_web_admin.erl: New appearance of WebAdmin logo,
        fixed logo-fill.
 
index 9d0fde157c0c7b3f8d6f5e821defb10016de1473..3282fc3d8dbe1e62ec33a2718a2ea90ec88dc753 100644 (file)
@@ -622,31 +622,59 @@ other different modules for some specific virtual hosts:
 will listen and what services will be run on them. Each element of the list is a
 tuple with the following elements:
 </P><UL CLASS="itemize"><LI CLASS="li-itemize">
-Port number.
-</LI><LI CLASS="li-itemize">Module that serves this port.
-</LI><LI CLASS="li-itemize">Options to this module.
-</LI></UL><P>
+Port number. Optionally also the IP address.
+</LI><LI CLASS="li-itemize">Listening module that serves this port.
+</LI><LI CLASS="li-itemize">Options for the TCP socket and for the listening module.
+</LI></UL><P>With the basic syntax the ports will listen on all IPv4 network addresses:
+</P><PRE CLASS="verbatim">{listen, [
+          {&lt;port-number&gt;, &lt;module&gt;, [&lt;options&gt;]},
+          {&lt;port-number&gt;, &lt;module&gt;, [&lt;options&gt;]},
+          ...
+          {&lt;port-number&gt;, &lt;module&gt;, [&lt;options&gt;]}
+         ]}.
+</PRE><P>It is possible to specify the IP address for a port using the full syntax:
+</P><PRE CLASS="verbatim">          {{&lt;port-number&gt;, &lt;ip-address&gt;}, &lt;module&gt;, [&lt;options&gt;]}
+</PRE><P> <A NAME="listened-port"></A> </P><!--TOC subsubsection Port Number and IP Address-->
+<H4 CLASS="subsubsection"><!--SEC ANCHOR --><A HREF="#listened-port">Port Number and IP Address</A></H4><!--SEC END --><P> <A NAME="listened-port"></A> </P><P>The port number defines which port to listen for incoming connections.
+It can be a Jabber/XMPP standard port 
+(see section <A HREF="#firewall">5.1</A>) or any other valid port number.</P><P>The IP address can be represented with a string
+or an Erlang tuple with decimal or hexadecimal numbers.
+The socket will listen only in that network interface.
+It is possible to specify a generic address,
+so <TT>ejabberd</TT> will listen in all addresses.
+Depending in the type of the IP address, IPv4 or IPv6 will be used.</P><P>Some example values for IP address:
+</P><UL CLASS="itemize"><LI CLASS="li-itemize">
+<CODE>"0.0.0.0"</CODE> to listen in all IPv4 network interfaces. This is the default value when no IP is specified.
+</LI><LI CLASS="li-itemize"><CODE>"::"</CODE> to listen in all IPv6 network interfaces
+</LI><LI CLASS="li-itemize"><CODE>"10.11.12.13"</CODE> is the IPv4 address <CODE>10.11.12.13</CODE>
+</LI><LI CLASS="li-itemize"><CODE>"::FFFF:127.0.0.1"</CODE> is the IPv6 address <CODE>::FFFF:127.0.0.1/128</CODE>
+</LI><LI CLASS="li-itemize"><CODE>{10, 11, 12, 13}</CODE> is the IPv4 address <CODE>10.11.12.13</CODE>
+</LI><LI CLASS="li-itemize"><CODE>{0, 0, 0, 0, 0, 65535, 32512, 1}</CODE> is the IPv6 address <CODE>::FFFF:127.0.0.1/128</CODE>
+</LI><LI CLASS="li-itemize"><CODE>{16#fdca, 16#8ab6, 16#a243, 16#75ef, 0, 0, 0, 1}</CODE> is the IPv6 address <CODE>FDCA:8AB6:A243:75EF::1/128</CODE>
+</LI></UL><P> <A NAME="listened-module"></A> </P><!--TOC subsubsection Listening Module-->
+<H4 CLASS="subsubsection"><!--SEC ANCHOR --><A HREF="#listened-module">Listening Module</A></H4><!--SEC END --><P> <A NAME="listened-module"></A> </P><P>
 The available modules, their purpose and the options allowed by each one are:
 </P><DL CLASS="description"><DT CLASS="dt-description">
 <B><TT>ejabberd_c2s</TT></B></DT><DD CLASS="dd-description">
 Handles c2s connections.<BR>
- Options: <TT>access</TT>, <TT>certfile</TT>, <TT>inet6</TT>,
-<TT>ip</TT>, <TT>max_stanza_size</TT>, <TT>shaper</TT>,
+ Options: <TT>access</TT>, <TT>certfile</TT>,
+<TT>max_stanza_size</TT>, <TT>shaper</TT>,
 <TT>starttls</TT>, <TT>starttls_required</TT>, <TT>tls</TT>,
 <TT>zlib</TT>
 </DD><DT CLASS="dt-description"><B><TT>ejabberd_s2s_in</TT></B></DT><DD CLASS="dd-description">
 Handles incoming s2s connections.<BR>
- Options: <TT>inet6</TT>, <TT>ip</TT>, <TT>max_stanza_size</TT>
+ Options: <TT>max_stanza_size</TT>
 </DD><DT CLASS="dt-description"><B><TT>ejabberd_service</TT></B></DT><DD CLASS="dd-description">
 Interacts with an <A HREF="http://www.ejabberd.im/tutorials-transports">external component</A>
 (as defined in the Jabber Component Protocol (<A HREF="http://www.xmpp.org/extensions/xep-0114.html">XEP-0114</A>).<BR>
- Options: <TT>access</TT>, <TT>hosts</TT>, <TT>inet6</TT>,
-<TT>ip</TT>, <TT>shaper</TT>, <TT>service_check_from</TT>
+ Options: <TT>access</TT>, <TT>hosts</TT>,
+<TT>shaper</TT>, <TT>service_check_from</TT>
 </DD><DT CLASS="dt-description"><B><TT>ejabberd_http</TT></B></DT><DD CLASS="dd-description">
 Handles incoming HTTP connections.<BR>
  Options: <TT>certfile</TT>, <TT>http_bind</TT>, <TT>http_poll</TT>,
-<TT>inet6</TT>, <TT>ip</TT>, <TT>request_handlers</TT>, <TT>tls</TT>, <TT>web_admin</TT><BR>
-</DD></DL><P>This is a detailed description of each option allowed by the listening modules:
+<TT>request_handlers</TT>, <TT>tls</TT>, <TT>web_admin</TT><BR>
+</DD></DL><P> <A NAME="listened-options"></A> </P><!--TOC subsubsection Options-->
+<H4 CLASS="subsubsection"><!--SEC ANCHOR --><A HREF="#listened-options">Options</A></H4><!--SEC END --><P> <A NAME="listened-options"></A> </P><P>This is a detailed description of each option allowed by the listening modules:
 </P><DL CLASS="description"><DT CLASS="dt-description">
 <B><TT>{access, &lt;access rule&gt;}</TT></B></DT><DD CLASS="dd-description"> This option defines
 access to the port. The default value is <TT>all</TT>.
@@ -685,13 +713,7 @@ do not allow outgoing sockets on port 5222.<P>If HTTP Polling is enabled, it wil
 is also needed in the Jabber client. Remark also that HTTP Polling can be
 interesting to host a web-based Jabber client such as
 <A HREF="http://jwchat.sourceforge.net/">JWChat</A>.
-</P></DD><DT CLASS="dt-description"><B><TT>inet6</TT></B></DT><DD CLASS="dd-description"> Set up the socket for IPv6 instead of IPv4.
-Note: this option is not required for S2S outgoing connections,
-because when ejabberd attempts to establish a S2S outgoing connection
-it first tries IPv4, and if that fails it attempts with IPv6.
-</DD><DT CLASS="dt-description"><B><TT>{ip, IPAddress}</TT></B></DT><DD CLASS="dd-description"> This option specifies which network
-interface to listen for. For example <CODE>{ip, {192, 168, 1, 1}}</CODE>.
-</DD><DT CLASS="dt-description"><B><TT>{max_stanza_size, Size}</TT></B></DT><DD CLASS="dd-description">
+</P></DD><DT CLASS="dt-description"><B><TT>{max_stanza_size, Size}</TT></B></DT><DD CLASS="dd-description">
 This option specifies an
 approximate maximum size in bytes of XML stanzas. Approximate,
 because it is calculated with the precision of one block of readed
@@ -736,7 +758,7 @@ is available on connections to the port. Client connections cannot use
 stream compression and stream encryption simultaneously. Hence, if you
 specify both <TT>tls</TT> (or <TT>ssl</TT>) and <TT>zlib</TT>, the latter
 option will not affect connections (there will be no stream compression).
-</DD></DL><P>There are some additional global options:
+</DD></DL><P>There are some additional global options that can be specified in the ejabberd configuration file (outside <TT>listen</TT>):
 </P><DL CLASS="description"><DT CLASS="dt-description">
 <B><TT>{s2s_use_starttls, true|false}</TT></B></DT><DD CLASS="dd-description">
 This option defines whether to
@@ -759,17 +781,18 @@ with a small list of trusted servers, or to block some specific servers.
 </DD><DT CLASS="dt-description"><B><TT>{s2s_max_retry_delay, Seconds}</TT></B></DT><DD CLASS="dd-description"> 
 The maximum allowed delay for retry to connect after a failed connection attempt.
 Specified in seconds. The default value is 300 seconds (5 minutes).
-</DD></DL><P>For example, the following simple configuration defines:
+</DD></DL><P> <A NAME="listened-examples"></A> </P><!--TOC subsubsection Examples-->
+<H4 CLASS="subsubsection"><!--SEC ANCHOR --><A HREF="#listened-examples">Examples</A></H4><!--SEC END --><P> <A NAME="listened-examples"></A> </P><P>For example, the following simple configuration defines:
 </P><UL CLASS="itemize"><LI CLASS="li-itemize">
 There are three domains. The default certificate file is <TT>server.pem</TT>.
 However, the c2s and s2s connections to the domain <TT>example.com</TT> use the file <TT>example_com.pem</TT>.
 </LI><LI CLASS="li-itemize">Port 5222 listens for c2s connections with STARTTLS,
 and also allows plain connections for old clients.
 </LI><LI CLASS="li-itemize">Port 5223 listens for c2s connections with the old SSL.
-</LI><LI CLASS="li-itemize">Port 5269 listens for s2s connections with STARTTLS.
+</LI><LI CLASS="li-itemize">Port 5269 listens for s2s connections with STARTTLS. The socket is set for IPv6 instead of IPv4.
 </LI><LI CLASS="li-itemize">Port 5280 listens for HTTP requests, and serves the HTTP Poll service.
 </LI><LI CLASS="li-itemize">Port 5281 listens for HTTP requests, and serves the Web Admin using HTTPS as explained in
-section&#XA0;<A HREF="#webadmin">4.2</A>.
+section&#XA0;<A HREF="#webadmin">4.2</A>. The socket only listens connections to the IP address 127.0.0.1.
 </LI></UL><PRE CLASS="verbatim">{hosts, ["example.com", "example.org", "example.net"]}.
 {listen,
  [
@@ -785,17 +808,17 @@ section&#XA0;<A HREF="#webadmin">4.2</A>.
                         tls, {certfile, "/etc/ejabberd/server.pem"},
                         {max_stanza_size, 65536}
                        ]},
-  {5269, ejabberd_s2s_in, [
-                           {shaper, s2s_shaper},
-                           {max_stanza_size, 131072}
-                          ]},
+  {{5269, "::"}, ejabberd_s2s_in, [
+                                   {shaper, s2s_shaper},
+                                   {max_stanza_size, 131072}
+                                  ]},
   {5280, ejabberd_http, [
                          http_poll
                         ]},
-  {5281, ejabberd_http, [
-                         web_admin,
-                         tls, {certfile, "/etc/ejabberd/server.pem"},
-                        ]}
+  {{5281, "127.0.0.1"}, ejabberd_http, [
+                                        web_admin,
+                                        tls, {certfile, "/etc/ejabberd/server.pem"},
+                                       ]}
  ]
 }.
 {s2s_use_starttls, true}.
@@ -803,21 +826,23 @@ section&#XA0;<A HREF="#webadmin">4.2</A>.
 {domain_certfile, "example.com", "/etc/ejabberd/example_com.pem"}.
 </PRE><P>In this example, the following configuration defines that:
 </P><UL CLASS="itemize"><LI CLASS="li-itemize">
-c2s connections are listened for on port 5222 and 5223 (SSL) and denied
+c2s connections are listened for on port 5222 (all IPv4 addresses) and
+on port 5223 (SSL, IP 192.168.0.1 and fdca:8ab6:a243:75ef::1) and denied
 for the user called &#X2018;<TT>bad</TT>&#X2019;.
-</LI><LI CLASS="li-itemize">s2s connections are listened for on port 5269 with STARTTLS for secured
-traffic enabled.
+</LI><LI CLASS="li-itemize">s2s connections are listened for on port 5269 (all IPv4 addresses) 
+with STARTTLS for secured traffic enabled. 
 Incoming and outgoing connections of remote Jabber servers are denied,
 only two servers can connect: "jabber.example.org" and "example.com".
-</LI><LI CLASS="li-itemize">Port 5280 is serving the Web Admin and the HTTP Polling service. Note
+</LI><LI CLASS="li-itemize">Port 5280 is serving the Web Admin and the HTTP Polling service
+in all the IPv4 addresses. Note
 that it is also possible to serve them on different ports. The second
 example in section&#XA0;<A HREF="#webadmin">4.2</A> shows how exactly this can be done.
-</LI><LI CLASS="li-itemize">All users except for the administrators have a traffic of limit
+</LI><LI CLASS="li-itemize">All users except for the administrators have a traffic of limit 
 1,000&#XA0;Bytes/second
 </LI><LI CLASS="li-itemize">The
 <A HREF="http://www.ejabberd.im/pyaimt">AIM transport</A>
-<TT>aim.example.org</TT> is connected to port 5233 with password
-&#X2018;<TT>aimsecret</TT>&#X2019;.
+<TT>aim.example.org</TT> is connected to port 5233 on localhost IP addresses
+(127.0.0.1 and ::1) with password &#X2018;<TT>aimsecret</TT>&#X2019;.
 </LI><LI CLASS="li-itemize">The ICQ transport JIT (<TT>icq.example.org</TT> and
 <TT>sms.example.org</TT>) is connected to port 5234 with password
 &#X2018;<TT>jitsecret</TT>&#X2019;.
@@ -843,13 +868,32 @@ connected to port 5237 with password &#X2018;<TT>ggsecret</TT>&#X2019;.
 {access, c2s_shaper, [{none, admin},
                       {normal, all}]}.
 {listen,
- [{5222, ejabberd_c2s,     [{access, c2s}, {shaper, c2s_shaper}]},
-  {5223, ejabberd_c2s,     [{access, c2s},
-                            ssl, {certfile, "/path/to/ssl.pem"}]},
-  {5269, ejabberd_s2s_in,  []},
-  {5280, ejabberd_http,    [http_poll, web_admin]},
-  {5233, ejabberd_service, [{hosts, ["aim.example.org"],
-                             [{password, "aimsecret"}]}]},
+ [{5222, ejabberd_c2s, [
+                        {access, c2s},
+                        {shaper, c2s_shaper}
+                       ]},
+  {{5223, {192, 168, 0, 1}}, ejabberd_c2s, [
+                                            {access, c2s},
+                                            ssl, {certfile, "/path/to/ssl.pem"}
+                                           ]},
+  {{5223, {16#fdca, 16#8ab6, 16#a243, 16#75ef, 0, 0, 0, 1}},
+   ejabberd_c2s, [
+                  {access, c2s},
+                  ssl, {certfile, "/path/to/ssl.pem"}
+                 ]},
+  {5269, ejabberd_s2s_in, []},
+  {{5280, {0, 0, 0, 0}}, ejabberd_http, [
+                                         http_poll,
+                                         web_admin
+                                        ]},
+  {{5233, {127, 0, 0, 1}}, ejabberd_service, [
+                                              {hosts, ["aim.example.org"],
+                                                 [{password, "aimsecret"}]}
+                                             ]},
+  {{5233, "::1"}, ejabberd_service, [
+                                     {hosts, ["aim.example.org"],
+                                        [{password, "aimsecret"}]}
+                                    ]},
   {5234, ejabberd_service, [{hosts, ["icq.example.org", "sms.example.org"],
                              [{password, "jitsecret"}]}]},
   {5235, ejabberd_service, [{hosts, ["msn.example.org"],
@@ -3073,16 +3117,20 @@ administer the virtual host <TT>example.com</TT>.
 </PRE></LI><LI CLASS="li-itemize">For security reasons, you can serve the Web Admin on a secured
 connection, on a port differing from the HTTP Polling interface, and bind it
 to the internal LAN IP. The Web Admin will be accessible by pointing your
-web browser to <CODE>https://192.168.1.1:5280/admin/</CODE>:
+web browser to <CODE>https://192.168.1.1:5282/admin/</CODE>:
 <PRE CLASS="verbatim">
 {hosts, ["example.org"]}.
 
 {listen,
  [
   ...
-  {5270, ejabberd_http,    [http_poll]},
-  {5280, ejabberd_http,    [web_admin, {ip, {192, 168, 1, 1}},
-                            tls, {certfile, "/usr/local/etc/server.pem"}]},
+  {5280, ejabberd_http, [
+                         http_poll
+                        ]},
+  {{5282, "192.168.1.1"}, ejabberd_http, [
+                                          web_admin,
+                                          tls, {certfile, "/usr/local/etc/server.pem"}
+                                         ]},
   ...
  ]}.
 </PRE></LI></UL><P>Certain pages in the ejabberd Web Admin contain a link to a related
index 6e2422af57405369538a848b3889d262c6015fae..bcf79b568332768481db1a8bb0061f1463430c22 100644 (file)
@@ -735,34 +735,80 @@ The option \option{listen} defines for which addresses and ports \ejabberd{}
 will listen and what services will be run on them. Each element of the list is a
 tuple with the following elements:
 \begin{itemize}
-\item Port number.
-\item Module that serves this port.
-\item Options to this module.
+\item Port number. Optionally also the IP address.
+\item Listening module that serves this port.
+\item Options for the TCP socket and for the listening module.
 \end{itemize}
 
+With the basic syntax the ports will listen on all IPv4 network addresses:
+\begin{verbatim}
+{listen, [
+          {<port-number>, <module>, [<options>]},
+          {<port-number>, <module>, [<options>]},
+          ...
+          {<port-number>, <module>, [<options>]}
+         ]}.
+\end{verbatim}
+
+It is possible to specify the IP address for a port using the full syntax:
+\begin{verbatim}
+          {{<port-number>, <ip-address>}, <module>, [<options>]}
+\end{verbatim}
+
+
+\makesubsubsection{listened-port}{Port Number and IP Address}
+
+The port number defines which port to listen for incoming connections.
+It can be a Jabber/XMPP standard port 
+(see section \ref{firewall}) or any other valid port number.
+
+The IP address can be represented with a string
+or an Erlang tuple with decimal or hexadecimal numbers.
+The socket will listen only in that network interface.
+It is possible to specify a generic address,
+so \ejabberd{} will listen in all addresses.
+Depending in the type of the IP address, IPv4 or IPv6 will be used.
+
+Some example values for IP address:
+\begin{itemize}
+\item \verb|"0.0.0.0"| to listen in all IPv4 network interfaces. This is the default value when no IP is specified.
+\item \verb|"::"| to listen in all IPv6 network interfaces
+\item \verb|"10.11.12.13"| is the IPv4 address \verb|10.11.12.13|
+\item \verb|"::FFFF:127.0.0.1"| is the IPv6 address \verb|::FFFF:127.0.0.1/128|
+\item \verb|{10, 11, 12, 13}| is the IPv4 address \verb|10.11.12.13|
+\item \verb|{0, 0, 0, 0, 0, 65535, 32512, 1}| is the IPv6 address \verb|::FFFF:127.0.0.1/128|
+\item \verb|{16#fdca, 16#8ab6, 16#a243, 16#75ef, 0, 0, 0, 1}| is the IPv6 address \verb|FDCA:8AB6:A243:75EF::1/128|
+\end{itemize}
+
+
+\makesubsubsection{listened-module}{Listening Module}
+
 \ind{modules!ejabberd\_c2s}\ind{modules!ejabberd\_s2s\_in}\ind{modules!ejabberd\_service}\ind{modules!ejabberd\_http}\ind{protocols!XEP-0114: Jabber Component Protocol}
 The available modules, their purpose and the options allowed by each one are:
 \begin{description}
   \titem{\texttt{ejabberd\_c2s}}
     Handles c2s connections.\\
-    Options: \texttt{access}, \texttt{certfile}, \texttt{inet6},
-    \texttt{ip}, \texttt{max\_stanza\_size}, \texttt{shaper},
+    Options: \texttt{access}, \texttt{certfile},
+    \texttt{max\_stanza\_size}, \texttt{shaper},
     \texttt{starttls}, \texttt{starttls\_required}, \texttt{tls},
     \texttt{zlib}
   \titem{\texttt{ejabberd\_s2s\_in}}
     Handles incoming s2s connections.\\
-    Options: \texttt{inet6}, \texttt{ip}, \texttt{max\_stanza\_size}
+    Options: \texttt{max\_stanza\_size}
   \titem{\texttt{ejabberd\_service}}
     Interacts with an \footahref{http://www.ejabberd.im/tutorials-transports}{external component}
     (as defined in the Jabber Component Protocol (\xepref{0114}).\\
-    Options: \texttt{access}, \texttt{hosts}, \texttt{inet6},
-    \texttt{ip}, \texttt{shaper}, \texttt{service\_check\_from}
+    Options: \texttt{access}, \texttt{hosts},
+    \texttt{shaper}, \texttt{service\_check\_from}
   \titem{\texttt{ejabberd\_http}}
     Handles incoming HTTP connections.\\
     Options: \texttt{certfile}, \texttt{http\_bind}, \texttt{http\_poll},
-    \texttt{inet6}, \texttt{ip}, \texttt{request\_handlers}, \texttt{tls}, \texttt{web\_admin}\\
+    \texttt{request\_handlers}, \texttt{tls}, \texttt{web\_admin}\\
 \end{description}
 
+
+\makesubsubsection{listened-options}{Options}
+
 This is a detailed description of each option allowed by the listening modules:
 \begin{description}
   \titem{\{access, <access rule>\}} \ind{options!access}This option defines
@@ -808,13 +854,7 @@ This is a detailed description of each option allowed by the listening modules:
     is also needed in the \Jabber{} client. Remark also that HTTP Polling can be
     interesting to host a web-based \Jabber{} client such as
     \footahref{http://jwchat.sourceforge.net/}{JWChat}.
-  \titem{inet6} \ind{options!inet6}\ind{IPv6}Set up the socket for IPv6 instead of IPv4.
-    Note: this option is not required for S2S outgoing connections,
-    because when ejabberd attempts to establish a S2S outgoing connection
-    it first tries IPv4, and if that fails it attempts with IPv6.
-  \titem{\{ip, IPAddress\}} \ind{options!ip}This option specifies which network
-    interface to listen for. For example \verb|{ip, {192, 168, 1, 1}}|.
-    \titem{\{max\_stanza\_size, Size\}}
+  \titem{\{max\_stanza\_size, Size\}}
     \ind{options!max\_stanza\_size}This option specifies an
     approximate maximum size in bytes of XML stanzas.  Approximate,
     because it is calculated with the precision of one block of readed
@@ -861,7 +901,7 @@ This is a detailed description of each option allowed by the listening modules:
     option will not affect connections (there will be no stream compression).
 \end{description}
 
-There are some additional global options:
+There are some additional global options that can be specified in the ejabberd configuration file (outside \term{listen}):
 \begin{description}
   \titem{\{s2s\_use\_starttls, true|false\}}
   \ind{options!s2s\_use\_starttls}\ind{STARTTLS}This option defines whether to
@@ -886,6 +926,8 @@ There are some additional global options:
   Specified in seconds. The default value is 300 seconds (5 minutes).
 \end{description}
 
+\makesubsubsection{listened-examples}{Examples}
+
 For example, the following simple configuration defines:
 \begin{itemize}
 \item There are three domains. The default certificate file is \term{server.pem}.
@@ -893,10 +935,10 @@ However, the c2s and s2s connections to the domain \term{example.com} use the fi
 \item Port 5222 listens for c2s connections with STARTTLS,
   and also allows plain connections for old clients.
 \item Port 5223 listens for c2s connections with the old SSL.
-\item Port 5269 listens for s2s connections with STARTTLS.
+\item Port 5269 listens for s2s connections with STARTTLS. The socket is set for IPv6 instead of IPv4.
 \item Port 5280 listens for HTTP requests, and serves the HTTP Poll service.
 \item Port 5281 listens for HTTP requests, and serves the Web Admin using HTTPS as explained in
-  section~\ref{webadmin}.
+  section~\ref{webadmin}. The socket only listens connections to the IP address 127.0.0.1.
 \end{itemize}
 \begin{verbatim}
 {hosts, ["example.com", "example.org", "example.net"]}.
@@ -914,17 +956,17 @@ However, the c2s and s2s connections to the domain \term{example.com} use the fi
                         tls, {certfile, "/etc/ejabberd/server.pem"},
                         {max_stanza_size, 65536}
                        ]},
-  {5269, ejabberd_s2s_in, [
-                           {shaper, s2s_shaper},
-                           {max_stanza_size, 131072}
-                          ]},
+  {{5269, "::"}, ejabberd_s2s_in, [
+                                   {shaper, s2s_shaper},
+                                   {max_stanza_size, 131072}
+                                  ]},
   {5280, ejabberd_http, [
                          http_poll
                         ]},
-  {5281, ejabberd_http, [
-                         web_admin,
-                         tls, {certfile, "/etc/ejabberd/server.pem"},
-                        ]}
+  {{5281, "127.0.0.1"}, ejabberd_http, [
+                                        web_admin,
+                                        tls, {certfile, "/etc/ejabberd/server.pem"},
+                                       ]}
  ]
 }.
 {s2s_use_starttls, true}.
@@ -934,21 +976,23 @@ However, the c2s and s2s connections to the domain \term{example.com} use the fi
 
 In this example, the following configuration defines that:
 \begin{itemize}
-\item c2s connections are listened for on port 5222 and 5223 (SSL) and denied
+\item c2s connections are listened for on port 5222 (all IPv4 addresses) and
+  on port 5223 (SSL, IP 192.168.0.1 and fdca:8ab6:a243:75ef::1) and denied
   for the user called `\term{bad}'.
-\item s2s connections are listened for on port 5269 with STARTTLS for secured
-  traffic enabled.
+\item s2s connections are listened for on port 5269 (all IPv4 addresses) 
+  with STARTTLS for secured traffic enabled. 
   Incoming and outgoing connections of remote Jabber servers are denied,
   only two servers can connect: "jabber.example.org" and "example.com".
-\item Port 5280 is serving the Web Admin and the HTTP Polling service. Note
+\item Port 5280 is serving the Web Admin and the HTTP Polling service
+  in all the IPv4 addresses. Note
   that it is also possible to serve them on different ports. The second
   example in section~\ref{webadmin} shows how exactly this can be done.
-\item All users except for the administrators have a traffic of limit
+\item All users except for the administrators have a traffic of limit 
   1,000\,Bytes/second
 \item \ind{transports!AIM}The
   \footahref{http://www.ejabberd.im/pyaimt}{AIM transport}
-  \jid{aim.example.org} is connected to port 5233 with password
-  `\term{aimsecret}'.
+  \jid{aim.example.org} is connected to port 5233 on localhost IP addresses
+  (127.0.0.1 and ::1) with password `\term{aimsecret}'.
 \item \ind{transports!ICQ}The ICQ transport JIT (\jid{icq.example.org} and
   \jid{sms.example.org}) is connected to port 5234 with password
   `\term{jitsecret}'.
@@ -976,13 +1020,32 @@ In this example, the following configuration defines that:
 {access, c2s_shaper, [{none, admin},
                       {normal, all}]}.
 {listen,
- [{5222, ejabberd_c2s,     [{access, c2s}, {shaper, c2s_shaper}]},
-  {5223, ejabberd_c2s,     [{access, c2s},
-                            ssl, {certfile, "/path/to/ssl.pem"}]},
-  {5269, ejabberd_s2s_in,  []},
-  {5280, ejabberd_http,    [http_poll, web_admin]},
-  {5233, ejabberd_service, [{hosts, ["aim.example.org"],
-                             [{password, "aimsecret"}]}]},
+ [{5222, ejabberd_c2s, [
+                        {access, c2s},
+                        {shaper, c2s_shaper}
+                       ]},
+  {{5223, {192, 168, 0, 1}}, ejabberd_c2s, [
+                                            {access, c2s},
+                                            ssl, {certfile, "/path/to/ssl.pem"}
+                                           ]},
+  {{5223, {16#fdca, 16#8ab6, 16#a243, 16#75ef, 0, 0, 0, 1}},
+   ejabberd_c2s, [
+                  {access, c2s},
+                  ssl, {certfile, "/path/to/ssl.pem"}
+                 ]},
+  {5269, ejabberd_s2s_in, []},
+  {{5280, {0, 0, 0, 0}}, ejabberd_http, [
+                                         http_poll,
+                                         web_admin
+                                        ]},
+  {{5233, {127, 0, 0, 1}}, ejabberd_service, [
+                                              {hosts, ["aim.example.org"],
+                                                 [{password, "aimsecret"}]}
+                                             ]},
+  {{5233, "::1"}, ejabberd_service, [
+                                     {hosts, ["aim.example.org"],
+                                        [{password, "aimsecret"}]}
+                                    ]},
   {5234, ejabberd_service, [{hosts, ["icq.example.org", "sms.example.org"],
                              [{password, "jitsecret"}]}]},
   {5235, ejabberd_service, [{hosts, ["msn.example.org"],
@@ -3940,7 +4003,7 @@ Examples:
 \item For security reasons, you can serve the Web Admin on a secured
   connection, on a port differing from the HTTP Polling interface, and bind it
   to the internal LAN IP. The Web Admin will be accessible by pointing your
-  web browser to \verb|https://192.168.1.1:5280/admin/|:
+  web browser to \verb|https://192.168.1.1:5282/admin/|:
 \begin{verbatim}
 
 {hosts, ["example.org"]}.
@@ -3948,9 +4011,13 @@ Examples:
 {listen,
  [
   ...
-  {5270, ejabberd_http,    [http_poll]},
-  {5280, ejabberd_http,    [web_admin, {ip, {192, 168, 1, 1}},
-                            tls, {certfile, "/usr/local/etc/server.pem"}]},
+  {5280, ejabberd_http, [
+                         http_poll
+                        ]},
+  {{5282, "192.168.1.1"}, ejabberd_http, [
+                                          web_admin,
+                                          tls, {certfile, "/usr/local/etc/server.pem"}
+                                         ]},
   ...
  ]}.
 \end{verbatim}
index c8005529ea97a7549f1d5cfb26ebb0d33fe759dc..ecf8ec30faff9eb145082ab473c368d4996d5c0b 100644 (file)
@@ -324,8 +324,16 @@ process_term(Term, State) ->
        {host_config, Host, Terms} ->
            lists:foldl(fun(T, S) -> process_host_term(T, Host, S) end,
                        State, Terms);
-       {listen, Val} ->
-           add_option(listen, Val, State);
+       {listen, Listeners} ->
+           Listeners2 =
+               lists:map(
+                 fun({PortIP, Module, Opts}) ->
+                         {Port, IPT, _, _, OptsClean} =
+                             ejabberd_listener:parse_listener_portip(PortIP, Opts),
+                         {{Port, IPT}, Module, OptsClean}
+                 end,
+                 Listeners),
+           add_option(listen, Listeners2, State);
        {language, Val} ->
            add_option(language, Val, State);
        {outgoing_s2s_port, Port} ->
index 93ae78a7c5ea7d887cdb38e0fe5eb2a82e397183..cd5ca0898b910734240edcc658c6db193e667abb 100644 (file)
@@ -32,6 +32,7 @@
         start_listeners/0,
         start_listener/3,
         stop_listener/2,
+        parse_listener_portip/2,
         add_listener/3,
         delete_listener/2
        ]).
@@ -53,12 +54,25 @@ start_listeners() ->
        undefined ->
            ignore;
        Ls ->
-           lists:map(
-             fun({Port, Module, Opts}) ->
-                     start_listener(Port, Module, Opts)
-             end, Ls)
+           Ls2 = lists:map(
+               fun({Port, Module, Opts}) ->
+                       start_listener(Port, Module, Opts)
+                   end, Ls),
+           report_duplicated_portips(Ls),
+           {ok, {{one_for_one, 10, 1}, Ls2}}
     end.
 
+report_duplicated_portips(L) ->
+    LKeys = [Port || {Port, _, _} <- L],
+    LNoDupsKeys = proplists:get_keys(L),
+    case LKeys -- LNoDupsKeys of
+       [] -> ok;
+       Dups ->
+           ?CRITICAL_MSG("In the ejabberd configuration there are duplicated "
+                         "Port number + IP address:~n  ~p",
+                         [Dups])
+  end.
+
 start(Port, Module, Opts) ->
     %% Check if the module is an ejabberd listener or an independent listener
     ModuleRaw = strip_frontend(Module),
@@ -67,11 +81,11 @@ start(Port, Module, Opts) ->
        _ -> start_dependent(Port, Module, Opts)
     end.
 
+%% -> {ok, Pid} | {error, ErrorMessage}
 start_dependent(Port, Module, Opts) ->
     case includes_deprecated_ssl_option(Opts) of
        false ->
-           {ok, proc_lib:spawn_link(?MODULE, init,
-                                    [Port, Module, Opts])};
+           proc_lib:start_link(?MODULE, init, [Port, Module, Opts]);
        true ->
             SSLErr="There is a problem with your ejabberd configuration file: "
                "the option 'ssl' for listening sockets is no longer available."
@@ -91,13 +105,16 @@ includes_deprecated_ssl_option(Opts) ->
            lists:member(ssl, Opts)
     end.
 
-init(Port, Module, Opts) ->
+init(PortIP, Module, Opts1) ->
+    {Port, IPT, IPS, IPV, OptsClean} = parse_listener_portip(PortIP, Opts1),
+    %% The first inet|inet6 and the last {ip, _} work,
+    %% so overriding those in Opts
+    Opts = [IPV | OptsClean] ++ [{ip, IPT}],
     SockOpts = lists:filter(fun({ip, _}) -> true;
                               (inet6) -> true;
                               (inet) -> true;
                               (_) -> false
                            end, Opts),
-
     Res = gen_tcp:listen(Port, [binary,
                                {packet, 0},
                                {active, false},
@@ -108,13 +125,82 @@ init(Port, Module, Opts) ->
                                SockOpts]),
     case Res of
        {ok, ListenSocket} ->
+               %% Inform my parent that this port was opened succesfully
+               proc_lib:init_ack({ok, self()}),
+               %% And now start accepting connection attempts
            accept(ListenSocket, Module, Opts);
        {error, Reason} ->
-           ?ERROR_MSG("Failed to open socket for ~p: ~p",
-                      [{Port, Module, Opts}, Reason]),
-           error
+           ReasonT = case Reason of
+                         eaddrnotavail -> "IP address not available: " ++ IPS;
+                         _ -> atom_to_list(Reason)
+                     end,
+           ?ERROR_MSG("Failed to open socket:~n  ~p~nReason: ~s",
+                      [{Port, Module, SockOpts}, ReasonT]),
+           throw(ReasonT)
+    end.
+
+%% @spec (PortIP, Opts) -> {Port, IPT, IPS, IPV, OptsClean}
+%% where
+%%      PortIP = Port | {Port, IPT | IPS}
+%%      Port = integer()
+%%      IPT = tuple()
+%%      IPS = string()
+%%      IPV = inet | inet6
+%%      Opts = [IPV | {ip, IPT} | atom() | tuple()]
+%%      OptsClean = [atom() | tuple()]
+%% @doc Parse any kind of ejabberd listener specification.
+%% The parsed options are returned in several formats.
+%% OptsClean does not include inet/inet6 or ip options.
+%% Opts can include the options inet6 and {ip, Tuple},
+%% but they are only used when no IP address was specified in the PortIP.
+%% The IP version (either IPv4 or IPv6) is inferred from the IP address type,
+%% so the option inet/inet6 is only used when no IP is specified at all.
+parse_listener_portip(PortIP, Opts) ->
+    {IPOpt, Opts2} = strip_ip_option(Opts),
+    {IPVOpt, OptsClean} = case lists:member(inet6, Opts2) of
+                             true -> {inet6, Opts2 -- [inet6]};
+                             false -> {inet, Opts2}
+                         end,
+    {Port, IPT, IPS} = case PortIP of
+                          P when is_integer(P) ->
+                              T = get_ip_tuple(IPOpt, IPVOpt),
+                              S = inet_parse:ntoa(T),
+                              {P, T, S};
+                          {P, T} when is_integer(P) and is_tuple(T) ->
+                              S = inet_parse:ntoa(T),
+                              {P, T, S};
+                          {P, S} when is_integer(P) and is_list(S) ->
+                              [S | _] = string:tokens(S, "/"),
+                              {ok, T} = inet_parse:address(S),
+                              {P, T, S}
+                      end,
+    IPV = case size(IPT) of
+             4 -> inet;
+             8 -> inet6
+         end,
+    {Port, IPT, IPS, IPV, OptsClean}.
+
+strip_ip_option(Opts) ->
+    {IPL, OptsNoIP} = lists:partition(
+                       fun({ip, _}) -> true;
+                          (_) -> false
+                       end,
+                       Opts),
+    case IPL of
+       %% Only the first ip option is considered
+       [{ip, T1} | _] when is_tuple(T1) ->
+           {T1, OptsNoIP};
+       [] ->
+           {no_ip_option, OptsNoIP}
     end.
 
+get_ip_tuple(no_ip_option, inet) ->
+    {0, 0, 0, 0};
+get_ip_tuple(no_ip_option, inet6) ->
+    {0, 0, 0, 0, 0, 0, 0, 0};
+get_ip_tuple(IPOpt, _IPVOpt) ->
+    IPOpt.
+
 accept(ListenSocket, Module, Opts) ->
     case gen_tcp:accept(ListenSocket) of
        {ok, Socket} ->
@@ -137,11 +223,14 @@ accept(ListenSocket, Module, Opts) ->
            accept(ListenSocket, Module, Opts)
     end.
 
+%% @spec (Port, Module, Opts) -> {ok, Pid} | {error, Error}
 start_listener(Port, Module, Opts) ->
+    %% It is only required to start the supervisor in some cases.
+    %% But it doesn't hurt to attempt to start it for any listener.
+    %% So, it's normal (and harmless) that in most cases this call returns: {error, {already_started, pid()}}
     start_module_sup(Port, Module),
     start_listener_sup(Port, Module, Opts).
 
-%% Only required for some listeners, but doing for all doesn't hurt
 start_module_sup(_Port, Module) ->
     Proc1 = gen_mod:get_module_proc("sup", Module),
     ChildSpec1 =
@@ -151,7 +240,7 @@ start_module_sup(_Port, Module) ->
         infinity,
         supervisor,
         [ejabberd_tmp_sup]},
-    catch supervisor:start_child(ejabberd_sup, ChildSpec1).
+    supervisor:start_child(ejabberd_sup, ChildSpec1).
 
 start_listener_sup(Port, Module, Opts) ->
     ChildSpec = {Port,
@@ -165,33 +254,55 @@ start_listener_sup(Port, Module, Opts) ->
 stop_listener(Port, Module) ->
     supervisor:terminate_child(ejabberd_listeners, Port),
     supervisor:delete_child(ejabberd_listeners, Port),
-
     Proc1 = gen_mod:get_module_proc("sup", Module),
     supervisor:terminate_child(ejabberd_sup, Proc1),
     supervisor:delete_child(ejabberd_sup, Proc1).
 
-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, Module) ->
+%% @spec (PortIP, Module, Opts) -> {ok, Pid} | {error, Error}
+%% where
+%%      PortIP = {Port, IPT | IPS}
+%%      Port = integer()
+%%      IPT = tuple()
+%%      IPS = string()
+%%      IPV = inet | inet6
+%%      Module = atom()
+%%      Opts = [IPV | {ip, IPT} | atom() | tuple()]
+%% @doc Add a listener and store in config if success
+add_listener(PortIP, Module, Opts) ->
+    case start_listener(PortIP, Module, Opts) of
+       {ok, _Pid} ->
+           Ports = case ejabberd_config:get_local_option(listen) of
+                       undefined ->
+                           [];
+                       Ls ->
+                           Ls
+                   end,
+           Ports1 = lists:keydelete(PortIP, 1, Ports),
+           Ports2 = [{PortIP, Module, Opts} | Ports1],
+           ejabberd_config:add_local_option(listen, Ports2),
+               ok;
+       {error, {already_started, _Pid}} ->
+           {error, {already_started, PortIP}};
+       {error, Error} ->
+           {error, Error}
+    end.
+  
+%% @spec (PortIP) -> ok
+%% where
+%%      PortIP = {Port, IPT | IPS}
+%%      Port = integer()
+%%      IPT = tuple()
+%%      IPS = string()
+delete_listener(PortIP, Module) ->
     Ports = case ejabberd_config:get_local_option(listen) of
                undefined ->
                    [];
                Ls ->
                    Ls
            end,
-    Ports1 = lists:keydelete(Port, 1, Ports),
+    Ports1 = lists:keydelete(PortIP, 1, Ports),
     ejabberd_config:add_local_option(listen, Ports1),
-    stop_listener(Port, Module).
+    stop_listener(PortIP, Module).
 
 is_frontend({frontend, _Module}) -> true;
 is_frontend(_) -> false.
index ba95ad4a31895138e3756447a188652b09c23fac..ee11e3db05eccf4ae19333cff7ddff8ce236b655 100644 (file)
@@ -52,6 +52,7 @@
          name,
          stream_addr,
          port,
+         ip,
          acl
         }).
 
@@ -65,7 +66,7 @@ start_link(Host, Opts) ->
     gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
 
 init([Host, Opts]) ->
-    {_IP, State} = parse_options(Host, Opts),
+    State = parse_options(Host, Opts),
     ejabberd_router:register_route(State#state.myhost),
     {ok, State}.
 
@@ -89,8 +90,8 @@ handle_info({route, From, To, {xmlelement, "iq", _, _} = Packet}, State) ->
 handle_info(_Info, State) ->
     {noreply, State}.
 
-handle_call(get_port, _From, State) ->
-    {reply, {port, State#state.port}, State};
+handle_call(get_port_ip, _From, State) ->
+    {reply, {port_ip, State#state.port, State#state.ip}, State};
 handle_call(_Request, _From, State) ->
     {reply, ok, State}.
 
@@ -105,14 +106,14 @@ code_change(_OldVsn, State, _Extra) ->
 %%%------------------------
 
 add_listener(Host, Opts) ->
-    {IP, State} = parse_options(Host, Opts),
-    NewOpts = [Host, {ip, IP} | Opts],
-    ejabberd_listener:add_listener(State#state.port,mod_proxy65_stream,NewOpts).
+    State = parse_options(Host, Opts),
+    NewOpts = [Host | Opts],
+    ejabberd_listener:add_listener({State#state.port, State#state.ip}, mod_proxy65_stream, NewOpts).
 
 delete_listener(Host) ->
     Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
-    {port, Port} = gen_server:call(Proc, get_port),
-    catch ejabberd_listener:delete_listener(Port, mod_proxy65_stream).
+    {port_ip, Port, IP} = gen_server:call(Proc, get_port_ip),
+    catch ejabberd_listener:delete_listener({Port, IP}, mod_proxy65_stream).
 
 %%%------------------------
 %%% IQ Processing
@@ -219,12 +220,13 @@ parse_options(ServerHost, Opts) ->
             end,
     StrIP = inet_parse:ntoa(IP),
     StreamAddr = [{"jid", MyHost}, {"host", StrIP}, {"port", integer_to_list(Port)}],
-    {IP, #state{myhost      = MyHost,
+    #state{myhost      = MyHost,
                serverhost  = ServerHost,
                name        = Name,
                port        = Port,
+               ip          = IP,
                stream_addr = StreamAddr, 
-               acl         = ACL}}.
+               acl         = ACL}.
 
 %% Return the IP of the proxy host, or if not found, the ip of the xmpp domain
 get_proxy_or_domainip(ServerHost, MyHost) ->
index 40505041bda1c9c8d4b832499cafaed748c82ca9..c6d0103ae233b47437c9f01973532ef59a17b481 100644 (file)
@@ -78,7 +78,8 @@ code_change(_OldVsn, StateName, StateData, _Extra) ->
     {ok, StateName, StateData}.
 %%-------------------------------
 
-start({gen_tcp, Socket}, [Host | Opts]) ->
+start({gen_tcp, Socket}, Opts1) ->
+    {[Host], Opts} = lists:partition(fun(O) -> is_list(O) end, Opts1),
     Supervisor = gen_mod:get_module_proc(Host, ejabberd_mod_proxy65_sup),
     supervisor:start_child(Supervisor, [Socket, Host, Opts]).
 
index 1cbb20dc16a8026cf81b9cfb4b16c0475943b25c..cf3150b90b53bf21681944709c914935e653470b 100644 (file)
@@ -1809,9 +1809,14 @@ get_node(global, Node, ["ports"], Query, Lang) ->
                  ok;
              {'EXIT', _Reason} ->
                  error;
+             {is_added, ok} ->
+                 ok;
+             {is_added, {error, Reason}} ->
+                 {error, io_lib:format("~p", [Reason])};
              _ ->
                  nothing
          end,
+    %% TODO: This sorting does not work when [{{Port, IP}, Module, Opts}]
     NewPorts = lists:sort(
                 rpc:call(Node, ejabberd_config, get_local_option, [listen])),
     H1String = ?T("Listened Ports at ") ++ atom_to_list(Node),
@@ -1819,6 +1824,7 @@ get_node(global, Node, ["ports"], Query, Lang) ->
        case Res of
            ok -> [?CT("Submitted"), ?P];
            error -> [?CT("Bad format"), ?P];
+           {error, ReasonT} -> [?CT("Problem: "), ?C(ReasonT), ?P];
            nothing -> []
        end ++
        [?XAE("form", [{"action", ""}, {"method", "post"}],
@@ -2057,58 +2063,77 @@ node_ports_to_xhtml(Ports, Lang) ->
         [?XE("thead",
              [?XE("tr",
                   [?XCT("td", "Port"),
+                   ?XCT("td", "IP"),
                    ?XCT("td", "Module"),
                    ?XCT("td", "Options")
                   ])]),
          ?XE("tbody",
              lists:map(
-               fun({Port, Module, Opts} = _E) ->
-                       SPort = integer_to_list(Port),
+               fun({PortIP, Module, Opts} = _E) ->
+                       {_Port, SPort, _TIP, SIP, SSPort, OptsClean} =
+                           get_port_data(PortIP, Opts),
                        SModule = atom_to_list(Module),
                        %%ID = term_to_id(E),
                        ?XE("tr",
-                           [?XC("td", SPort),
-                            ?XE("td", [?INPUT("text", "module" ++ SPort,
-                                              SModule)]),
-                            ?XE("td", [?INPUTS("text", "opts" ++ SPort,
-                                               term_to_string(Opts), "40")]),
-                            ?XE("td", [?INPUTT("submit", "add" ++ SPort,
+                           [?XAE("td", [{"size", "6"}], [?C(SPort)]),
+                            ?XAE("td", [{"size", "15"}], [?C(SIP)]),
+                            ?XE("td", [?INPUTS("text", "module" ++ SSPort,
+                                               SModule, "15")]),
+                            ?XE("td", [?INPUTS("text", "opts" ++ SSPort,
+                                               term_to_string(OptsClean), "40")]),
+                            ?XE("td", [?INPUTT("submit", "add" ++ SSPort,
                                                "Update")]),
-                            ?XE("td", [?INPUTT("submit", "delete" ++ SPort,
+                            ?XE("td", [?INPUTT("submit", "delete" ++ SSPort,
                                                "Delete")])
                            ]
                           )
                end, Ports) ++
              [?XE("tr",
                   [?XE("td", [?INPUTS("text", "portnew", "", "6")]),
-                   ?XE("td", [?INPUT("text", "modulenew", "")]),
-                   ?XE("td", [?INPUTS("text", "optsnew", "", "40")]),
+                   ?XE("td", [?INPUTS("text", "ipnew", "0.0.0.0", "15")]),
+                   ?XE("td", [?INPUTS("text", "modulenew", "", "17")]),
+                   ?XE("td", [?INPUTS("text", "optsnew", "[]", "40")]),
                    ?XAE("td", [{"colspan", "2"}],
                         [?INPUTT("submit", "addnew", "Add New")])
                   ]
                  )]
             )]).
 
+get_port_data(PortIP, Opts) ->
+    {Port, IPT, IPS, _IPV, OptsClean} = ejabberd_listener:parse_listener_portip(PortIP, Opts),
+    SPort = io_lib:format("~p", [Port]),
+
+    SSPort = lists:flatten(
+              lists:map(
+                fun(N) -> io_lib:format("~.16b", [N]) end,
+                binary_to_list(crypto:md5(SPort++IPS)))),
+    {Port, SPort, IPT, IPS, SSPort, OptsClean}.
+  
 
 node_ports_parse_query(Node, Ports, Query) ->
     lists:foreach(
-      fun({Port, Module1, _Opts1}) ->
-             SPort = integer_to_list(Port),
-             case lists:keysearch("add" ++ SPort, 1, Query) of
+      fun({PortIP, Module1, Opts1}) ->
+             {Port, _SPort, TIP, _SIP, SSPort, _OptsClean} =
+                 get_port_data(PortIP, Opts1),
+             case lists:keysearch("add" ++ SSPort, 1, Query) of
                  {value, _} ->
+                     PortIP2 = {Port, TIP},
                      {{value, {_, SModule}}, {value, {_, SOpts}}} =
-                         {lists:keysearch("module" ++ SPort, 1, Query),
-                          lists:keysearch("opts" ++ SPort, 1, Query)},
+                         {lists:keysearch("module" ++ SSPort, 1, Query),
+                          lists:keysearch("opts" ++ SSPort, 1, Query)},
                      Module = list_to_atom(SModule),
                      {ok, Tokens, _} = erl_scan:string(SOpts ++ "."),
                      {ok, Opts} = erl_parse:parse_term(Tokens),
-                     rpc:call(Node, ejabberd_listener, delete_listener, [Port, Module]),
-                     rpc:call(Node, ejabberd_listener, add_listener, [Port, Module, Opts]),
-                     throw(submitted);
+                     rpc:call(Node, ejabberd_listener, delete_listener,
+                              [PortIP2, Module1]),
+                     R=rpc:call(Node, ejabberd_listener, add_listener,
+                                [PortIP2, Module, Opts]),
+                     throw({is_added, R});
                  _ ->
-                     case lists:keysearch("delete" ++ SPort, 1, Query) of
+                     case lists:keysearch("delete" ++ SSPort, 1, Query) of
                          {value, _} ->
-                             rpc:call(Node, ejabberd_listener, delete_listener, [Port, Module1]),
+                             rpc:call(Node, ejabberd_listener, delete_listener,
+                                      [PortIP, Module1]),
                              throw(submitted);
                          _ ->
                              ok
@@ -2118,17 +2143,28 @@ node_ports_parse_query(Node, Ports, Query) ->
     case lists:keysearch("addnew", 1, Query) of
        {value, _} ->
            {{value, {_, SPort}},
+            {value, {_, STIP}}, %% It is a string that may represent a tuple
             {value, {_, SModule}},
             {value, {_, SOpts}}} =
                {lists:keysearch("portnew", 1, Query),
+                lists:keysearch("ipnew", 1, Query),
                 lists:keysearch("modulenew", 1, Query),
                 lists:keysearch("optsnew", 1, Query)},
-           Port = list_to_integer(SPort),
+           {ok, Toks, _} = erl_scan:string(SPort ++ "."),
+           {ok, Port2} = erl_parse:parse_term(Toks),
+           {ok, ToksIP, _} = erl_scan:string(STIP ++ "."),
+           STIP2 = case erl_parse:parse_term(ToksIP) of
+                       {ok, IPTParsed} -> IPTParsed;
+                       {error, _} -> STIP
+                   end,
            Module = list_to_atom(SModule),
            {ok, Tokens, _} = erl_scan:string(SOpts ++ "."),
            {ok, Opts} = erl_parse:parse_term(Tokens),
-           rpc:call(Node, ejabberd_listener, add_listener, [Port, Module, Opts]),
-           throw(submitted);
+           {Port2, _SPort, IP2, _SIP, _SSPort, OptsClean} =
+               get_port_data({Port2, STIP2}, Opts),
+           R=rpc:call(Node, ejabberd_listener, add_listener,
+                      [{Port2, IP2}, Module, OptsClean]),
+           throw({is_added, R});
        _ ->
            ok
     end.