#include "main.h" #include "client.h" /* The state machine */ int state = CLIENT_STATE_INITIAL; /* Used in ping mode */ struct timespec ping_sent_time; /* Client mode tunnel */ tunnel client_tunnel; /* Sock representing the local port - call accept() on it */ int bind_sockfd; fd_set client_master_fdset; int handle_pong_frame(protocol_frame *rcvd_frame) { struct timespec pong_rcvd_time; double secs1, secs2; clock_gettime(CLOCK_MONOTONIC, &pong_rcvd_time); secs1 = (1.0 * ping_sent_time.tv_sec) + (1e-9 * ping_sent_time.tv_nsec); secs2 = (1.0 * pong_rcvd_time.tv_sec) + (1e-9 * pong_rcvd_time.tv_nsec); printf("GOT PONG! Time = %.3fs\n", secs2-secs1); if(ping_mode) { // state = CLIENT_STATE_PONG_RECEIVED; state = CLIENT_STATE_SEND_PING; } } int local_bind() { struct addrinfo hints, *res; char port[6]; int yes = 1; int flags; snprintf(port, 6, "%d", local_port); memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; // fill in my IP for me getaddrinfo(NULL, port, &hints, &res); bind_sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if(bind_sockfd < 0) { fprintf(stderr, "Could not create a socket for local listening: %s\n", strerror(errno)); exit(1); } setsockopt(bind_sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); /* Set O_NONBLOCK to make accept() non-blocking */ if (-1 == (flags = fcntl(bind_sockfd, F_GETFL, 0))) { flags = 0; } fcntl(bind_sockfd, F_SETFL, flags | O_NONBLOCK); if(bind(bind_sockfd, res->ai_addr, res->ai_addrlen) < 0) { fprintf(stderr, "Bind to port %d failed: %s\n", local_port, strerror(errno)); close(bind_sockfd); exit(1); } if(listen(bind_sockfd, 1) < 0) { fprintf(stderr, "Listening on port %d failed: %s\n", local_port, strerror(errno)); close(bind_sockfd); exit(1); } fprintf(stderr, "Bound to local port %d\n", local_port); } /* Bind the client.sockfd to a tunnel */ int handle_acktunnel_frame(protocol_frame *rcvd_frame) { tunnel *tun; if(!client_mode) { fprintf(stderr, "Got ACK tunnel frame when not in client mode!?\n"); return -1; } tun = tunnel_create( client_tunnel.sockfd, rcvd_frame->connid, rcvd_frame->friendnumber ); /* Mark that we can accept() another connection */ client_tunnel.sockfd = -1; printf("New tunnel ID: %d\n", tun->connid); if(client_local_port_mode) { update_select_nfds(tun->sockfd); FD_SET(tun->sockfd, &client_master_fdset); fprintf(stderr, "Accepted a new connection on port %d\n", local_port); } else { fprintf(stderr, "This tunnel mode is not supported yet"); exit(1); } } /* Handle a TCP frame received from server */ int handle_server_tcp_frame(protocol_frame *rcvd_frame) { int offset = 0; tunnel *tun = NULL; int tun_id = rcvd_frame->connid; HASH_FIND_INT(by_id, &tun_id, tun); if(!tun) { fprintf(stderr, "Got TCP frame with unknown tunnel ID %d\n", rcvd_frame->connid); return -1; } while(offset < rcvd_frame->data_length) { int sent_bytes; sent_bytes = send( tun->sockfd, rcvd_frame->data + offset, rcvd_frame->data_length - offset, 0 ); if(sent_bytes < 0) { char data[PROTOCOL_BUFFER_OFFSET]; protocol_frame frame_st, *frame; fprintf(stderr, "Could not write to socket %d: %s\n", tun->sockfd, strerror(errno)); frame = &frame_st; memset(frame, 0, sizeof(protocol_frame)); frame->friendnumber = tun->friendnumber; frame->packet_type = PACKET_TYPE_TCP_FIN; frame->connid = tun->connid; frame->data_length = 0; send_frame(frame, data); tunnel_delete(tun); return -1; } offset += sent_bytes; } printf("Got %d bytes from server - wrote to fd %d\n", rcvd_frame->data_length, tun->sockfd); return 0; } /* Main loop for the client */ int do_client_loop(char *tox_id_str) { unsigned char tox_packet_buf[PROTOCOL_MAX_PACKET_SIZE]; unsigned char tox_id[TOX_FRIEND_ADDRESS_SIZE]; uint32_t friendnumber; struct timeval tv; fd_set fds; client_tunnel.sockfd = 0; FD_ZERO(&client_master_fdset); if(!string_to_id(tox_id, tox_id_str)) { fprintf(stderr, "Invalid Tox ID"); exit(1); } if(!ping_mode) /* TODO handle pipe mode */ { local_bind(); signal(SIGPIPE, SIG_IGN); } fprintf(stderr, "Connecting to Tox...\n"); while(1) { /* Let tox do its stuff */ tox_do(tox); switch(state) { /* * Send friend request */ case CLIENT_STATE_INITIAL: if(tox_isconnected(tox)) { state = CLIENT_STATE_CONNECTED; } break; case CLIENT_STATE_CONNECTED: { uint8_t data[] = "Hi, fellow tuntox instance!"; uint16_t length = sizeof(data); fprintf(stderr, "Connected. Sending friend request.\n"); friendnumber = tox_add_friend( tox, tox_id, data, length ); if(friendnumber < 0) { fprintf(stderr, "Error %d adding friend %s\n", friendnumber, tox_id); exit(-1); } tox_lossless_packet_registerhandler(tox, friendnumber, (PROTOCOL_MAGIC_V1)>>8, parse_lossless_packet, (void*)&friendnumber); state = CLIENT_STATE_SENTREQUEST; fprintf(stderr, "Waiting for friend to accept us...\n"); } break; case CLIENT_STATE_SENTREQUEST: if(tox_get_friend_connection_status(tox, friendnumber) == 1) { fprintf(stderr, "Friend request accepted!\n"); state = CLIENT_STATE_REQUEST_ACCEPTED; } else { } break; case CLIENT_STATE_REQUEST_ACCEPTED: if(ping_mode) { state = CLIENT_STATE_SEND_PING; } else { state = CLIENT_STATE_BIND_PORT; } break; case CLIENT_STATE_SEND_PING: /* Send the ping packet */ { uint8_t data[] = { 0xa2, 0x6a, 0x01, 0x08, 0x00, 0x00, 0x00, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f }; clock_gettime(CLOCK_MONOTONIC, &ping_sent_time); tox_send_lossless_packet( tox, friendnumber, data, sizeof(data) ); } state = CLIENT_STATE_PING_SENT; break; case CLIENT_STATE_PING_SENT: /* Just sit there and wait for pong */ break; case CLIENT_STATE_BIND_PORT: if(bind_sockfd < 0) { fprintf(stderr, "Shutting down - could not bind to listening port\n"); state = CLIENT_STATE_SHUTDOWN; } else { state = CLIENT_STATE_FORWARDING; } break; case CLIENT_STATE_REQUEST_TUNNEL: send_tunnel_request_packet( remote_host, remote_port, friendnumber ); state = CLIENT_STATE_WAIT_FOR_ACKTUNNEL; break; case CLIENT_STATE_WAIT_FOR_ACKTUNNEL: break; case CLIENT_STATE_FORWARDING: { int accept_fd = 0; tunnel *tmp = NULL; tunnel *tun = NULL; tv.tv_sec = 0; tv.tv_usec = 20000; fds = client_master_fdset; /* Handle accepting new connections */ if(client_tunnel.sockfd <= 0) /* Don't accept if we're already waiting to establish a tunnel */ { accept_fd = accept(bind_sockfd, NULL, NULL); if(accept_fd != -1) { fprintf(stderr, "Accepting a new connection - requesting tunnel...\n"); /* Open a new tunnel for this FD */ client_tunnel.sockfd = accept_fd; send_tunnel_request_packet( remote_host, remote_port, friendnumber ); } } /* Handle reading from sockets */ select(select_nfds, &fds, NULL, NULL, &tv); HASH_ITER(hh, by_id, tun, tmp) { if(FD_ISSET(tun->sockfd, &fds)) { int nbytes = recv(tun->sockfd, tox_packet_buf + PROTOCOL_BUFFER_OFFSET, READ_BUFFER_SIZE, 0); /* Check if connection closed */ if(nbytes == 0) { char data[PROTOCOL_BUFFER_OFFSET]; protocol_frame frame_st, *frame; fprintf(stderr, "Connection closed\n"); frame = &frame_st; memset(frame, 0, sizeof(protocol_frame)); frame->friendnumber = tun->friendnumber; frame->packet_type = PACKET_TYPE_TCP_FIN; frame->connid = tun->connid; frame->data_length = 0; send_frame(frame, data); tunnel_delete(tun); } else { protocol_frame frame_st, *frame; frame = &frame_st; memset(frame, 0, sizeof(protocol_frame)); frame->friendnumber = tun->friendnumber; frame->packet_type = PACKET_TYPE_TCP; frame->connid = tun->connid; frame->data_length = nbytes; send_frame(frame, tox_packet_buf); printf("Wrote %d bytes from sock %d to tunnel %d\n", nbytes, tun->sockfd, tun->connid); } } } fds = client_master_fdset; } break; case CLIENT_STATE_SHUTDOWN: exit(0); break; } usleep(tox_do_interval(tox) * 1000); } }