First version of Multiple Tunneling Protocol Daemon (mtpd) for Android.
Currently it only supports L2TP without secrets.
diff --git a/mtpd.c b/mtpd.c
new file mode 100644
index 0000000..996bced
--- /dev/null
+++ b/mtpd.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <sys/wait.h>
+#include <netdb.h>
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+
+#include "mtpd.h"
+
+int the_socket = -1;
+
+extern struct protocol l2tp;
+static struct protocol *protocols[] = {&l2tp, NULL};
+static struct protocol *the_protocol;
+
+static int pppd_argc;
+static char **pppd_argv;
+static pid_t pppd_pid;
+
+/* We redirect signals to a pipe in order to prevent race conditions. */
+static int signals[2];
+
+static void interrupt(int signal)
+{
+ write(signals[1], &signal, sizeof(int));
+}
+
+static int initialize(int argc, char **argv)
+{
+ int timeout = 0;
+ int i;
+
+ for (i = 2; i < argc; ++i) {
+ if (!argv[i][0]) {
+ pppd_argc = argc - i - 1;
+ pppd_argv = &argv[i + 1];
+ argc = i;
+ break;
+ }
+ }
+
+ if (argc >= 2) {
+ for (i = 0; protocols[i]; ++i) {
+ if (!strcmp(argv[1], protocols[i]->name)) {
+ log_print(INFO, "Using protocol %s", protocols[i]->name);
+ the_protocol = protocols[i];
+ timeout = the_protocol->connect(argc - 2, &argv[2]);
+ break;
+ }
+ }
+ }
+
+ if (!the_protocol || timeout == -USAGE_ERROR) {
+ printf("Usage: %s <protocol-args> '' <pppd-args>, "
+ "where protocol-args are one of:\n", argv[0]);
+ for (i = 0; protocols[i]; ++i) {
+ printf(" %s %s\n", protocols[i]->name, protocols[i]->usage);
+ }
+ exit(USAGE_ERROR);
+ }
+ return timeout;
+}
+
+static void stop_pppd()
+{
+ if (pppd_pid) {
+ log_print(INFO, "Sending signal to pppd (pid = %d)", pppd_pid);
+ kill(pppd_pid, SIGTERM);
+ sleep(5);
+ pppd_pid = 0;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ struct pollfd pollfds[2];
+ int timeout;
+ int error = 0;
+
+ srandom(time(NULL));
+
+ if (pipe(signals) == -1) {
+ log_print(FATAL, "Pipe() %s", strerror(errno));
+ exit(SYSTEM_ERROR);
+ }
+
+ signal(SIGHUP, interrupt);
+ signal(SIGINT, interrupt);
+ signal(SIGTERM, interrupt);
+ signal(SIGCHLD, interrupt);
+ signal(SIGPIPE, SIG_IGN);
+ atexit(stop_pppd);
+
+ timeout = initialize(argc, argv);
+ pollfds[0].fd = signals[0];
+ pollfds[0].events = POLLIN;
+ pollfds[1].fd = the_socket;
+ pollfds[1].events = POLLIN;
+
+ while (timeout >= 0) {
+ if (poll(pollfds, 2, timeout ? timeout : -1) == -1 && errno != EINTR) {
+ log_print(FATAL, "Poll() %s", strerror(errno));
+ exit(SYSTEM_ERROR);
+ }
+ if (pollfds[0].revents) {
+ break;
+ }
+ timeout = pollfds[1].revents ?
+ the_protocol->process() : the_protocol->timeout();
+ }
+
+ if (timeout < 0) {
+ error = -timeout;
+ } else {
+ int signal;
+ read(signals[0], &signal, sizeof(int));
+ log_print(INFO, "Received signal %d", signal);
+ if (signal == SIGCHLD && waitpid(pppd_pid, &error, WNOHANG) == pppd_pid
+ && WIFEXITED(error)) {
+ error = WEXITSTATUS(error);
+ log_print(INFO, "Pppd is terminated (status = %d)", error);
+ error += PPPD_EXITED;
+ pppd_pid = 0;
+ } else {
+ error = USER_REQUESTED;
+ }
+ }
+
+ stop_pppd();
+ the_protocol->shutdown();
+
+ log_print(INFO, "Mtpd is terminated (status = %d)", error);
+ return error;
+}
+
+void log_print(int level, char *format, ...)
+{
+ if (level >= 0 && level <= LOG_MAX) {
+ char *levels = "DIWEF";
+ va_list ap;
+ fprintf(stderr, "%c: ", levels[level]);
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+ fputc('\n', stderr);
+ }
+}
+
+void create_socket(int family, int type, char *server, char *port)
+{
+ struct addrinfo hints = {
+ .ai_flags = AI_NUMERICSERV,
+ .ai_family = family,
+ .ai_socktype = type,
+ };
+ struct addrinfo *records;
+ struct addrinfo *r;
+ int error;
+
+ log_print(INFO, "Connecting to %s port %s", server, port);
+
+ error = getaddrinfo(server, port, &hints, &records);
+ if (error) {
+ log_print(FATAL, "Getaddrinfo() %s", (error == EAI_SYSTEM) ?
+ strerror(errno) : gai_strerror(error));
+ exit(NETWORK_ERROR);
+ }
+
+ for (r = records; r; r = r->ai_next) {
+ the_socket = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
+ if (the_socket != -1
+ && connect(the_socket, r->ai_addr, r->ai_addrlen) == 0) {
+ break;
+ }
+ }
+
+ freeaddrinfo(records);
+
+ if (the_socket == -1) {
+ log_print(FATAL, "Connect() %s", strerror(errno));
+ exit(NETWORK_ERROR);
+ }
+
+ fcntl(the_socket, F_SETFD, FD_CLOEXEC);
+ log_print(INFO, "Connection established (socket = %d)", the_socket);
+}
+
+void start_pppd(int pppox)
+{
+ if (pppd_pid) {
+ log_print(WARNING, "Pppd is already started (pid = %d)", pppd_pid);
+ close(pppox);
+ return;
+ }
+
+ log_print(INFO, "Starting pppd (pppox = %d)", pppox);
+
+ pppd_pid = fork();
+ if (pppd_pid < 0) {
+ log_print(FATAL, "Fork() %s", strerror(errno));
+ exit(SYSTEM_ERROR);
+ }
+
+ if (!pppd_pid) {
+ char number[16];
+ char *args[1024] = {"", "nodetach", "pppox", number};
+ int i;
+
+ sprintf(number, "%d", pppox);
+ for (i = 0; i < pppd_argc; ++i) {
+ args[4 + i] = pppd_argv[i];
+ }
+
+ execvp("pppd", args);
+ log_print(FATAL, "Exec() %s", strerror(errno));
+ exit(1); /* Pretending a fatal error in pppd. */
+ }
+
+ log_print(INFO, "Pppd started (pid = %d)", pppd_pid);
+ close(pppox);
+}