/*
 * connection.cpp
 *
 *  Created on: Sep 23, 2017
 *      Author: root
 */

#include "connection.h"
#include "encrypt.h"
#include "fd_manager.h"

int disable_anti_replay = 0;  // if anti_replay windows is diabled

const int disable_conn_clear = 0;  // a raw connection is called conn.

conn_manager_t conn_manager;

anti_replay_seq_t anti_replay_t::get_new_seq_for_send() {
    return anti_replay_seq++;
}
anti_replay_t::anti_replay_t() {
    max_packet_received = 0;
    anti_replay_seq = get_true_random_number_64() / 10;  // random first seq
    // memset(window,0,sizeof(window)); //not necessary
}
void anti_replay_t::re_init() {
    max_packet_received = 0;
    // memset(window,0,sizeof(window));
}

int anti_replay_t::is_vaild(u64_t seq) {
    if (disable_anti_replay) return 1;
    // if(disabled) return 0;

    if (seq == max_packet_received)
        return 0;
    else if (seq > max_packet_received) {
        if (seq - max_packet_received >= anti_replay_window_size) {
            memset(window, 0, sizeof(window));
            window[seq % anti_replay_window_size] = 1;
        } else {
            for (u64_t i = max_packet_received + 1; i < seq; i++)
                window[i % anti_replay_window_size] = 0;
            window[seq % anti_replay_window_size] = 1;
        }
        max_packet_received = seq;
        return 1;
    } else if (seq < max_packet_received) {
        if (max_packet_received - seq >= anti_replay_window_size)
            return 0;
        else {
            if (window[seq % anti_replay_window_size] == 1)
                return 0;
            else {
                window[seq % anti_replay_window_size] = 1;
                return 1;
            }
        }
    }

    return 0;  // for complier check
}

void conn_info_t::recover(const conn_info_t &conn_info) {
    raw_info = conn_info.raw_info;

    raw_info.rst_received = 0;
    raw_info.disabled = 0;

    last_state_time = conn_info.last_state_time;
    last_hb_recv_time = conn_info.last_hb_recv_time;
    last_hb_sent_time = conn_info.last_hb_sent_time;
    my_id = conn_info.my_id;
    oppsite_id = conn_info.oppsite_id;
    blob->anti_replay.re_init();

    my_roller = 0;       // no need to set,but for easier debug,set it to zero
    oppsite_roller = 0;  // same as above
    last_oppsite_roller_time = 0;
}

void conn_info_t::re_init() {
    // send_packet_info.protocol=g_packet_info_send.protocol;
    if (program_mode == server_mode)
        state.server_current_state = server_idle;
    else
        state.client_current_state = client_idle;
    last_state_time = 0;
    oppsite_const_id = 0;

    timer_fd64 = 0;

    my_roller = 0;
    oppsite_roller = 0;
    last_oppsite_roller_time = 0;
}
conn_info_t::conn_info_t() {
    blob = 0;
    re_init();
}
void conn_info_t::prepare() {
    assert(blob == 0);
    blob = new blob_t;
    if (program_mode == server_mode) {
        blob->conv_manager.s.additional_clear_function = server_clear_function;
    } else {
        assert(program_mode == client_mode);
    }
}

conn_info_t::conn_info_t(const conn_info_t &b) {
    assert(0 == 1);
    // mylog(log_error,"called!!!!!!!!!!!!!\n");
}

conn_info_t &conn_info_t::operator=(const conn_info_t &b) {
    mylog(log_fatal, "not allowed\n");
    myexit(-1);
    return *this;
}
conn_info_t::~conn_info_t() {
    if (program_mode == server_mode) {
        if (state.server_current_state == server_ready) {
            assert(blob != 0);
            assert(oppsite_const_id != 0);
            // assert(conn_manager.const_id_mp.find(oppsite_const_id)!=conn_manager.const_id_mp.end()); // conn_manager 's deconstuction function  erases it
        } else {
            assert(blob == 0);
            assert(oppsite_const_id == 0);
        }
    }
    assert(timer_fd64 == 0);
    // if(oppsite_const_id!=0)     //do this at conn_manager 's deconstuction function
    // conn_manager.const_id_mp.erase(oppsite_const_id);
    if (blob != 0)
        delete blob;

    // send_packet_info.protocol=g_packet_info_send.protocol;
}

