blob: f749c5cad9a72e75edad851c6cffcb7e8b8a5bef [file] [log] [blame]
/*
* 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.
*/
/* A simple implementation of PPTP Network Server (RFC 2637) which only
* creates a single session. The following code only handles control packets.
* Data packets are handled by PPPoPNS driver which can be found in Android
* kernel tree. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/if_pppopns.h>
#include "mtpd.h"
enum pptp_message {
SCCRQ = 1,
SCCRP = 2,
STOPCCRQ = 3,
STOPCCRP = 4,
ECHORQ = 5,
ECHORP = 6,
OCRQ = 7,
OCRP = 8,
ICRQ = 9,
ICRP = 10,
ICCN = 11,
CCRQ = 12,
CDN = 13,
WEN = 14,
SLI = 15,
MESSAGE_MAX = 15,
};
static char *messages[] = {
NULL, "SCCRQ", "SCCRP", "STOPCCRQ", "STOPCCRP", "ECHORQ", "ECHORP",
"OCRQ", "OCRP", "ICRQ", "ICRP", "ICCN", "CCRQ", "CDN", "WEN", "SLI",
};
static uint8_t lengths[] = {
0, 156, 156, 16, 16, 16, 20, 168, 32, 220, 24, 28, 16, 148, 40, 24,
};
#define CONTROL_MESSAGE htons(1)
#define MAGIC_COOKIE htonl(0x1A2B3C4D)
#define PROTOCOL_VERSION htons(0x0100)
#define RESULT_OK 1
#define RESULT_ERROR 2
/* Some implementation uses 0 instead of 1, so we allow both of them. */
#define ESTABLISHED(result) (result <= 1)
#define HEADER_SIZE 8
#define MIN_MESSAGE_SIZE 10
static uint16_t local;
static uint16_t remote;
static uint16_t state;
#define MAX_PACKET_LENGTH 220
/* We define all the fields we used in this structure. Type conversion and byte
* alignment are solved in one place. Although it looks a little bit ugly, it
* really makes life easier. */
static struct packet {
int length;
int expect;
union {
uint8_t buffer[MAX_PACKET_LENGTH];
struct {
struct __attribute__((packed)) {
uint16_t length;
uint16_t type;
uint32_t cookie;
} header;
uint16_t message;
uint16_t reserved;
union {
struct __attribute__((packed)) {
uint16_t protocol_version;
uint8_t result;
uint8_t error;
uint32_t framing;
uint32_t bearer;
uint16_t channels;
uint16_t firmware_revision;
char host[64];
} sccrp, sccrq;
struct __attribute__((packed)) {
uint16_t call;
uint16_t serial;
uint32_t minimum_speed;
uint32_t maximum_speed;
uint32_t bearer;
uint32_t framing;
uint16_t window_size;
} ocrq;
struct __attribute__((packed)) {
uint16_t call;
uint16_t peer;
uint8_t result;
} ocrp, icrp;
struct __attribute__((packed)) {
uint32_t identifier;
uint8_t result;
} echorq, echorp;
struct __attribute__((packed)) {
uint16_t call;
} icrq, ccrq, cdn;
};
} __attribute__((packed));
} __attribute__((aligned(4)));
} incoming, outgoing;
static void set_message(uint16_t message)
{
uint16_t length = lengths[message];
memset(outgoing.buffer, 0, length);
outgoing.length = length;
outgoing.header.length = htons(length);
outgoing.header.type = CONTROL_MESSAGE;
outgoing.header.cookie = MAGIC_COOKIE;
outgoing.message = htons(message);
}
static void send_packet()
{
send(the_socket, outgoing.buffer, outgoing.length, 0);
}
static int recv_packet()
{
int length;
/* We are going to read a new message if incoming.expect is 0. */
if (!incoming.expect) {
incoming.length = 0;
incoming.expect = HEADER_SIZE;
}
/* The longest message defined in RFC 2637 is 220 bytes, but the protocol
* itself allows up to 65536 bytes. Therefore we always read a complete
* message but only keep the first 220 bytes before passing up. */
length = incoming.expect - incoming.length;
if (incoming.length >= MAX_PACKET_LENGTH) {
uint8_t buffer[length];
length = recv(the_socket, buffer, length, 0);
} else {
if (incoming.expect > MAX_PACKET_LENGTH) {
length = MAX_PACKET_LENGTH - incoming.length;
}
length = recv(the_socket, &incoming.buffer[incoming.length], length, 0);
}
if (length == -1) {
if (errno == EINTR) {
return 0;
}
log_print(FATAL, "Recv() %s", strerror(errno));
exit(NETWORK_ERROR);
}
if (length == 0) {
log_print(DEBUG, "Connection closed");
log_print(INFO, "Remote server hung up");
return -REMOTE_REQUESTED;
}
incoming.length += length;
/* If incoming.header is valid, check cookie and update incoming.expect. */
if (incoming.length == HEADER_SIZE && incoming.expect == HEADER_SIZE) {
if (incoming.header.cookie != MAGIC_COOKIE) {
log_print(DEBUG, "Loss of synchronization");
log_print(ERROR, "Protocol error");
return -PROTOCOL_ERROR;
}
incoming.expect = ntohs(incoming.header.length);
if (incoming.expect < HEADER_SIZE) {
log_print(DEBUG, "Invalid message length");
log_print(ERROR, "Protocol error");
return -PROTOCOL_ERROR;
}
}
/* Now we have a complete message. Reset incoming.expect. */
if (incoming.length == incoming.expect) {
incoming.expect = 0;
/* Return 1 if it is a control message. */
if (incoming.header.type == CONTROL_MESSAGE) {
return 1;
}
log_print(DEBUG, "Ignored non-control message (type = %d)",
ntohs(incoming.header.type));
}
return 0;
}
static int pptp_connect(int argc, char **argv)
{
if (argc < 2) {
return -USAGE_ERROR;
}
create_socket(AF_UNSPEC, SOCK_STREAM, argv[0], argv[1]);
log_print(DEBUG, "Sending SCCRQ");
state = SCCRQ;
set_message(SCCRQ);
outgoing.sccrq.protocol_version = PROTOCOL_VERSION;
outgoing.sccrq.framing = htonl(3);
outgoing.sccrq.bearer = htonl(3);
outgoing.sccrq.channels = htons(1);
strcpy(outgoing.sccrq.host, "anonymous");
send_packet();
return 0;
}
static int create_pppox()
{
int pppox;
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));
exit(SYSTEM_ERROR);
} else {
struct sockaddr_pppopns address = {
.sa_family = AF_PPPOX,
.sa_protocol = PX_PROTO_OPNS,
.tcp_socket = the_socket,
.local = local,
.remote = remote,
};
if (connect(pppox, (struct sockaddr *)&address, sizeof(address)) != 0) {
log_print(FATAL, "Connect() %s", strerror(errno));
exit(SYSTEM_ERROR);
}
}
return pppox;
}
static int pptp_process()
{
int result = recv_packet();
if (result <= 0) {
return result;
}
if (incoming.length < MIN_MESSAGE_SIZE) {
log_print(DEBUG, "Control message too short");
return 0;
}
incoming.message = ntohs(incoming.message);
if (incoming.message > MESSAGE_MAX || !messages[incoming.message]) {
log_print(DEBUG, "Received UNKNOWN %d", incoming.message);
return 0;
}
if (incoming.length < lengths[incoming.message]) {
log_print(DEBUG, "Received %s with invalid length (length = %d)",
messages[incoming.message], incoming.length);
return 0;
}
switch(incoming.message) {
case SCCRP:
if (state == SCCRQ) {
if (incoming.sccrp.protocol_version == PROTOCOL_VERSION
&& ESTABLISHED(incoming.sccrp.result)) {
while (!local) {
local = random();
}
log_print(DEBUG, "Received SCCRP -> Sending OCRQ "
"(local = %d)", local);
log_print(INFO, "Tunnel established");
state = OCRQ;
set_message(OCRQ);
outgoing.ocrq.call = local;
outgoing.ocrq.serial = random();
outgoing.ocrq.minimum_speed = htonl(1000);
outgoing.ocrq.maximum_speed = htonl(100000000);
outgoing.ocrq.bearer = htonl(3);
outgoing.ocrq.framing = htonl(3);
outgoing.ocrq.window_size = htons(8192);
send_packet();
return 0;
}
log_print(DEBUG, "Received SCCRP (result = %d)",
incoming.sccrq.result);
log_print(INFO, "Remote server hung up");
return -REMOTE_REQUESTED;
}
break;
case OCRP:
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(INFO, "Session established");
state = OCRP;
start_pppd(create_pppox());
return 0;
}
log_print(DEBUG, "Received OCRP (result = %d)",
incoming.ocrp.result);
log_print(INFO, "Remote server hung up");
return -REMOTE_REQUESTED;
}
break;
case STOPCCRQ:
log_print(DEBUG, "Received STOPCCRQ");
log_print(INFO, "Remote server hung up");
state = STOPCCRQ;
return -REMOTE_REQUESTED;
case CCRQ:
/* According to RFC 2637 page 45, we should never receive CCRQ for
* 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(INFO, "Remote server hung up");
return -REMOTE_REQUESTED;
}
break;
case CDN:
if (state == OCRP && incoming.cdn.call == remote) {
log_print(DEBUG, "Received CDN (remote = %d)", remote);
log_print(INFO, "Remote server hung up");
return -REMOTE_REQUESTED;
}
break;
case ECHORQ:
log_print(DEBUG, "Received ECHORQ -> Sending ECHORP");
set_message(ECHORP);
outgoing.echorp.identifier = incoming.echorq.identifier;
outgoing.echorp.result = RESULT_OK;
send_packet();
return 0;
case WEN:
case SLI:
log_print(DEBUG, "Recevied %s", messages[incoming.message]);
return 0;
case ICRQ:
log_print(DEBUG, "Received ICRQ (remote = %d) -> Sending ICRP "
"with error", incoming.icrq.call);
set_message(ICRP);
outgoing.icrp.peer = incoming.icrq.call;
outgoing.icrp.result = RESULT_ERROR;
send_packet();
return 0;
case OCRQ:
log_print(DEBUG, "Received OCRQ (remote = %d) -> Sending OCRP "
"with error", incoming.ocrq.call);
set_message(OCRP);
outgoing.ocrp.peer = incoming.ocrq.call;
outgoing.ocrp.result = RESULT_ERROR;
send_packet();
return 0;
}
/* We reach here if we got an unexpected message. Just log it. */
log_print(DEBUG, "Received UNEXPECTED %s", messages[incoming.message]);
return 0;
}
static int pptp_timeout()
{
return 0;
}
static void pptp_shutdown()
{
/* Normally we should send STOPCCRQ and wait for STOPCCRP, but this might
* block for a long time. Here we simply take the shortcut: do nothing. */
}
struct protocol pptp = {
.name = "pptp",
.usage = "<server> <port>",
.connect = pptp_connect,
.process = pptp_process,
.timeout = pptp_timeout,
.shutdown = pptp_shutdown,
};