| /* |
| * Copyright (C) 2011 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 <signal.h> |
| #include <poll.h> |
| #include <unistd.h> |
| |
| #include "config.h" |
| #include "gcmalloc.h" |
| #include "schedule.h" |
| #include "plog.h" |
| |
| #ifdef ANDROID_CHANGES |
| |
| #include <openssl/engine.h> |
| |
| #include <string.h> |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <sys/ioctl.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <linux/if.h> |
| #include <linux/if_tun.h> |
| |
| #include <android/log.h> |
| #include <cutils/sockets.h> |
| #include <private/android_filesystem_config.h> |
| |
| static void notify_death() |
| { |
| creat("/data/misc/vpn/abort", 0); |
| } |
| |
| static int android_get_control_and_arguments(int *argc, char ***argv) |
| { |
| static char *args[32]; |
| int control; |
| int i; |
| |
| atexit(notify_death); |
| |
| if ((i = android_get_control_socket("racoon")) == -1) { |
| return -1; |
| } |
| do_plog(LLV_DEBUG, "Waiting for control socket"); |
| if (listen(i, 1) == -1 || (control = accept(i, NULL, 0)) == -1) { |
| do_plog(LLV_ERROR, "Cannot get control socket"); |
| exit(1); |
| } |
| close(i); |
| fcntl(control, F_SETFD, FD_CLOEXEC); |
| |
| args[0] = (*argv)[0]; |
| for (i = 1; i < 32; ++i) { |
| unsigned char bytes[2]; |
| if (recv(control, &bytes[0], 1, 0) != 1 || |
| recv(control, &bytes[1], 1, 0) != 1) { |
| do_plog(LLV_ERROR, "Cannot get argument length"); |
| exit(1); |
| } else { |
| int length = bytes[0] << 8 | bytes[1]; |
| int offset = 0; |
| |
| if (length == 0xFFFF) { |
| break; |
| } |
| args[i] = malloc(length + 1); |
| while (offset < length) { |
| int n = recv(control, &args[i][offset], length - offset, 0); |
| if (n > 0) { |
| offset += n; |
| } else { |
| do_plog(LLV_ERROR, "Cannot get argument value"); |
| exit(1); |
| } |
| } |
| args[i][length] = 0; |
| } |
| } |
| do_plog(LLV_DEBUG, "Received %d arguments", i - 1); |
| |
| *argc = i; |
| *argv = args; |
| return control; |
| } |
| |
| const char *android_hook(char **envp) |
| { |
| struct ifreq ifr = {.ifr_flags = IFF_TUN}; |
| int tun = open("/dev/tun", 0); |
| |
| /* Android does not support INTERNAL_WINS4_LIST, so we just use it. */ |
| while (*envp && strncmp(*envp, "INTERNAL_WINS4_LIST=", 20)) { |
| ++envp; |
| } |
| if (!*envp) { |
| do_plog(LLV_ERROR, "Cannot find environment variable\n"); |
| exit(1); |
| } |
| if (ioctl(tun, TUNSETIFF, &ifr)) { |
| do_plog(LLV_ERROR, "Cannot allocate TUN: %s\n", strerror(errno)); |
| exit(1); |
| } |
| sprintf(*envp, "INTERFACE=%s", ifr.ifr_name); |
| return "/etc/ppp/ip-up-vpn"; |
| } |
| |
| #endif |
| |
| extern void setup(int argc, char **argv); |
| |
| static int monitors; |
| static void (*callbacks[10])(int fd); |
| static struct pollfd pollfds[10]; |
| |
| char *pname; |
| |
| static void terminate(int signal) |
| { |
| exit(1); |
| } |
| |
| static void terminated() |
| { |
| do_plog(LLV_INFO, "Bye\n"); |
| } |
| |
| void monitor_fd(int fd, void (*callback)(int)) |
| { |
| if (fd < 0 || monitors == 10) { |
| do_plog(LLV_ERROR, "Cannot monitor fd"); |
| exit(1); |
| } |
| callbacks[monitors] = callback; |
| pollfds[monitors].fd = fd; |
| pollfds[monitors].events = callback ? POLLIN : 0; |
| ++monitors; |
| } |
| |
| int main(int argc, char **argv) |
| { |
| #ifdef ANDROID_CHANGES |
| int control = android_get_control_and_arguments(&argc, &argv); |
| #if !defined(OPENSSL_IS_BORINGSSL) |
| ENGINE *engine; |
| #endif |
| |
| if (control != -1) { |
| pname = "%p"; |
| monitor_fd(control, NULL); |
| |
| #if !defined(OPENSSL_IS_BORINGSSL) |
| ENGINE_load_dynamic(); |
| engine = ENGINE_by_id("keystore"); |
| if (!engine || !ENGINE_init(engine)) { |
| do_plog(LLV_ERROR, "ipsec-tools: cannot load keystore engine"); |
| exit(1); |
| } |
| #endif |
| } |
| #endif |
| |
| do_plog(LLV_INFO, "ipsec-tools 0.7.3 (http://ipsec-tools.sf.net)\n"); |
| |
| signal(SIGHUP, terminate); |
| signal(SIGINT, terminate); |
| signal(SIGTERM, terminate); |
| signal(SIGPIPE, SIG_IGN); |
| atexit(terminated); |
| |
| setup(argc, argv); |
| |
| #ifdef ANDROID_CHANGES |
| shutdown(control, SHUT_WR); |
| setuid(AID_VPN); |
| #endif |
| |
| while (1) { |
| struct timeval *tv = schedular(); |
| int timeout = tv->tv_sec * 1000 + tv->tv_usec / 1000 + 1; |
| |
| if (poll(pollfds, monitors, timeout) > 0) { |
| int i; |
| for (i = 0; i < monitors; ++i) { |
| if (pollfds[i].revents & POLLHUP) { |
| do_plog(LLV_INFO, "Connection is closed\n", pollfds[i].fd); |
| /* Wait for few seconds to consume late messages. */ |
| sleep(5); |
| exit(1); |
| } |
| if (pollfds[i].revents & POLLIN) { |
| callbacks[i](pollfds[i].fd); |
| } |
| } |
| } |
| } |
| |
| #if !defined(OPENSSL_IS_BORINGSSL) |
| if (engine) { |
| ENGINE_finish(engine); |
| ENGINE_free(engine); |
| } |
| #endif |
| return 0; |
| } |
| |
| /* plog.h */ |
| |
| void do_plog(int level, char *format, ...) |
| { |
| if (level >= 0 && level <= 5) { |
| #ifdef ANDROID_CHANGES |
| static int levels[6] = { |
| ANDROID_LOG_ERROR, ANDROID_LOG_WARN, ANDROID_LOG_INFO, |
| ANDROID_LOG_INFO, ANDROID_LOG_DEBUG, ANDROID_LOG_VERBOSE |
| }; |
| va_list ap; |
| va_start(ap, format); |
| __android_log_vprint(levels[level], "racoon", format, ap); |
| va_end(ap); |
| #else |
| static char *levels = "EWNIDV"; |
| fprintf(stderr, "%c: ", levels[level]); |
| va_list ap; |
| va_start(ap, format); |
| vfprintf(stderr, format, ap); |
| va_end(ap); |
| #endif |
| } |
| } |
| |
| char *binsanitize(char *data, size_t length) |
| { |
| char *output = racoon_malloc(length + 1); |
| if (output) { |
| size_t i; |
| for (i = 0; i < length; ++i) { |
| output[i] = (data[i] < ' ' || data[i] > '~') ? '?' : data[i]; |
| } |
| output[length] = '\0'; |
| } |
| return output; |
| } |