conn_manager_t::conn_manager_t() {
    ready_num = 0;
    mp.reserve(10007);
    // clear_it=mp.begin();
    // timer_fd_mp.reserve(10007);
    const_id_mp.reserve(10007);
    // udp_fd_mp.reserve(100007);
    last_clear_time = 0;
    // current_ready_ip=0;
    // current_ready_port=0;
}
int conn_manager_t::exist(address_t addr) {
    // u64_t u64=0;
    // u64=ip;
    // u64<<=32u;
    // u64|=port;
    if (mp.find(addr) != mp.end()) {
        return 1;
    }
    return 0;
}
/*
int insert(uint32_t ip,uint16_t port)
{
        uint64_t u64=0;
        u64=ip;
        u64<<=32u;
        u64|=port;
        mp[u64];
        return 0;
}*/
conn_info_t *&conn_manager_t::find_insert_p(address_t addr)  // be aware,the adress may change after rehash
{
    // u64_t u64=0;
    // u64=ip;
    // u64<<=32u;
    // u64|=port;
    unordered_map<address_t, conn_info_t *>::iterator it = mp.find(addr);
    if (it == mp.end()) {
        mp[addr] = new conn_info_t;
        // lru.new_key(addr);
    } else {
        // lru.update(addr);
    }
    return mp[addr];
}
conn_info_t &conn_manager_t::find_insert(address_t addr)  // be aware,the adress may change after rehash
{
    // u64_t u64=0;
    // u64=ip;
    // u64<<=32u;
    // u64|=port;
    unordered_map<address_t, conn_info_t *>::iterator it = mp.find(addr);
    if (it == mp.end()) {
        mp[addr] = new conn_info_t;
        // lru.new_key(addr);
    } else {
        // lru.update(addr);
    }
    return *mp[addr];
}
int conn_manager_t::erase(unordered_map<address_t, conn_info_t *>::iterator erase_it) {
    if (erase_it->second->state.server_current_state == server_ready) {
        ready_num--;
        assert(i32_t(ready_num) != -1);
        assert(erase_it->second != 0);

        assert(erase_it->second->timer_fd64 != 0);

        assert(fd_manager.exist(erase_it->second->timer_fd64));

        assert(erase_it->second->oppsite_const_id != 0);
        assert(const_id_mp.find(erase_it->second->oppsite_const_id) != const_id_mp.end());

        // assert(timer_fd_mp.find(erase_it->second->timer_fd)!=timer_fd_mp.end());

        const_id_mp.erase(erase_it->second->oppsite_const_id);

        fd_manager.fd64_close(erase_it->second->timer_fd64);

        erase_it->second->timer_fd64 = 0;
        // timer_fd_mp.erase(erase_it->second->timer_fd);
        // close(erase_it->second->timer_fd);// close will auto delte it from epoll
        delete (erase_it->second);
        mp.erase(erase_it->first);
    } else {
        assert(erase_it->second->blob == 0);
        assert(erase_it->second->timer_fd64 == 0);

        assert(erase_it->second->oppsite_const_id == 0);
        delete (erase_it->second);
        mp.erase(erase_it->first);
    }
    return 0;
}
int conn_manager_t::clear_inactive() {
    if (get_current_time() - last_clear_time > conn_clear_interval) {
        last_clear_time = get_current_time();
        return clear_inactive0();
    }
    return 0;
}
int conn_manager_t::clear_inactive0() {
    unordered_map<address_t, conn_info_t *>::iterator it;
    unordered_map<address_t, conn_info_t *>::iterator old_it;

    if (disable_conn_clear) return 0;

    // map<uint32_t,uint64_t>::iterator it;
    int cnt = 0;
    it = clear_it;
    int size = mp.size();
    int num_to_clean = size / conn_clear_ratio + conn_clear_min;  // clear 1/10 each time,to avoid latency glitch

    mylog(log_trace, "mp.size() %d\n", size);

    num_to_clean = min(num_to_clean, (int)mp.size());
    u64_t current_time = get_current_time();

    for (;;) {
        if (cnt >= num_to_clean) break;
        if (mp.begin() == mp.end()) break;

        if (it == mp.end()) {
            it = mp.begin();
        }

        if (it->second->state.server_current_state == server_ready && current_time - it->second->last_hb_recv_time <= server_conn_timeout) {
            it++;
        } else if (it->second->state.server_current_state != server_ready && current_time - it->second->last_state_time <= server_handshake_timeout) {
            it++;
        } else if (it->second->blob != 0 && it->second->blob->conv_manager.s.get_size() > 0) {
            assert(it->second->state.server_current_state == server_ready);
            it++;
        } else {
            mylog(log_info, "[%s:%d]inactive conn cleared \n", it->second->raw_info.recv_info.new_src_ip.get_str1(), it->second->raw_info.recv_info.src_port);
            old_it = it;
            it++;
            erase(old_it);
        }
        cnt++;
    }
    clear_it = it;

    return 0;
}

