| /* |
| * 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, |
| }; |