Merge "Apply local Android modifications to ping."
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..705cf38
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES:= ping.c ping_common.c
+LOCAL_MODULE := ping
+LOCAL_CFLAGS := -DWITHOUT_IFADDRS -Wno-sign-compare
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_CFLAGS := -DWITHOUT_IFADDRS -Wno-sign-compare
+LOCAL_SRC_FILES := ping6.c ping_common.c
+LOCAL_MODULE := ping6
+LOCAL_MODULE_TAGS := debug
+LOCAL_C_INCLUDES := external/openssl/include
+LOCAL_SHARED_LIBRARIES := libcrypto
+include $(BUILD_EXECUTABLE)
diff --git a/CleanSpec.mk b/CleanSpec.mk
new file mode 100644
index 0000000..b84e1b6
--- /dev/null
+++ b/CleanSpec.mk
@@ -0,0 +1,49 @@
+# Copyright (C) 2007 The Android Open Source Project
+#
+# 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.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list.  These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+#     $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+#     $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list.  E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..59bdf39
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,33 @@
+Copyright (c) 1989 The Regents of the University of California.
+All rights reserved.
+
+This code is derived from software contributed to Berkeley by
+Mike Muuss.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. All advertising materials mentioning features or use of this software
+   must display the following acknowledgement:
+     This product includes software developed by the University of
+     California, Berkeley and its contributors.
+4. Neither the name of the University nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/in6_flowlabel.h b/in6_flowlabel.h
index 68b58d6..9a27397 100644
--- a/in6_flowlabel.h
+++ b/in6_flowlabel.h
@@ -1,39 +1,4 @@
-/*
-   It is just a stripped copy of the kernel header "linux/in6.h"
-
-   "Flow label" things are still not defined in "netinet/in*.h" headers,
-   but we cannot use "linux/in6.h" immediately because it currently
-   conflicts with "netinet/in.h" .
-*/
-
-struct in6_flowlabel_req
-{
-	struct in6_addr	flr_dst;
-	__u32	flr_label;
-	__u8	flr_action;
-	__u8	flr_share;
-	__u16	flr_flags;
-	__u16 	flr_expires;
-	__u16	flr_linger;
-	__u32	__flr_pad;
-	/* Options in format of IPV6_PKTOPTIONS */
-};
-
-#define IPV6_FL_A_GET	0
-#define IPV6_FL_A_PUT	1
-#define IPV6_FL_A_RENEW	2
-
-#define IPV6_FL_F_CREATE	1
-#define IPV6_FL_F_EXCL		2
-
-#define IPV6_FL_S_NONE		0
-#define IPV6_FL_S_EXCL		1
-#define IPV6_FL_S_PROCESS	2
-#define IPV6_FL_S_USER		3
-#define IPV6_FL_S_ANY		255
-
-#define IPV6_FLOWINFO_FLOWLABEL		0x000fffff
-#define IPV6_FLOWINFO_PRIORITY		0x0ff00000
-
-#define IPV6_FLOWLABEL_MGR	32
-#define IPV6_FLOWINFO_SEND	33
+/* The in6_flowlabel.h file in the iputils distribution exists to provide
+ * kernel flowlabel API definitions that are not in the userspace headers
+ * because they are linux-specific. It's not needed on Android because Android
+ * exposes the kernel definitions to userspace directly . */
diff --git a/ping.c b/ping.c
index c0366cd..25bbc3c 100644
--- a/ping.c
+++ b/ping.c
@@ -73,6 +73,10 @@
 };
 #endif
 
+#ifdef ANDROID
+#include <sys/auxv.h>
+#define bcmp(a, b, c) memcmp(a, b, c)
+#endif
 
 #define	MAXIPLEN	60
 #define	MAXICMPLEN	76
@@ -91,6 +95,7 @@
 int optlen = 0;
 int settos = 0;			/* Set TOS, Precendence or other QOS options */
 int icmp_sock;			/* socket file descriptor */
+int using_ping_socket = 0;
 u_char outpack[0x10000];
 int maxpacket = sizeof(outpack);
 
@@ -115,7 +120,6 @@
 char *device;
 int pmtudisc = -1;
 
-
 int
 main(int argc, char **argv)
 {
@@ -131,6 +135,13 @@
 #endif
 	char rspace[3 + 4 * NROUTES + 1];	/* record route space */
 
+#ifdef ANDROID
+	if (getauxval(AT_SECURE) != 0) {
+		fprintf(stderr, "This version of ping should NOT run with privileges. Aborting\n");
+		exit(1);
+	}
+#endif
+
 	limit_capabilities();
 
 #ifdef USE_IDN
@@ -139,7 +150,11 @@
 
 	enable_capability_raw();
 
-	icmp_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+	icmp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
+	if (icmp_sock != -1)
+		using_ping_socket = 1;
+	else
+		icmp_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
 	socket_errno = errno;
 
 	disable_capability_raw();
@@ -453,13 +468,35 @@
 		}
 	}
 