int send_bare(raw_info_t &raw_info, const char *data, int len)  // send function with encryption but no anti replay,this is used when client and server verifys each other
// you have to design the protocol carefully, so that you wont be affect by relay attack
{
    if (len < 0) {
        mylog(log_debug, "input_len <0\n");
        return -1;
    }
    packet_info_t &send_info = raw_info.send_info;
    packet_info_t &recv_info = raw_info.recv_info;

    char send_data_buf[buf_len];  // buf for send data and send hb
    char send_data_buf2[buf_len];

    // static send_bare[buf_len];
    iv_t iv = get_true_random_number_64();
    padding_t padding = get_true_random_number_64();

    memcpy(send_data_buf, &iv, sizeof(iv));
    memcpy(send_data_buf + sizeof(iv), &padding, sizeof(padding));

    send_data_buf[sizeof(iv) + sizeof(padding)] = 'b';
    memcpy(send_data_buf + sizeof(iv) + sizeof(padding) + 1, data, len);
    int new_len = len + sizeof(iv) + sizeof(padding) + 1;

    if (my_encrypt(send_data_buf, send_data_buf2, new_len) != 0) {
        return -1;
    }
    send_raw0(raw_info, send_data_buf2, new_len);
    return 0;
}
int reserved_parse_bare(const char *input, int input_len, char *&data, int &len)  // a sub function used in recv_bare
{
    static char recv_data_buf[buf_len];

    if (input_len < 0) {
        mylog(log_debug, "input_len <0\n");
        return -1;
    }
    if (my_decrypt(input, recv_data_buf, input_len) != 0) {
        mylog(log_debug, "decrypt_fail in recv bare\n");
        return -1;
    }
    if (recv_data_buf[sizeof(iv_t) + sizeof(padding_t)] != 'b') {
        mylog(log_debug, "not a bare packet\n");
        return -1;
    }
    len = input_len;
    data = recv_data_buf + sizeof(iv_t) + sizeof(padding_t) + 1;
    len -= sizeof(iv_t) + sizeof(padding_t) + 1;
    if (len < 0) {
        mylog(log_debug, "len <0\n");
        return -1;
    }
    return 0;
}
int recv_bare(raw_info_t &raw_info, char *&data, int &len)  // recv function with encryption but no anti replay,this is used when client and server verifys each other
// you have to design the protocol carefully, so that you wont be affect by relay attack
{
    packet_info_t &send_info = raw_info.send_info;
    packet_info_t &recv_info = raw_info.recv_info;

    if (recv_raw0(raw_info, data, len) < 0) {
        // printf("recv_raw_fail in recv bare\n");
        return -1;
    }

    if (len >= max_data_len + 1) {
        mylog(log_debug, "data_len=%d >= max_data_len+1,ignored", len);
        return -1;
    }

    mylog(log_trace, "data len=%d\n", len);
    if ((raw_mode == mode_faketcp && (recv_info.syn == 1 || recv_info.ack != 1))) {
        mylog(log_debug, "unexpect packet type recv_info.syn=%d recv_info.ack=%d \n", recv_info.syn, recv_info.ack);
        return -1;
    }
    return reserved_parse_bare(data, len, data, len);
}

