diff options
Diffstat (limited to 'pkt.c')
-rw-r--r-- | pkt.c | 244 |
1 files changed, 244 insertions, 0 deletions
@@ -0,0 +1,244 @@ +#ifndef WIN32 +#include <netinet/in.h> +#include <arpa/inet.h> +#include <pthread.h> +#endif +#include <sys/time.h> + +#include "ptunnel.h" +#include "pkt.h" +#include "pdesc.h" +#include "options.h" +#include "utils.h" + +/* handle_proxy_packet: + * Processes incoming ICMP packets for the proxy. The packet can come either from the + * packet capture lib, or from the actual socket or both. + * Input: A buffer pointing at the start of an IP header, the buffer length and the proxy + * descriptor chain. + */ +void handle_packet(char *buf, unsigned bytes, int is_pcap, struct sockaddr_in *addr, int icmp_sock) { + ip_packet_t *ip_pkt; + icmp_echo_packet_t *pkt; + ping_tunnel_pkt_t *pt_pkt; + proxy_desc_t *cur; + uint32_t type_flag, pkt_flag, init_state, proxy_flag; + challenge_t *challenge; + struct timeval tt; + + proxy_flag = kProxy_flag; + + if (bytes < sizeof(icmp_echo_packet_t)+sizeof(ping_tunnel_pkt_t)) + pt_log(kLog_verbose, "Skipping this packet - too short. " + "Expect: %d+%d = %d ; Got: %d\n", + sizeof(icmp_echo_packet_t), + sizeof(ping_tunnel_pkt_t), + sizeof(icmp_echo_packet_t) + + sizeof(ping_tunnel_pkt_t), bytes); + else { + if (opts.udp) { + ip_pkt = 0; + pkt = (icmp_echo_packet_t*)buf; + pt_pkt = (ping_tunnel_pkt_t*)pkt->data; + } + else { + ip_pkt = (ip_packet_t*)buf; + pkt = (icmp_echo_packet_t*)ip_pkt->data; + pt_pkt = (ping_tunnel_pkt_t*)pkt->data; + } + + if (ntohl(pt_pkt->magic) == opts.magic) { + pt_pkt->state = ntohl(pt_pkt->state); + pkt->identifier = ntohs(pkt->identifier); + pt_pkt->id_no = ntohs(pt_pkt->id_no); + pt_pkt->seq_no = ntohs(pt_pkt->seq_no); + /* Find the relevant connection, if it exists */ + pthread_mutex_lock(&chain_lock); + for (cur=chain;cur;cur=cur->next) { + if (cur->id_no == pt_pkt->id_no) + break; + } + pthread_mutex_unlock(&chain_lock); + + /* Handle the packet if it comes from "the other end." This is a bit tricky + * to get right, since we receive both our own and the other end's packets. + * Basically, a proxy will accept any packet from a user, regardless if it + * has a valid connection or not. A user will only accept the packet if there + * exists a connection to handle it. + */ + if (cur) { + type_flag = cur->type_flag; + if (type_flag == (uint32_t)kProxy_flag) + cur->icmp_id = pkt->identifier; + if (!is_pcap) + cur->xfer.icmp_in++; + } + else + type_flag = kProxy_flag; + + pkt_flag = pt_pkt->state & kFlag_mask; + pt_pkt->state &= ~kFlag_mask; + pt_log(kLog_sendrecv, "Recv: %d [%d] bytes " + "[seq = %d] [type = %s] " + "[ack = %d] [icmp = %d] " + "[user = %s] [pcap = %d]\n", + bytes, ntohl(pt_pkt->data_len), + pt_pkt->seq_no, state_name[pt_pkt->state & (~kFlag_mask)], + ntohl(pt_pkt->ack), pkt->type, + (pkt_flag == kUser_flag ? "yes" : "no"), is_pcap); + + /* This test essentially verifies that the packet comes from someone who isn't us. */ + if ((pkt_flag == kUser_flag && type_flag == proxy_flag) || + (pkt_flag == proxy_flag && type_flag == kUser_flag)) + { + pt_pkt->data_len = ntohl(pt_pkt->data_len); + pt_pkt->ack = ntohl(pt_pkt->ack); + if (pt_pkt->state == kProxy_start) { + if (!cur && type_flag == proxy_flag) { + pt_log(kLog_info, "Incoming tunnel request from %s.\n", + inet_ntoa(*(struct in_addr *)&addr->sin_addr)); + gettimeofday(&tt, 0); + if (tt.tv_sec < seq_expiry_tbl[pt_pkt->id_no]) { + pt_log(kLog_verbose, "Dropping request: ID was recently in use.\n"); + return; + } + pt_log(kLog_info, "Starting new session to %s:%d with ID %d\n", + inet_ntoa(*(struct in_addr *)&pt_pkt->dst_ip), + ntohl(pt_pkt->dst_port), pt_pkt->id_no); + if ((opts.given_dst_ip && opts.given_dst_ip != pt_pkt->dst_ip) || + ((uint32_t)-1 != opts.given_dst_port && opts.given_dst_port != ntohl(pt_pkt->dst_port))) + { + pt_log(kLog_info, "Destination administratively prohibited!\n"); + return; + } + + if (opts.password) + init_state = kProto_authenticate; + else + init_state = kProto_data; + + cur = create_and_insert_proxy_desc(pt_pkt->id_no, pkt->identifier, 0, + addr, pt_pkt->dst_ip, + ntohl(pt_pkt->dst_port), + init_state, kProxy_flag); + if (init_state == kProto_authenticate) { + pt_log(kLog_debug, "Sending authentication challenge..\n"); + /* Send challenge */ + cur->challenge = generate_challenge(); + memcpy(cur->buf, cur->challenge, sizeof(challenge_t)); + queue_packet(icmp_sock, cur->pkt_type, cur->buf, + sizeof(challenge_t), cur->id_no, + cur->icmp_id, &cur->my_seq, cur->send_ring, + &cur->send_idx, &cur->send_wait_ack, 0, 0, + kProto_authenticate | cur->type_flag, + &cur->dest_addr, cur->next_remote_seq, + &cur->send_first_ack, &cur->ping_seq); + } + } + else if (type_flag == kUser_flag) { + pt_log(kLog_error, "Dropping proxy session request - we are not a proxy!\n"); + return; + } + else + pt_log(kLog_error, "Dropping duplicate proxy session request.\n"); + } + else if (cur && pt_pkt->state == kProto_authenticate) { + /* Sanity check packet length, and make sure it matches what we expect */ + if (pt_pkt->data_len != sizeof(challenge_t)) { + pt_log(kLog_error, "Received challenge packet, but data length " + "is not as expected.\n"); + pt_log(kLog_debug, "Data length: %d Expected: %d\n", + pt_pkt->data_len, sizeof (challenge_t)); + cur->should_remove = 1; + return; + } + /* Prevent packet data from being forwarded over TCP! */ + pt_pkt->data_len = 0; + challenge = (challenge_t*)pt_pkt->data; + /* If client: Compute response to challenge */ + if (type_flag == kUser_flag) { + if (!opts.password) { + pt_log(kLog_error, "This proxy requires a password! " + "Please supply one usin g the -x switch.\n"); + send_termination_msg(cur, icmp_sock); + cur->should_remove = 1; + return; + } + pt_log(kLog_debug, "Got authentication challenge - sending response\n"); + generate_response(challenge); + queue_packet(icmp_sock, cur->pkt_type, (char*)challenge, + sizeof(challenge_t), cur->id_no, cur->icmp_id, + &cur->my_seq, cur->send_ring, &cur->send_idx, + &cur->send_wait_ack, 0, 0, + kProto_authenticate | cur->type_flag, &cur->dest_addr, + cur->next_remote_seq, &cur->send_first_ack, &cur-> ping_seq); + /* We have authenticated locally. + * It's up to the proxy now if it accepts our response or not.. + */ + cur->authenticated = 1; + handle_data(pkt, bytes, cur->recv_ring, &cur->recv_wait_send, + &cur->recv_idx, &cur->next_remote_seq); + return; + } + /* If proxy: Handle client's response to challenge */ + else if (type_flag == proxy_flag) { + pt_log(kLog_debug, "Received remote challenge response.\n"); + if (validate_challenge(cur->challenge, challenge) || + cur->authenticated) + { + pt_log(kLog_verbose, "Remote end authenticated successfully.\n"); + /* Authentication has succeeded, so now we can proceed + * to handle incoming TCP data. + */ + cur->authenticated = 1; + cur->state = kProto_data; + /* Insert the packet into the receive ring, to avoid + * confusing the reliab ility mechanism. + */ + handle_data(pkt, bytes, cur->recv_ring, &cur->recv_wait_send, + &cur->recv_idx, &cur->next_remote_seq); + } + else { + pt_log(kLog_info, "Remote end failed authentication.\n"); + send_termination_msg(cur, icmp_sock); + cur->should_remove = 1; + } + return; + } + } + /* Handle close-messages for connections we know about */ + if (cur && pt_pkt->state == kProto_close) { + pt_log(kLog_info, "Received session close from remote peer.\n"); + cur->should_remove = 1; + return; + } + /* The proxy will ignore any other packets from the client + * until it has been authenticated. The packet resend mechanism + * insures that this isn't problematic. + */ + if (type_flag == proxy_flag && opts.password && + cur && !cur->authenticated) + { + pt_log(kLog_debug, "Ignoring packet with seq-no %d " + "- not authenticated yet.\n", pt_pkt->seq_no); + return; + } + + if (cur && cur->sock) { + if (pt_pkt->state == kProto_data || pt_pkt->state == kProxy_start || + pt_pkt->state == kProto_ack) + { + handle_data(pkt, bytes, cur->recv_ring, &cur->recv_wait_send, + &cur->recv_idx, &cur->next_remote_seq); + } + handle_ack((uint16_t)pt_pkt->ack, cur->send_ring, &cur->send_wait_ack, + 0, cur->send_idx, &cur->send_first_ack, &cur->remote_ack_val, + is_pcap); + cur->last_activity = time_as_double(); + } + } + } + else + pt_log(kLog_verbose, "Ignored incoming packet.\n"); + } +} |