int Cconnection::post_select(fd_set* fd_read_set, fd_set* fd_write_set) { return FD_ISSET(m_s, fd_read_set) && recv() || FD_ISSET(m_s, fd_write_set) && send() || srv_time() - m_ctime > 10 || m_state == 5 && m_r.empty(); }
void write_db_users() { m_write_db_users_time = srv_time(); if (!m_use_sql) return; if (!m_torrents_users_updates_buffer.empty()) { m_torrents_users_updates_buffer.pop_back(); async_query("insert into " + db_name("files_users") + " (active, announced, completed, downloaded, `left`, uploaded, mtime, fid, uid) values " + m_torrents_users_updates_buffer + " on duplicate key update" + " active = values(active)," + " announced = announced + values(announced)," + " completed = completed + values(completed)," + " downloaded = downloaded + values(downloaded)," + " `left` = values(`left`)," + " uploaded = uploaded + values(uploaded)," + " mtime = values(mtime)"); m_torrents_users_updates_buffer.erase(); } async_query("update " + db_name("files_users") + " set active = 0 where mtime < unix_timestamp() - 60 * 60"); if (!m_users_updates_buffer.empty()) { m_users_updates_buffer.pop_back(); async_query("insert into " + db_name("users") + " (downloaded, uploaded, " + db_name("uid") + ") values " + m_users_updates_buffer + " on duplicate key update" + " downloaded = downloaded + values(downloaded)," + " uploaded = uploaded + values(uploaded)"); m_users_updates_buffer.erase(); } }
void arrival() /* a customer arrives */ { struct packet_info *t, *x; narr += 1; /* keep tally of number of arrivals */ q_sum += q; schedule(negexp(iat), ARRIVAL); /* schedule the next arrival */ int new_pkt_len = (int)(-log(drand48()) * mean_pkt_length); double utilisation = (q_len * 8 * iat)/(pow(10.0, 6.0)); if(utilisation>0.9) { nloss += 1; } else if ((new_pkt_len+q_len) <= buffer_size) { /* still space in buffer */ q += 1; q_len += new_pkt_len; t = NEW(PACKET_Q); for(x=headPkt ; x->next!=tailPkt ; x=x->next); t->pkt_len = new_pkt_len; t->next = x->next; x->next = t; if (q == 1) schedule(srv_time(headPkt->next->pkt_len), DEPARTURE); } else { /* buffer is full; packet is dropped */ nloss += 1; } }
Cconnection::Cconnection(const Csocket& s, const sockaddr_in& a) { m_s = s; m_a = a; m_ctime = srv_time(); m_state = 0; m_w = m_read_b; }
void read_db_users() { m_read_db_users_time = srv_time(); if (!m_use_sql) return; try { Csql_query q(m_database, "select @uid"); if (m_read_users_can_leech) q += ", can_leech"; if (m_read_users_peers_limit) q += ", peers_limit"; if (m_read_users_torrent_pass) q += ", torrent_pass"; q += ", torrent_pass_version"; if (m_read_users_wait_time) q += ", wait_time"; q += " from @users"; Csql_result result = q.execute(); // m_users.reserve(result.size()); for (auto& i : m_users) i.second.marked = true; m_users_torrent_passes.clear(); while (Csql_row row = result.fetch_row()) { t_user& user = m_users[row[0].i()]; user.marked = false; int c = 0; user.uid = row[c++].i(); if (m_read_users_can_leech) user.can_leech = row[c++].i(); if (m_read_users_peers_limit) user.peers_limit = row[c++].i(); if (m_read_users_torrent_pass) { if (row[c].size() == 32) m_users_torrent_passes[to_array<char, 32>(row[c])] = &user; c++; } user.torrent_pass_version = row[c++].i(); if (m_read_users_wait_time) user.wait_time = row[c++].i(); } for (auto i = m_users.begin(); i != m_users.end(); ) { if (i->second.marked) m_users.erase(i++); else i++; } } catch (bad_query&) { } }
void write_db_torrents() { m_write_db_torrents_time = srv_time(); if (!m_use_sql) return; try { std::string buffer; while (1) { for (auto& i : m_torrents) { t_torrent& file = i.second; if (!file.dirty) continue; if (!file.fid) { Csql_query(m_database, "insert into @files (info_hash, mtime, ctime) values (?, unix_timestamp(), unix_timestamp())")(i.first).execute(); file.fid = m_database.insert_id(); } buffer += Csql_query(m_database, "(?,?,?,?),")(file.leechers)(file.seeders)(file.completed)(file.fid).read(); file.dirty = false; if (buffer.size() > 255 << 10) break; } if (buffer.empty()) break; buffer.pop_back(); async_query("insert into " + db_name("files") + " (" + db_name("leechers") + ", " + db_name("seeders") + ", " + db_name("completed") + ", " + db_name("fid") + ") values " + buffer + " on duplicate key update" + " " + db_name("leechers") + " = values(" + db_name("leechers") + ")," + " " + db_name("seeders") + " = values(" + db_name("seeders") + ")," + " " + db_name("completed") + " = values(" + db_name("completed") + ")," + " mtime = unix_timestamp()"); buffer.clear(); } } catch (bad_query&) { } if (!m_announce_log_buffer.empty()) { m_announce_log_buffer.pop_back(); async_query("insert delayed into " + db_name("announce_log") + " (ipa, port, event, info_hash, peer_id, downloaded, left0, uploaded, uid, mtime) values " + m_announce_log_buffer); m_announce_log_buffer.erase(); } if (!m_scrape_log_buffer.empty()) { m_scrape_log_buffer.pop_back(); async_query("insert delayed into " + db_name("scrape_log") + " (ipa, uid, mtime) values " + m_scrape_log_buffer); m_scrape_log_buffer.erase(); } }
void debug(const t_torrent& t, std::ostream& os) { for (auto& i : t.peers) { os << "<tr><td>" + Csocket::inet_ntoa(i.first.host_) << "<td class=ar>" << ntohs(i.second.port) << "<td class=ar>" << i.second.uid << "<td class=ar>" << i.second.left << "<td class=ar>" << srv_time() - i.second.mtime << "<td>" << hex_encode(i.second.peer_id); } }
void departure() /* a customer departs */ { struct packet_info *x; q -= 1; x = headPkt->next; /* Delete event from linked list */ q_len -= x->pkt_len; headPkt->next = headPkt->next->next; free(x); if (q > 0) schedule(srv_time(headPkt->next->pkt_len), DEPARTURE); }
void arrival() /* a customer arrives */ { int i; struct packet_info *t, *x; narr += 1; /* keep tally of number of arrivals */ q_sum += q; schedule(negexp(iat), ARRIVAL); /* schedule the next arrival */ /* check whether to schedule a batch or individual packet arrival */ if (batch_interval == 1 || narr % batch_interval == 0) { num_packets = batch_size; batch_arrival = 1; batch_packets += num_packets; } else { num_packets = 1; batch_arrival = 0; } /* Create the appropriate number of packets for the type of arrival */ for (i = 0; i < num_packets; ++i) { total_packets += 1; int new_pkt_len = (int)(-log(drand48()) * MEAN_PKT_LENGTH); if ((new_pkt_len+q_len) <= BUFFER_SIZE) { /* still space in buffer */ q += 1; q_len += new_pkt_len; t = NEW(PACKET_Q); for(x=headPkt ; x->next!=tailPkt ; x=x->next); t->pkt_len = new_pkt_len; t->next = x->next; x->next = t; if (q == 1) schedule(srv_time(headPkt->next->pkt_len), DEPARTURE); } else { /* buffer is full; packet is dropped */ if (batch_arrival) { batch_nloss += 1; } else { nloss += 1; } } } }
void read_config() { if (m_use_sql) { try { Csql_result result = Csql_query(m_database, "select name, value from @config where value is not null").execute(); Cconfig config; while (Csql_row row = result.fetch_row()) { if (config.set(row[0].s(), row[1].s())) std::cerr << "unknown config name: " << row[0].s() << std::endl; } config.load(m_conf_file); if (config.m_torrent_pass_private_key.empty()) { config.m_torrent_pass_private_key = generate_random_string(27); Csql_query(m_database, "insert into @config (name, value) values ('torrent_pass_private_key', ?)")(config.m_torrent_pass_private_key).execute(); } m_config = config; m_database.set_name("completed", m_config.m_column_files_completed); m_database.set_name("leechers", m_config.m_column_files_leechers); m_database.set_name("seeders", m_config.m_column_files_seeders); m_database.set_name("fid", m_config.m_column_files_fid); m_database.set_name("uid", m_config.m_column_users_uid); m_database.set_name("announce_log", m_config.m_table_announce_log.empty() ? m_table_prefix + "announce_log" : m_config.m_table_announce_log); m_database.set_name("files", m_config.m_table_torrents.empty() ? m_table_prefix + "files" : m_config.m_table_torrents); m_database.set_name("files_users", m_config.m_table_torrents_users.empty() ? m_table_prefix + "files_users" : m_config.m_table_torrents_users); m_database.set_name("scrape_log", m_config.m_table_scrape_log.empty() ? m_table_prefix + "scrape_log" : m_config.m_table_scrape_log); m_database.set_name("users", m_config.m_table_users.empty() ? m_table_prefix + "users" : m_config.m_table_users); } catch (bad_query&) { } } else { Cconfig config; if (!config.load(m_conf_file)) m_config = config; } if (m_config.m_listen_ipas.empty()) m_config.m_listen_ipas.insert(htonl(INADDR_ANY)); if (m_config.m_listen_ports.empty()) m_config.m_listen_ports.insert(2710); m_read_config_time = srv_time(); }
void read_db_torrents() { m_read_db_torrents_time = srv_time(); if (m_use_sql) read_db_torrents_sql(); else if (!m_config.m_auto_register) { std::set<t_torrent*> new_torrents; std::ifstream is("xbt_torrents.txt"); for (std::string s; getline(is, s); ) { s = hex_decode(s); if (s.size() == 20) new_torrents.insert(&m_torrents[to_array<char, 20>(s)]); } for (auto i = m_torrents.begin(); i != m_torrents.end(); ) { if (new_torrents.count(&i->second)) i++; else m_torrents.erase(i++); } } }
std::string srv_statistics() { std::ostringstream os; os << "<!DOCTYPE HTML><meta http-equiv=refresh content=60><title>XBT Tracker</title>"; os << "<style>.ar { text-align: right }</style>"; int leechers = 0; int seeders = 0; int torrents = 0; for (auto& i : m_torrents) { leechers += i.second.leechers; seeders += i.second.seeders; torrents += i.second.leechers || i.second.seeders; } int peers = leechers + seeders; time_t t = srv_time(); time_t up_time = std::max<time_t>(1, t - m_stats.start_time); os << "<table>" << "<tr><td>peers<td class=ar>" << peers; if (peers) { os << "<tr><td>seeders<td class=ar>" << seeders << "<td class=ar>" << seeders * 100 / peers << " %" << "<tr><td>leechers<td class=ar>" << leechers << "<td class=ar>" << leechers * 100 / peers << " %"; } os << "<tr><td>torrents<td class=ar>" << torrents << "<tr><td>" << "<tr><td>accepted tcp<td class=ar>" << m_stats.accepted_tcp << "<td class=ar>" << m_stats.accepted_tcp / up_time << " /s" << "<tr><td>slow tcp<td class=ar>" << m_stats.slow_tcp << "<td class=ar>" << m_stats.slow_tcp / up_time << " /s" << "<tr><td>rejected tcp<td class=ar>" << m_stats.rejected_tcp << "<tr><td>accept errors<td class=ar>" << m_stats.accept_errors; if (m_stats.announced()) { os << "<tr><td>announced<td class=ar>" << m_stats.announced() << "<td class=ar>" << m_stats.announced() * 100 / m_stats.accepted_tcp << " %" << "<tr><td>announced http <td class=ar>" << m_stats.announced_http << "<td class=ar>" << m_stats.announced_http * 100 / m_stats.announced() << " %" << "<tr><td>announced udp<td class=ar>" << m_stats.announced_udp << "<td class=ar>" << m_stats.announced_udp * 100 / m_stats.announced() << " %"; } os << "<tr><td>scraped full<td class=ar>" << m_stats.scraped_full; os << "<tr><td>scraped multi<td class=ar>" << m_stats.scraped_multi; if (m_stats.scraped()) { os << "<tr><td>scraped<td class=ar>" << m_stats.scraped() << "<td class=ar>" << m_stats.scraped() * 100 / m_stats.accepted_tcp << " %" << "<tr><td>scraped http<td class=ar>" << m_stats.scraped_http << "<td class=ar>" << m_stats.scraped_http * 100 / m_stats.scraped() << " %" << "<tr><td>scraped udp<td class=ar>" << m_stats.scraped_udp << "<td class=ar>" << m_stats.scraped_udp * 100 / m_stats.scraped() << " %"; } os << "<tr><td>" << "<tr><td>up time<td class=ar>" << duration2a(up_time) << "<tr><td>" << "<tr><td>anonymous announce<td class=ar>" << m_config.m_anonymous_announce << "<tr><td>anonymous scrape<td class=ar>" << m_config.m_anonymous_scrape << "<tr><td>auto register<td class=ar>" << m_config.m_auto_register << "<tr><td>full scrape<td class=ar>" << m_config.m_full_scrape << "<tr><td>read config time<td class=ar>" << t - m_read_config_time << " / " << m_config.m_read_config_interval << "<tr><td>clean up time<td class=ar>" << t - m_clean_up_time << " / " << m_config.m_clean_up_interval << "<tr><td>read db files time<td class=ar>" << t - m_read_db_torrents_time << " / " << m_config.m_read_db_interval; if (m_use_sql) { os << "<tr><td>read db users time<td class=ar>" << t - m_read_db_users_time << " / " << m_config.m_read_db_interval << "<tr><td>write db files time<td class=ar>" << t - m_write_db_torrents_time << " / " << m_config.m_write_db_interval << "<tr><td>write db users time<td class=ar>" << t - m_write_db_users_time << " / " << m_config.m_write_db_interval; } os << "</table>"; return os.str(); }
std::string srv_scrape(const Ctracker_input& ti, t_user* user) { if (m_use_sql && m_config.m_log_scrape) m_scrape_log_buffer += Csql_query(m_database, "(?,?,?),")(ntohl(ti.m_ipa))(user ? user->uid : 0)(srv_time()).read(); if (!m_config.m_anonymous_scrape && !user) return "d14:failure reason25:unregistered torrent passe"; std::string d; d += "d5:filesd"; if (ti.m_info_hashes.empty()) { m_stats.scraped_full++; d.reserve(90 * m_torrents.size()); for (auto& i : m_torrents) { if (i.second.leechers || i.second.seeders) d += (boost::format("20:%sd8:completei%de10:downloadedi%de10:incompletei%dee") % boost::make_iterator_range(i.first) % i.second.seeders % i.second.completed % i.second.leechers).str(); } } else { m_stats.scraped_http++; if (ti.m_info_hashes.size() > 1) m_stats.scraped_multi++; for (auto& j : ti.m_info_hashes) { if (const t_torrent* i = find_torrent(j)) d += (boost::format("20:%sd8:completei%de10:downloadedi%de10:incompletei%dee") % j % i->seeders % i->completed % i->leechers).str(); } } d += "e"; if (m_config.m_scrape_interval) d += (boost::format("5:flagsd20:min_request_intervali%dee") % m_config.m_scrape_interval).str(); d += "e"; return d; }
std::string srv_insert_peer(const Ctracker_input& v, bool udp, t_user* user) { if (m_use_sql && m_config.m_log_announce) { m_announce_log_buffer += Csql_query(m_database, "(?,?,?,?,?,?,?,?,?,?),") (ntohl(v.m_ipa)) (ntohs(v.m_port)) (v.m_event) (v.m_info_hash) (v.m_peer_id) (v.m_downloaded) (v.m_left) (v.m_uploaded) (user ? user->uid : 0) (srv_time()) .read(); } if (!m_config.m_offline_message.empty()) return m_config.m_offline_message; if (0) return bts_banned_client; if (!m_config.m_anonymous_announce && !user) return bts_unregistered_torrent_pass; if (!m_config.m_auto_register && !find_torrent(v.m_info_hash)) return bts_unregistered_torrent; if (v.m_left && user && !user->can_leech) return bts_can_not_leech; t_torrent& file = m_torrents[to_array<char, 20>(v.m_info_hash)]; if (!file.ctime) file.ctime = srv_time(); if (v.m_left && user && user->wait_time && file.ctime + user->wait_time > srv_time()) return bts_wait_time; peer_key_c peer_key(v.m_ipa, user ? user->uid : 0); t_peer* i = find_ptr(file.peers, peer_key); if (i) (i->left ? file.leechers : file.seeders)--; else if (v.m_left && user && user->peers_limit) { int c = 0; for (auto& j : file.peers) c += j.second.left && j.second.uid == user->uid; if (c >= user->peers_limit) return bts_peers_limit_reached; } if (m_use_sql && user && file.fid) { long long downloaded = 0; long long uploaded = 0; if (i && i->uid == user->uid && boost::equals(i->peer_id, v.m_peer_id) && v.m_downloaded >= i->downloaded && v.m_uploaded >= i->uploaded) { downloaded = v.m_downloaded - i->downloaded; uploaded = v.m_uploaded - i->uploaded; } m_torrents_users_updates_buffer += Csql_query(m_database, "(?,1,?,?,?,?,?,?,?),") (v.m_event != Ctracker_input::e_stopped) (v.m_event == Ctracker_input::e_completed) (downloaded) (v.m_left) (uploaded) (srv_time()) (file.fid) (user->uid) .read(); if (downloaded || uploaded) m_users_updates_buffer += Csql_query(m_database, "(?,?,?),")(downloaded)(uploaded)(user->uid).read(); if (m_torrents_users_updates_buffer.size() > 255 << 10) write_db_users(); } if (v.m_event == Ctracker_input::e_stopped) file.peers.erase(peer_key); else { t_peer& peer = i ? *i : file.peers[peer_key]; peer.downloaded = v.m_downloaded; peer.left = v.m_left; peer.peer_id = v.m_peer_id; peer.port = v.m_port; peer.uid = user ? user->uid : 0; peer.uploaded = v.m_uploaded; (peer.left ? file.leechers : file.seeders)++; peer.mtime = srv_time(); } if (v.m_event == Ctracker_input::e_completed) file.completed++; (udp ? m_stats.announced_udp : m_stats.announced_http)++; file.dirty = true; return std::string(); }
int srv_run(const std::string& table_prefix, bool use_sql, const std::string& conf_file) { for (int i = 0; i < 8; i++) m_secret = m_secret << 8 ^ rand(); m_conf_file = conf_file; m_database.set_name("config", table_prefix + "config"); m_table_prefix = table_prefix; m_use_sql = use_sql; read_config(); if (test_sql()) return 1; if (m_epoll.create() == -1) { std::cerr << "epoll_create failed" << std::endl; return 1; } std::list<Ctcp_listen_socket> lt; std::list<Cudp_listen_socket> lu; for (auto& j : m_config.m_listen_ipas) { for (auto& i : m_config.m_listen_ports) { Csocket l; if (l.open(SOCK_STREAM) == INVALID_SOCKET) std::cerr << "socket failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; else if (l.setsockopt(SOL_SOCKET, SO_REUSEADDR, true), l.bind(j, htons(i))) std::cerr << "bind failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; else if (l.listen()) std::cerr << "listen failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; else { #ifdef SO_ACCEPTFILTER accept_filter_arg afa; bzero(&afa, sizeof(afa)); strcpy(afa.af_name, "httpready"); if (l.setsockopt(SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa))) std::cerr << "setsockopt failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; #elif 0 // TCP_DEFER_ACCEPT if (l.setsockopt(IPPROTO_TCP, TCP_DEFER_ACCEPT, 90)) std::cerr << "setsockopt failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; #endif lt.push_back(Ctcp_listen_socket(l)); if (!m_epoll.ctl(EPOLL_CTL_ADD, l, EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLERR | EPOLLHUP, <.back())) continue; } return 1; } for (auto& i : m_config.m_listen_ports) { Csocket l; if (l.open(SOCK_DGRAM) == INVALID_SOCKET) std::cerr << "socket failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; else if (l.setsockopt(SOL_SOCKET, SO_REUSEADDR, true), l.bind(j, htons(i))) std::cerr << "bind failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; else { lu.push_back(Cudp_listen_socket(l)); if (!m_epoll.ctl(EPOLL_CTL_ADD, l, EPOLLIN | EPOLLPRI | EPOLLERR | EPOLLHUP, &lu.back())) continue; } return 1; } } clean_up(); read_db_torrents(); read_db_users(); write_db_torrents(); write_db_users(); #ifndef NDEBUG // test_announce(); #endif #ifndef WIN32 if (m_config.m_daemon) { if (daemon(true, false)) std::cerr << "daemon failed" << std::endl; std::ofstream(m_config.m_pid_file.c_str()) << getpid() << std::endl; struct sigaction act; act.sa_handler = sig_handler; sigemptyset(&act.sa_mask); act.sa_flags = 0; if (sigaction(SIGTERM, &act, NULL)) std::cerr << "sigaction failed" << std::endl; act.sa_handler = SIG_IGN; if (sigaction(SIGPIPE, &act, NULL)) std::cerr << "sigaction failed" << std::endl; } #endif #ifdef EPOLL std::array<epoll_event, 64> events; #else fd_set fd_read_set; fd_set fd_write_set; fd_set fd_except_set; #endif while (!g_sig_term) { #ifdef EPOLL int r = m_epoll.wait(events.data(), events.size(), 5000); if (r == -1) std::cerr << "epoll_wait failed: " << errno << std::endl; else { time_t prev_time = m_time; m_time = ::time(NULL); for (int i = 0; i < r; i++) reinterpret_cast<Cclient*>(events[i].data.ptr)->process_events(events[i].events); if (m_time == prev_time) continue; for (auto i = m_connections.begin(); i != m_connections.end(); ) { if (i->run()) i = m_connections.erase(i); else i++; } } #else FD_ZERO(&fd_read_set); FD_ZERO(&fd_write_set); FD_ZERO(&fd_except_set); int n = 0; for (auto& i : m_connections) { int z = i.pre_select(&fd_read_set, &fd_write_set); n = std::max(n, z); } for (auto& i : lt) { FD_SET(i.s(), &fd_read_set); n = std::max<int>(n, i.s()); } for (auto& i : lu) { FD_SET(i.s(), &fd_read_set); n = std::max<int>(n, i.s()); } timeval tv; tv.tv_sec = 5; tv.tv_usec = 0; if (select(n + 1, &fd_read_set, &fd_write_set, &fd_except_set, &tv) == SOCKET_ERROR) std::cerr << "select failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; else { m_time = ::time(NULL); for (auto& i : lt) { if (FD_ISSET(i.s(), &fd_read_set)) accept(i.s()); } for (auto& i : lu) { if (FD_ISSET(i.s(), &fd_read_set)) Ctransaction(i.s()).recv(); } for (auto i = m_connections.begin(); i != m_connections.end(); ) { if (i->post_select(&fd_read_set, &fd_write_set)) i = m_connections.erase(i); else i++; } } #endif if (srv_time() - m_read_config_time > m_config.m_read_config_interval) read_config(); else if (srv_time() - m_clean_up_time > m_config.m_clean_up_interval) clean_up(); else if (srv_time() - m_read_db_torrents_time > m_config.m_read_db_interval) read_db_torrents(); else if (srv_time() - m_read_db_users_time > m_config.m_read_db_interval) read_db_users(); else if (m_config.m_write_db_interval && srv_time() - m_write_db_torrents_time > m_config.m_write_db_interval) write_db_torrents(); else if (m_config.m_write_db_interval && srv_time() - m_write_db_users_time > m_config.m_write_db_interval) write_db_users(); } write_db_torrents(); write_db_users(); unlink(m_config.m_pid_file.c_str()); return 0; }
void clean_up() { for (auto& i : m_torrents) clean_up(i.second, srv_time() - static_cast<int>(1.5 * m_config.m_announce_interval)); m_clean_up_time = srv_time(); }
void Cconnection::read(const std::string& v) { #ifndef NDEBUG std::cout << v << std::endl; #endif if (srv_config().m_log_access) { static std::ofstream f("xbt_tracker_raw.log"); f << srv_time() << '\t' << inet_ntoa(m_a.sin_addr) << '\t' << ntohs(m_a.sin_port) << '\t' << v << std::endl; } Ctracker_input ti; size_t e = v.find('?'); if (e == std::string::npos) e = v.size(); else { size_t a = e + 1; size_t b = v.find(' ', a); if (b == std::string::npos) return; while (a < b) { size_t c = v.find('=', a); if (c++ == std::string::npos) break; size_t d = v.find_first_of(" &", c); if (d == std::string::npos) break; ti.set(v.substr(a, c - a - 1), uri_decode(v.substr(c, d - c))); a = d + 1; } } if (!ti.m_ipa || !is_private_ipa(m_a.sin_addr.s_addr)) ti.m_ipa = m_a.sin_addr.s_addr; str_ref torrent_pass; size_t a = 4; if (a < e && v[a] == '/') { a++; if (a + 32 < e && v[a + 32] == '/') { torrent_pass.assign(&v[a], 32); a += 33; } } std::string h = "HTTP/1.0 200 OK\r\n"; std::string s; bool gzip = true; switch (a < v.size() ? v[a] : 0) { case 'a': if (ti.valid()) { gzip = false; std::string error = srv_insert_peer(ti, false, find_user_by_torrent_pass(torrent_pass, ti.m_info_hash)); s = error.empty() ? srv_select_peers(ti) : (boost::format("d14:failure reason%d:%se") % error.size() % error).str(); } break; case 'd': if (srv_config().m_debug) { h += "Content-Type: text/html; charset=us-ascii\r\n"; s = srv_debug(ti); } break; case 's': if (v.size() >= 7 && v[6] == 't') { h += "Content-Type: text/html; charset=us-ascii\r\n"; s = srv_statistics(); } else if (srv_config().m_full_scrape || !ti.m_info_hash.empty()) { gzip = srv_config().m_gzip_scrape && ti.m_info_hash.empty(); s = srv_scrape(ti, find_user_by_torrent_pass(torrent_pass, ti.m_info_hash)); } break; } if (s.empty()) { if (!ti.m_info_hash.empty() || srv_config().m_redirect_url.empty()) h = "HTTP/1.0 404 Not Found\r\n"; else { h = "HTTP/1.0 302 Found\r\n" "Location: " + srv_config().m_redirect_url + (ti.m_info_hash.empty() ? "" : "?info_hash=" + uri_encode(ti.m_info_hash)) + "\r\n"; } } else if (gzip) { shared_data s2 = xcc_z::gzip(s); #ifndef NDEBUG static std::ofstream f("xbt_tracker_gzip.log"); f << srv_time() << '\t' << v[5] << '\t' << s.size() << '\t' << s2.size() << std::endl; #endif if (s2.size() + 24 < s.size()) { h += "Content-Encoding: gzip\r\n"; s = to_string(s2); } } h += "\r\n"; #ifdef WIN32 m_write_b = shared_data(h.size() + s.size()); memcpy(m_write_b.data(), h); memcpy(m_write_b.data() + h.size(), s); int r = m_s.send(m_write_b); #else std::array<iovec, 2> d; d[0].iov_base = const_cast<char*>(h.data()); d[0].iov_len = h.size(); d[1].iov_base = const_cast<char*>(s.data()); d[1].iov_len = s.size(); msghdr m; m.msg_name = NULL; m.msg_namelen = 0; m.msg_iov = const_cast<iovec*>(d.data()); m.msg_iovlen = d.size(); m.msg_control = NULL; m.msg_controllen = 0; m.msg_flags = 0; int r = sendmsg(m_s, &m, MSG_NOSIGNAL); #endif if (r == SOCKET_ERROR) { if (WSAGetLastError() != WSAECONNRESET) std::cerr << "send failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; } else if (r != h.size() + s.size()) { #ifndef WIN32 if (r < h.size()) { m_write_b = shared_data(h.size() + s.size()); memcpy(m_write_b.data(), h); memcpy(m_write_b.data() + h.size(), s); } else { m_write_b = make_shared_data(s); r -= h.size(); } #endif m_r = m_write_b; m_r.advance_begin(r); } if (m_r.empty()) m_write_b.clear(); }
int Cconnection::run() { return s() == INVALID_SOCKET || srv_time() - m_ctime > 10; }