int send_handshake(raw_info_t &raw_info, my_id_t id1, my_id_t id2, my_id_t id3)  // a warp for send_bare for sending handshake(this is not tcp handshake) easily
{
    packet_info_t &send_info = raw_info.send_info;
    packet_info_t &recv_info = raw_info.recv_info;

    char *data;
    int len;
    // len=sizeof(id_t)*3;
    if (numbers_to_char(id1, id2, id3, data, len) != 0) return -1;
    if (send_bare(raw_info, data, len) != 0) {
        mylog(log_warn, "send bare fail\n");
        return -1;
    }
    return 0;
}
/*
int recv_handshake(packet_info_t &info,id_t &id1,id_t &id2,id_t &id3)
{
        char * data;int len;
        if(recv_bare(info,data,len)!=0) return -1;

        if(char_to_numbers(data,len,id1,id2,id3)!=0) return -1;

        return 0;
}*/

int send_safer(conn_info_t &conn_info, char type, const char *data, int len)  // safer transfer function with anti-replay,when mutually verification is done.
{
    packet_info_t &send_info = conn_info.raw_info.send_info;
    packet_info_t &recv_info = conn_info.raw_info.recv_info;

    if (type != 'h' && type != 'd') {
        mylog(log_warn, "first byte is not h or d  ,%x\n", type);
        return -1;
    }

    char send_data_buf[buf_len];  // buf for send data and send hb
    char send_data_buf2[buf_len];

    my_id_t n_tmp_id = htonl(conn_info.my_id);

    memcpy(send_data_buf, &n_tmp_id, sizeof(n_tmp_id));

    n_tmp_id = htonl(conn_info.oppsite_id);

    memcpy(send_data_buf + sizeof(n_tmp_id), &n_tmp_id, sizeof(n_tmp_id));

    anti_replay_seq_t n_seq = hton64(conn_info.blob->anti_replay.get_new_seq_for_send());

    memcpy(send_data_buf + sizeof(n_tmp_id) * 2, &n_seq, sizeof(n_seq));

    send_data_buf[sizeof(n_tmp_id) * 2 + sizeof(n_seq)] = type;
    send_data_buf[sizeof(n_tmp_id) * 2 + sizeof(n_seq) + 1] = conn_info.my_roller;

    memcpy(send_data_buf + 2 + sizeof(n_tmp_id) * 2 + sizeof(n_seq), data, len);  // data;

    int new_len = len + sizeof(n_seq) + sizeof(n_tmp_id) * 2 + 2;

    if (g_fix_gro == 0) {
        if (my_encrypt(send_data_buf, send_data_buf2, new_len) != 0) {
            return -1;
        }
    } else {
        if (my_encrypt(send_data_buf, send_data_buf2 + 2, new_len) != 0) {
            return -1;
        }
        write_u16(send_data_buf2, new_len);
        new_len += 2;
        if (cipher_mode == cipher_xor) {
            send_data_buf2[0] ^= gro_xor[0];
            send_data_buf2[1] ^= gro_xor[1];
        } else if (cipher_mode == cipher_aes128cbc || cipher_mode == cipher_aes128cbc) {
            aes_ecb_encrypt1(send_data_buf2);
        }
    }

    if (send_raw0(conn_info.raw_info, send_data_buf2, new_len) != 0) return -1;

    if (after_send_raw0(conn_info.raw_info) != 0) return -1;

    return 0;
}
int send_data_safer(conn_info_t &conn_info, const char *data, int len, u32_t conv_num)  // a wrap for  send_safer for transfer data.
{
    packet_info_t &send_info = conn_info.raw_info.send_info;
    packet_info_t &recv_info = conn_info.raw_info.recv_info;

    char send_data_buf[buf_len];
    // send_data_buf[0]='d';
    u32_t n_conv_num = htonl(conv_num);
    memcpy(send_data_buf, &n_conv_num, sizeof(n_conv_num));

    memcpy(send_data_buf + sizeof(n_conv_num), data, len);
    int new_len = len + sizeof(n_conv_num);
    send_safer(conn_info, 'd', send_data_buf, new_len);
    return 0;
}
int reserved_parse_safer(conn_info_t &conn_info, const char *input, int input_len, char &type, char *&data, int &len)  // subfunction for recv_safer,allow overlap
{
    static char recv_data_buf[buf_len];

    // char *recv_data_buf=recv_data_buf0; //fix strict alias warning
    if (my_decrypt(input, recv_data_buf, input_len) != 0) {
        // printf("decrypt fail\n");
        return -1;
    }

    // char *a=recv_data_buf;
    // id_t h_oppiste_id= ntohl (  *((id_t * )(recv_data_buf)) );
    my_id_t h_oppsite_id;
    memcpy(&h_oppsite_id, recv_data_buf, sizeof(h_oppsite_id));
    h_oppsite_id = ntohl(h_oppsite_id);

    // id_t h_my_id= ntohl (  *((id_t * )(recv_data_buf+sizeof(id_t)))    );
    my_id_t h_my_id;
    memcpy(&h_my_id, recv_data_buf + sizeof(my_id_t), sizeof(h_my_id));
    h_my_id = ntohl(h_my_id);

    // anti_replay_seq_t h_seq= ntoh64 (  *((anti_replay_seq_t * )(recv_data_buf  +sizeof(id_t) *2 ))   );
    anti_replay_seq_t h_seq;
    memcpy(&h_seq, recv_data_buf + sizeof(my_id_t) * 2, sizeof(h_seq));
    h_seq = ntoh64(h_seq);

    if (h_oppsite_id != conn_info.oppsite_id || h_my_id != conn_info.my_id) {
        mylog(log_debug, "id and oppsite_id verification failed %x %x %x %x \n", h_oppsite_id, conn_info.oppsite_id, h_my_id, conn_info.my_id);
        return -1;
    }

    if (conn_info.blob->anti_replay.is_vaild(h_seq) != 1) {
        mylog(log_debug, "dropped replay packet\n");
        return -1;
    }

    // printf("recv _len %d\n ",recv_len);
    data = recv_data_buf + sizeof(anti_replay_seq_t) + sizeof(my_id_t) * 2;
    len = input_len - (sizeof(anti_replay_seq_t) + sizeof(my_id_t) * 2);

    if (data[0] != 'h' && data[0] != 'd') {
        mylog(log_debug, "first byte is not h or d  ,%x\n", data[0]);
        return -1;
    }

    uint8_t roller = data[1];

    type = data[0];
    data += 2;
    len -= 2;

    if (len < 0) {
        mylog(log_debug, "len <0 ,%d\n", len);
        return -1;
    }

    if (roller != conn_info.oppsite_roller) {
        conn_info.oppsite_roller = roller;
        conn_info.last_oppsite_roller_time = get_current_time();
    }
    if (hb_mode == 0)
        conn_info.my_roller++;  // increase on a successful recv
    else if (hb_mode == 1) {
        if (type == 'h')
            conn_info.my_roller++;
    } else {
        mylog(log_fatal, "unknow hb_mode\n");
        myexit(-1);
    }

    if (after_recv_raw0(conn_info.raw_info) != 0) return -1;  // TODO might need to move this function to somewhere else after --fix-gro is introduced

    return 0;
}
int recv_safer_notused(conn_info_t &conn_info, char &type, char *&data, int &len)  /// safer transfer function with anti-replay,when mutually verification is done.
{
    packet_info_t &send_info = conn_info.raw_info.send_info;
    packet_info_t &recv_info = conn_info.raw_info.recv_info;

    char *recv_data;
    int recv_len;
    // static char recv_data_buf[buf_len];

    if (recv_raw0(conn_info.raw_info, recv_data, recv_len) != 0) return -1;

    return reserved_parse_safer(conn_info, recv_data, recv_len, type, data, len);
}