-	if ((options&F_STRICTSOURCE) &&
-	    bind(icmp_sock, (struct sockaddr*)&source, sizeof(source)) == -1) {
-		perror("bind");
-		exit(2);
+	if (!using_ping_socket) {
+		if ((options&F_STRICTSOURCE) &&
+		    bind(icmp_sock, (struct sockaddr*)&source, sizeof(source)) == -1) {
+			perror("bind");
+			exit(2);
+		}
+	} else {
+		struct sockaddr_in sa;
+		socklen_t sl;
+
+		sa.sin_family = AF_INET;
+		sa.sin_port = 0;
+		sa.sin_addr.s_addr = (options&F_STRICTSOURCE) ?
+			source.sin_addr.s_addr : 0;
+		sl = sizeof(sa);
+
+		if (bind(icmp_sock, (struct sockaddr *) &sa, sl) == -1) {
+			perror("bind");
+			exit(2);
+		}
+
+		if (getsockname(icmp_sock, (struct sockaddr *) &sa, &sl) == -1) {
+			perror("getsockname");
+			exit(2);
+		}
+		ident = sa.sin_port;
 	}
 
-	if (1) {
+	if (!using_ping_socket) {
 		struct icmp_filter filt;
 		filt.data = ~((1<<ICMP_SOURCE_QUENCH)|
 			      (1<<ICMP_DEST_UNREACH)|
@@ -474,6 +511,12 @@
 	hold = 1;
 	if (setsockopt(icmp_sock, SOL_IP, IP_RECVERR, (char *)&hold, sizeof(hold)))
 		fprintf(stderr, "WARNING: your kernel is veeery old. No problems.\n");
+	if (using_ping_socket) {
+		if (setsockopt(icmp_sock, SOL_IP, IP_RECVTTL, (char *)&hold, sizeof(hold)))
+			perror("WARNING: setsockopt(IP_RECVTTL)");
+		if (setsockopt(icmp_sock, SOL_IP, IP_RETOPTS, (char *)&hold, sizeof(hold)))
+			perror("WARNING: setsockopt(IP_RETOPTS)");
+	}
 
 	/* record route option */
 	if (options & F_RROUTE) {
@@ -642,6 +685,7 @@
 		nerrors++;
 	} else if (e->ee_origin == SO_EE_ORIGIN_ICMP) {
 		struct sockaddr_in *sin = (struct sockaddr_in*)(e+1);
+		int error_pkt;
 
 		if (res < sizeof(icmph) ||
 		    target.sin_addr.s_addr != whereto.sin_addr.s_addr ||
@@ -652,9 +696,18 @@
 			goto out;
 		}
 
-		acknowledge(ntohs(icmph.un.echo.sequence));
+		error_pkt = (e->ee_type != ICMP_REDIRECT &&
+			     e->ee_type != ICMP_SOURCE_QUENCH);
+		if (error_pkt) {
+			acknowledge(ntohs(icmph.un.echo.sequence));
+			net_errors++;
+			nerrors++;
+		}
+		else {
+			saved_errno = 0;
+		}
 
-		if (!working_recverr) {
+		if (!using_ping_socket && !working_recverr) {
 			struct icmp_filter filt;
 			working_recverr = 1;
 			/* OK, it works. Add stronger filter. */
@@ -665,15 +718,14 @@
 				perror("\rWARNING: setsockopt(ICMP_FILTER)");
 		}
 
-		net_errors++;
-		nerrors++;
 		if (options & F_QUIET)
 			goto out;
 		if (options & F_FLOOD) {
-			write_stdout("\bE", 2);
+			if (error_pkt)
+				write_stdout("\bE", 2);
 		} else {
 			print_timestamp();
-			printf("From %s icmp_seq=%u ", pr_addr(sin->sin_addr.s_addr), ntohs(icmph.un.echo.sequence));
+			printf("From %s: icmp_seq=%u ", pr_addr(sin->sin_addr.s_addr), ntohs(icmph.un.echo.sequence));
 			pr_icmph(e->ee_type, e->ee_code, e->ee_info, NULL);
 			fflush(stdout);
 		}
@@ -765,15 +817,41 @@
 	struct iphdr *ip;
 	int hlen;
 	int csfailed;
+	struct cmsghdr *cmsg;
+	int ttl;
+	__u8 *opts;
+	int optlen;
 
 	/* Check the IP header */
 	ip = (struct iphdr *)buf;
-	hlen = ip->ihl*4;
-	if (cc < hlen + 8 || ip->ihl < 5) {
-		if (options & F_VERBOSE)
-			fprintf(stderr, "ping: packet too short (%d bytes) from %s\n", cc,
-				pr_addr(from->sin_addr.s_addr));
-		return 1;
+	if (!using_ping_socket) {
+		hlen = ip->ihl*4;
+		if (cc < hlen + 8 || ip->ihl < 5) {
+			if (options & F_VERBOSE)
+				fprintf(stderr, "ping: packet too short (%d bytes) from %s\n", cc,
+					pr_addr(from->sin_addr.s_addr));
+			return 1;
+		}
+		ttl = ip->ttl;
+		opts = buf + sizeof(struct iphdr);
+		optlen = hlen - sizeof(struct iphdr);
+	} else {
+		hlen = 0;
+		ttl = 0;
+		opts = buf;
+		optlen = 0;
+		for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+			if (cmsg->cmsg_level != SOL_IP)
+				continue;
+			if (cmsg->cmsg_type == IP_TTL) {
+				if (cmsg->cmsg_len < sizeof(int))
+					continue;
+				ttl = *(int *) CMSG_DATA(cmsg);
+			} else if (cmsg->cmsg_type == IP_RETOPTS) {
+				opts = (__u8 *) CMSG_DATA(cmsg);
+				optlen = cmsg->cmsg_len;
+			}
+		}
 	}
 
 	/* Now the ICMP part */
@@ -786,7 +864,7 @@
 			return 1;			/* 'Twas not our ECHO */
 		if (gather_statistics((__u8*)icp, sizeof(*icp), cc,
 				      ntohs(icp->un.echo.sequence),
-				      ip->ttl, 0, tv, pr_addr(from->sin_addr.s_addr),
+				      ttl, 0, tv, pr_addr(from->sin_addr.s_addr),
 				      pr_echo_reply))
 			return 0;
 	} else {
@@ -877,7 +955,7 @@
 	}
 
 	if (!(options & F_FLOOD)) {
-		pr_options(buf + sizeof(struct iphdr), hlen);
+		pr_options(opts, optlen + sizeof(struct iphdr));
 
 		if (options & F_AUDIBLE)
 			putchar('\a');
@@ -1022,8 +1100,7 @@
 			printf("Redirect, Bad Code: %d", code);
 			break;
 		}
-		if (icp)
-			printf("(New nexthop: %s)\n", pr_addr(icp->un.gateway));
+		printf("(New nexthop: %s)\n", pr_addr(icp ? icp->un.gateway : info));
 		if (icp && (options & F_VERBOSE))
 			pr_iph((struct iphdr*)(icp + 1));
 		break;
@@ -1339,7 +1416,7 @@
 		insns
 	};
 
-	if (once)
+	if (once || using_ping_socket)
 		return;
 	once = 1;
 
diff --git a/ping_common.c b/ping_common.c
index 8d6b145..2bdd6f9 100644
--- a/ping_common.c
+++ b/ping_common.c
@@ -630,7 +630,6 @@
 			fprintf(stderr, "Warning: no SO_TIMESTAMP support, falling back to SIOCGSTAMP\n");
 	}
 #endif
-#ifdef SO_MARK
 	if (options & F_MARK) {
 		int ret;
 
@@ -645,7 +644,6 @@
 			fprintf(stderr, "Warning: Failed to set mark %d\n", mark);
 		}
 	}
-#endif
 
 	/* Set some SNDTIMEO to prevent blocking forever
 	 * on sends, when device is too slow or stalls. Just put limit
@@ -677,7 +675,8 @@
 			*p++ = i;
 	}
 
-	ident = htons(getpid() & 0xFFFF);
+	if (!ident)
+		ident = htons(getpid() & 0xFFFF);
 
 	set_signal(SIGINT, sigexit);
 	set_signal(SIGALRM, sigexit);
diff --git a/ping_common.h b/ping_common.h
index 27ae0f0..9cdb07b 100644
--- a/ping_common.h
+++ b/ping_common.h
@@ -33,6 +33,10 @@
 #include <linux/types.h>
 #include <linux/errqueue.h>
 
+#ifdef ANDROID
+#include <linux/icmp.h>
+#endif
+
 #include "SNAPSHOT.h"
 
 #define	DEFDATALEN	(64 - 8)	/* default data length */