MINOR: tcp: add dst_is_local and src_is_local

It is sometimes needed in application server environments to easily tell
if a source is local to the machine or a remote one, without necessarily
knowing all the local addresses (dhcp, vrrp, etc). Similarly in transparent
proxy configurations it is sometimes desired to tell the difference between
local and remote destination addresses.

This patch adds two new sample fetch functions for this :

dst_is_local : boolean
  Returns true if the destination address of the incoming connection is local
  to the system, or false if the address doesn't exist on the system, meaning
  that it was intercepted in transparent mode. It can be useful to apply
  certain rules by default to forwarded traffic and other rules to the traffic
  targetting the real address of the machine. For example the stats page could
  be delivered only on this address, or SSH access could be locally redirected.
  Please note that the check involves a few system calls, so it's better to do
  it only once per connection.

src_is_local : boolean
  Returns true if the source address of the incoming connection is local to the
  system, or false if the address doesn't exist on the system, meaning that it
  comes from a remote machine. Note that UNIX addresses are considered local.
  It can be useful to apply certain access restrictions based on where the
  client comes from (eg: require auth or https for remote machines). Please
  note that the check involves a few system calls, so it's better to do it only
  once per connection.
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index 717ba28..f7610df 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -2299,6 +2299,48 @@
 	return 1;
 }
 
+/* check if the destination address of the front connection is local to the
+ * system or if it was intercepted.
+ */
+int smp_fetch_dst_is_local(const struct arg *args, struct sample *smp, const char *kw, void *private)
+{
+	struct connection *conn = objt_conn(smp->sess->origin);
+	struct listener *li = smp->sess->listener;
+
+	if (!conn)
+		return 0;
+
+	conn_get_to_addr(conn);
+	if (!(conn->flags & CO_FL_ADDR_TO_SET))
+		return 0;
+
+	smp->data.type = SMP_T_BOOL;
+	smp->flags = 0;
+	smp->data.u.sint = addr_is_local(li->netns, &conn->addr.to);
+	return smp->data.u.sint >= 0;
+}
+
+/* check if the source address of the front connection is local to the system
+ * or not.
+ */
+int smp_fetch_src_is_local(const struct arg *args, struct sample *smp, const char *kw, void *private)
+{
+	struct connection *conn = objt_conn(smp->sess->origin);
+	struct listener *li = smp->sess->listener;
+
+	if (!conn)
+		return 0;
+
+	conn_get_from_addr(conn);
+	if (!(conn->flags & CO_FL_ADDR_FROM_SET))
+		return 0;
+
+	smp->data.type = SMP_T_BOOL;
+	smp->flags = 0;
+	smp->data.u.sint = addr_is_local(li->netns, &conn->addr.from);
+	return smp->data.u.sint >= 0;
+}
+
 /* set temp integer to the frontend connexion's destination port */
 static int
 smp_fetch_dport(const struct arg *args, struct sample *smp, const char *kw, void *private)
@@ -2620,8 +2662,10 @@
  */
 static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
 	{ "dst",      smp_fetch_dst,   0, NULL, SMP_T_IPV4, SMP_USE_L4CLI },
+	{ "dst_is_local", smp_fetch_dst_is_local, 0, NULL, SMP_T_BOOL, SMP_USE_L4CLI },
 	{ "dst_port", smp_fetch_dport, 0, NULL, SMP_T_SINT, SMP_USE_L4CLI },
 	{ "src",      smp_fetch_src,   0, NULL, SMP_T_IPV4, SMP_USE_L4CLI },
+	{ "src_is_local", smp_fetch_src_is_local, 0, NULL, SMP_T_BOOL, SMP_USE_L4CLI },
 	{ "src_port", smp_fetch_sport, 0, NULL, SMP_T_SINT, SMP_USE_L4CLI },
 #ifdef TCP_INFO
 	{ "fc_rtt",    smp_fetch_fc_rtt,    ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_L4CLI },
diff --git a/src/standard.c b/src/standard.c
index f002573..c2d1689 100644
--- a/src/standard.c
+++ b/src/standard.c
@@ -11,12 +11,14 @@
  */
 
 #include <ctype.h>
+#include <errno.h>
 #include <netdb.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
+#include <unistd.h>
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <netinet/in.h>
@@ -1406,6 +1408,47 @@
 	return addr->ss_family;
 }
 
+/* check if the given address is local to the system or not. It will return
+ * -1 when it's not possible to know, 0 when the address is not local, 1 when
+ * it is. We don't want to iterate over all interfaces for this (and it is not
+ * portable). So instead we try to bind in UDP to this address on a free non
+ * privileged port and to connect to the same address, port 0 (connect doesn't
+ * care). If it succeeds, we own the address. Note that non-inet addresses are
+ * considered local since they're most likely AF_UNIX.
+ */
+int addr_is_local(const struct netns_entry *ns,
+                  const struct sockaddr_storage *orig)
+{
+	struct sockaddr_storage addr;
+	int result;
+	int fd;
+
+	if (!is_inet_addr(orig))
+		return 1;
+
+	memcpy(&addr, orig, sizeof(addr));
+	set_host_port(&addr, 0);
+
+	fd = my_socketat(ns, addr.ss_family, SOCK_DGRAM, IPPROTO_UDP);
+	if (fd < 0)
+		return -1;
+
+	result = -1;
+	if (bind(fd, (struct sockaddr *)&addr, get_addr_len(&addr)) == 0) {
+		if (connect(fd, (struct sockaddr *)&addr, get_addr_len(&addr)) == -1)
+			result = 0; // fail, non-local address
+		else
+			result = 1; // success, local address
+	}
+	else {
+		if (errno == EADDRNOTAVAIL)
+			result = 0; // definitely not local :-)
+	}
+	close(fd);
+
+	return result;
+}
+
 /* will try to encode the string <string> replacing all characters tagged in
  * <map> with the hexadecimal representation of their ASCII-code (2 digits)
  * prefixed by <escape>, and will store the result between <start> (included)