int recv_safer_multi(conn_info_t &conn_info, vector<char> &type_arr, vector<string> &data_arr)  /// safer transfer function with anti-replay,when mutually verification is done.
{
    packet_info_t &send_info = conn_info.raw_info.send_info;
    packet_info_t &recv_info = conn_info.raw_info.recv_info;

    char *recv_data;
    int recv_len;
    assert(type_arr.empty());
    assert(data_arr.empty());

    if (recv_raw0(conn_info.raw_info, recv_data, recv_len) != 0) return -1;

    char type;
    char *data;
    int len;

    if (g_fix_gro == 0) {
        int ret = reserved_parse_safer(conn_info, recv_data, recv_len, type, data, len);
        if (ret == 0) {
            type_arr.push_back(type);
            data_arr.emplace_back(data, data + len);
            // std::copy(data,data+len,data_arr[0]);
        }
        return 0;
    } else {
        char *ori_recv_data = recv_data;
        int ori_recv_len = recv_len;
        // mylog(log_debug,"recv_len:%d\n",recv_len);
        int cnt = 0;
        while (recv_len >= 16) {
            cnt++;
            int single_len_no_xor;
            single_len_no_xor = read_u16(recv_data);
            int single_len;
            if (cipher_mode == cipher_xor) {
                recv_data[0] ^= gro_xor[0];
                recv_data[1] ^= gro_xor[1];
            } else if (cipher_mode == cipher_aes128cbc || cipher_mode == cipher_aes128cbc) {
                aes_ecb_decrypt1(recv_data);
            }
            single_len = read_u16(recv_data);
            recv_len -= 2;
            recv_data += 2;
            if (single_len > recv_len) {
                mylog(log_debug, "illegal single_len %d(%d), recv_len %d left,dropped\n", single_len, single_len_no_xor, recv_len);
                break;
            }
            if (single_len > max_data_len) {
                mylog(log_warn, "single_len %d(%d) > %d, maybe you need to turn down mtu at upper level\n", single_len, single_len_no_xor, max_data_len);
                break;
            }

            int ret = reserved_parse_safer(conn_info, recv_data, single_len, type, data, len);

            if (ret != 0) {
                mylog(log_debug, "parse failed, offset= %d,single_len=%d(%d)\n", (int)(recv_data - ori_recv_data), single_len, single_len_no_xor);
            } else {
                type_arr.push_back(type);
                data_arr.emplace_back(data, data + len);
                // std::copy(data,data+len,data_arr[data_arr.size()-1]);
            }
            recv_data += single_len;
            recv_len -= single_len;
        }
        if (cnt > 1) {
            mylog(log_debug, "got a suspected gro packet, %d packets recovered, recv_len=%d, loop_cnt=%d\n", (int)data_arr.size(), ori_recv_len, cnt);
        }
        return 0;
    }
}

void server_clear_function(u64_t u64)  // used in conv_manager in server mode.for server we have to use one udp fd for one conv(udp connection),
// so we have to close the fd when conv expires
{
    // int fd=int(u64);
    //	int ret;
    // assert(fd!=0);
    /*
    epoll_event ev;

    ev.events = EPOLLIN;
    ev.data.u64 = u64;

    ret = epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &ev);
    if (ret!=0)
    {
            mylog(log_fatal,"fd:%d epoll delete failed!!!!\n",fd);
            myexit(-1);   //this shouldnt happen
    }*/
    // no need

    /*ret= close(fd);  //closed fd should be auto removed from epoll

    if (ret!=0)
    {
            mylog(log_fatal,"close fd %d failed !!!!\n",fd);
            myexit(-1);  //this shouldnt happen
    }*/
    // mylog(log_fatal,"size:%d !!!!\n",conn_manager.udp_fd_mp.size());
    fd64_t fd64 = u64;
    assert(fd_manager.exist(fd64));
    fd_manager.fd64_close(fd64);

    // assert(conn_manager.udp_fd_mp.find(fd)!=conn_manager.udp_fd_mp.end());
    // conn_manager.udp_fd_mp.erase(fd);
}
