summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mtpd.c53
-rw-r--r--mtpd.h1
-rw-r--r--pptp.c206
3 files changed, 245 insertions, 15 deletions
diff --git a/mtpd.c b/mtpd.c
index 53ea362..ac724ac 100644
--- a/mtpd.c
+++ b/mtpd.c
@@ -418,3 +418,56 @@ ret:
close(session_fd);
close(tunnel_fd);
}
+
+/**
+ * Start pppd daemon with pppopptp-android plugin.
+ *
+ * @param pptp_fd PPTP socket file descriptor
+ */
+void start_pppd_pptp(int pptp_fd)
+{
+ if (pppd_pid) {
+ log_print(WARNING, "Pppd is already started (pid = %d)", pppd_pid);
+ goto ret;
+ }
+
+ log_print(INFO, "Starting pppd (pptp_fd = %d)", pptp_fd);
+
+ pppd_pid = fork();
+ if (pppd_pid < 0) {
+ log_print(FATAL, "Fork() %s", strerror(errno));
+ exit(SYSTEM_ERROR);
+ }
+
+ if (!pppd_pid) {
+ char pptp_fd_str[FD_MAX_LEN + 1];
+
+ snprintf(pptp_fd_str, FD_MAX_LEN + 1, "%d", pptp_fd);
+
+ const char *pptp_args[] = {
+ "pppd",
+ "nodetach",
+ "plugin",
+ "pppopptp-android.so",
+ "pptp_socket",
+ pptp_fd_str,
+ };
+ const size_t args_len = ARRAY_SIZE(pptp_args) + pppd_argc + 1;
+ char *args[args_len];
+
+ /* Populate args[] from pptp_args[] and pppd_argv[] */
+ memcpy(args, pptp_args, sizeof(pptp_args));
+ memcpy(args + ARRAY_SIZE(pptp_args), pppd_argv,
+ sizeof(char *) * pppd_argc);
+ args[args_len - 1] = NULL;
+
+ execvp("pppd", args);
+ log_print(FATAL, "Exec() %s", strerror(errno));
+ exit(SYSTEM_ERROR); /* Pretending a fatal error in pppd. */
+ }
+
+ log_print(INFO, "Pppd started (pid = %d)", pppd_pid);
+
+ret:
+ close(pptp_fd);
+}
diff --git a/mtpd.h b/mtpd.h
index dad3ea6..290a583 100644
--- a/mtpd.h
+++ b/mtpd.h
@@ -72,6 +72,7 @@ void create_socket(int family, int type, char *server, char *port);
void start_pppd(int pppox);
void start_pppd_ol2tp(int tunnel_fd, int session_fd, int tunnel_id,
int session_id);
+void start_pppd_pptp(int pptp_fd);
/* Each protocol must implement everything defined in this structure. Note that
* timeout intervals are in milliseconds, where zero means forever. To indicate
diff --git a/pptp.c b/pptp.c
index c748d1a..80eb24a 100644
--- a/pptp.c
+++ b/pptp.c
@@ -19,15 +19,19 @@
* Data packets are handled by PPPoPNS driver which can be found in Android
* kernel tree. */
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
+#include <netdb.h>
+#include <unistd.h>
#include <arpa/inet.h>
#include <linux/netdevice.h>
#include <linux/if_pppox.h>
+#include <linux/types.h>
#include "mtpd.h"
@@ -72,9 +76,10 @@ static uint8_t lengths[] = {
#define HEADER_SIZE 8
#define MIN_MESSAGE_SIZE 10
-static uint16_t local;
-static uint16_t remote;
+static __be16 local;
+static __be16 remote;
static uint16_t state;
+static const char *remote_name; /* server host name or IP address */
#define MAX_PACKET_LENGTH 220
@@ -207,14 +212,16 @@ static int recv_packet()
if (incoming.header.type == CONTROL_MESSAGE) {
return 1;
}
- log_print(DEBUG, "Ignored non-control message (type = %d)",
- ntohs(incoming.header.type));
+ log_print(DEBUG, "Ignored non-control message (type = %u)",
+ (unsigned)ntohs(incoming.header.type));
}
return 0;
}
static int pptp_connect(char **arguments)
{
+ remote_name = arguments[0];
+
create_socket(AF_UNSPEC, SOCK_STREAM, arguments[0], arguments[1]);
log_print(DEBUG, "Sending SCCRQ");
@@ -229,10 +236,40 @@ static int pptp_connect(char **arguments)
return 0;
}
-static int create_pppox()
+/**
+ * Check if upstream kernel implementation is enabled.
+ *
+ * @return true if upstream PPTP is enabled in kernel and false otherwise
+ */
+static bool check_pptp(void)
+{
+ int fd = socket(AF_PPPOX, SOCK_DGRAM, PX_PROTO_PPTP);
+
+ if (fd < 0) {
+ return false;
+ } else {
+ close(fd);
+ return true;
+ }
+}
+
+/**
+ * Create OPNS session.
+ *
+ * @deprecated It will be removed soon in favor of upstream PPTP.
+ *
+ * @return PPPoX socket file descriptor
+ */
+static int create_pppox_opns(void)
{
- int pppox = socket(AF_PPPOX, SOCK_DGRAM, PX_PROTO_OPNS);
+ int pppox;
+
+ log_print(WARNING, "Using deprecated OPNS protocol. "
+ "Its support will be removed soon. "
+ "Please enable PPTP support in your kernel");
+
log_print(INFO, "Creating PPPoX socket");
+ pppox = socket(AF_PPPOX, SOCK_DGRAM, PX_PROTO_OPNS);
if (pppox == -1) {
log_print(FATAL, "Socket() %s", strerror(errno));
@@ -253,6 +290,135 @@ static int create_pppox()
return pppox;
}
+/**
+ * Get IP address by host name.
+ *
+ * @param name Host name to get IP address for
+ *
+ * @return IP address for given host name
+ */
+static struct in_addr get_addr_by_name(const char *name)
+{
+ struct addrinfo hints;
+ struct addrinfo *res, *rp;
+ struct in_addr addr;
+ int err;
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = AF_INET; /* allow only IPv4 */
+ hints.ai_socktype = SOCK_DGRAM; /* UDP */
+ hints.ai_protocol = 0; /* any protocol */
+
+ err = getaddrinfo(name, NULL, &hints, &res);
+ if (err) {
+ log_print(FATAL, "%s: getaddrinfo: %s", __func__, gai_strerror(err));
+ exit(SYSTEM_ERROR);
+ }
+
+ for (rp = res; rp != NULL; rp = rp->ai_next) {
+ /* For now we only support IPv4 */
+ if (rp->ai_family == AF_INET) {
+ addr = ((struct sockaddr_in *)rp->ai_addr)->sin_addr;
+ break;
+ }
+ }
+
+ if (rp == NULL) {
+ log_print(FATAL, "%s: No IPv4 addresses found", __func__);
+ freeaddrinfo(res);
+ exit(SYSTEM_ERROR);
+ }
+
+ freeaddrinfo(res);
+
+ return addr;
+}
+
+/**
+ * Get local IP address.
+ *
+ * Make a socket connection with remote server and then call getsockname() on
+ * the connected socket. This will return the local IP address.
+ *
+ * @param remote_addr Server IP address
+ *
+ * @return Local IP address
+ */
+static struct in_addr get_local_addr(struct in_addr remote_addr)
+{
+ int sock;
+ struct sockaddr_in addr;
+ socklen_t addr_len;
+
+ addr_len = sizeof(struct sockaddr_in);
+ addr.sin_addr = remote_addr;
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(0);
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ log_print(FATAL, "%s: Socket() %s", __func__, strerror(errno));
+ exit(SYSTEM_ERROR);
+ }
+
+ if (connect(sock, (struct sockaddr*)&addr, sizeof(addr))) {
+ close(sock);
+ log_print(FATAL, "%s: Connect() %s", __func__, strerror(errno));
+ exit(SYSTEM_ERROR);
+ }
+
+ getsockname(sock, (struct sockaddr*)&addr, &addr_len);
+ close(sock);
+
+ return addr.sin_addr;
+}
+
+/**
+ * Create PPTP session.
+ *
+ * @return PPTP socket file descriptor
+ */
+static int create_pppox_pptp(void)
+{
+ int pptp_fd;
+ struct sockaddr_pppox src, dst;
+ struct in_addr remote_addr; /* server IP address */
+ struct in_addr local_addr; /* client IP address */
+
+ remote_addr = get_addr_by_name(remote_name);
+ local_addr = get_local_addr(remote_addr);
+
+ src.sa_family = AF_PPPOX;
+ src.sa_protocol = PX_PROTO_PPTP;
+ src.sa_addr.pptp.call_id = ntohs(local);
+ src.sa_addr.pptp.sin_addr = local_addr;
+
+ dst.sa_family = AF_PPPOX;
+ dst.sa_protocol = PX_PROTO_PPTP;
+ dst.sa_addr.pptp.call_id = ntohs(remote);
+ dst.sa_addr.pptp.sin_addr = remote_addr;
+
+ pptp_fd = socket(AF_PPPOX, SOCK_STREAM, PX_PROTO_PPTP);
+ if (pptp_fd < 0) {
+ log_print(FATAL, "Failed to create PPTP socket (%s)", strerror(errno));
+ exit(SYSTEM_ERROR);
+ }
+
+ if (bind(pptp_fd, (struct sockaddr*)&src, sizeof(src))) {
+ log_print(FATAL, "Failed to bind PPTP socket (%s)", strerror(errno));
+ close(pptp_fd);
+ exit(SYSTEM_ERROR);
+ }
+
+ if (connect(pptp_fd, (struct sockaddr*)&dst, sizeof(dst))) {
+ log_print(FATAL, "Failed to connect PPTP socket (%s)", strerror(errno));
+ close(pptp_fd);
+ exit(SYSTEM_ERROR);
+ }
+
+ return pptp_fd;
+}
+
static int pptp_process()
{
int result = recv_packet();
@@ -284,7 +450,7 @@ static int pptp_process()
local = random();
}
log_print(DEBUG, "Received SCCRP -> Sending OCRQ "
- "(local = %d)", local);
+ "(local = %u)", (unsigned)ntohs(local));
log_print(INFO, "Tunnel established");
state = OCRQ;
set_message(OCRQ);
@@ -309,10 +475,16 @@ static int pptp_process()
if (state == OCRQ && incoming.ocrp.peer == local) {
if (ESTABLISHED(incoming.ocrp.result)) {
remote = incoming.ocrp.call;
- log_print(DEBUG, "Received OCRQ (remote = %d)", remote);
+ log_print(DEBUG, "Received OCRQ (remote = %u)",
+ (unsigned)ntohs(remote));
log_print(INFO, "Session established");
state = OCRP;
- start_pppd(create_pppox());
+
+ if (check_pptp())
+ start_pppd_pptp(create_pppox_pptp());
+ else
+ start_pppd(create_pppox_opns());
+
return 0;
}
log_print(DEBUG, "Received OCRP (result = %d)",
@@ -333,7 +505,8 @@ static int pptp_process()
* outgoing calls. However, some implementation only acts as PNS and
* always uses CCRQ to clear a call, so here we still handle it. */
if (state == OCRP && incoming.ccrq.call == remote) {
- log_print(DEBUG, "Received CCRQ (remote = %d)", remote);
+ log_print(DEBUG, "Received CCRQ (remote = %u)",
+ (unsigned)ntohs(remote));
log_print(INFO, "Remote server hung up");
return -REMOTE_REQUESTED;
}
@@ -341,7 +514,8 @@ static int pptp_process()
case CDN:
if (state == OCRP && incoming.cdn.call == remote) {
- log_print(DEBUG, "Received CDN (remote = %d)", remote);
+ log_print(DEBUG, "Received CDN (remote = %u)",
+ (unsigned)ntohs(remote));
log_print(INFO, "Remote server hung up");
return -REMOTE_REQUESTED;
}
@@ -361,8 +535,9 @@ static int pptp_process()
return 0;
case ICRQ:
- log_print(DEBUG, "Received ICRQ (remote = %d) -> Sending ICRP "
- "with error", incoming.icrq.call);
+ log_print(DEBUG, "Received ICRQ (remote = %u, call = %u) -> "
+ "Sending ICRP with error", (unsigned)ntohs(remote),
+ (unsigned)ntohs(incoming.icrq.call));
set_message(ICRP);
outgoing.icrp.peer = incoming.icrq.call;
outgoing.icrp.result = RESULT_ERROR;
@@ -370,8 +545,9 @@ static int pptp_process()
return 0;
case OCRQ:
- log_print(DEBUG, "Received OCRQ (remote = %d) -> Sending OCRP "
- "with error", incoming.ocrq.call);
+ log_print(DEBUG, "Received OCRQ (remote = %u, call = %u) -> "
+ "Sending OCRP with error", (unsigned)ntohs(remote),
+ (unsigned)ntohs(incoming.ocrq.call));
set_message(OCRP);
outgoing.ocrp.peer = incoming.ocrq.call;
outgoing.ocrp.result = RESULT_ERROR;