ctorrent_ipv6/tracker.cpp

435 lines
11 KiB
C++

#include "tracker.h"
#ifndef WINDOWS
#include <unistd.h>
#include <sys/time.h>
#include <time.h>
#include <netdb.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "peerlist.h"
#include "httpencode.h"
#include "bencode.h"
#include "setnonblock.h"
#include "connect_nonb.h"
#include "btcontent.h"
#include "iplist.h"
#include "btconfig.h"
btTracker Tracker;
btTracker::btTracker()
{
memset(m_host,0,MAXHOSTNAMELEN);
memset(m_path,0,MAXPATHLEN);
m_sock = INVALID_SOCKET;
m_port = 80;
m_status = T_FREE;
m_f_started = m_f_stoped = m_f_pause = 0;
m_interval = 15;
m_connect_refuse_click = 0;
m_last_timestamp = (time_t) 0;
}
btTracker::~btTracker()
{
if( m_sock != INVALID_SOCKET) CLOSE_SOCKET(m_sock);
}
void btTracker::Reset(time_t new_interval)
{
if(new_interval) m_interval = new_interval;
if( INVALID_SOCKET != m_sock ){
CLOSE_SOCKET(m_sock);
m_sock = INVALID_SOCKET;
}
m_reponse_buffer.Reset();
time(&m_last_timestamp);
m_status = T_FREE;
}
int btTracker:: _IPsin(char *h, int p, struct sockaddr_in6 *psin)
{
psin->sin6_family = AF_INET6;
psin->sin6_port = htons(p);
return (inet_pton(AF_INET6, h, &psin->sin6_addr ) != 1) ? -1 : 0;
}
int btTracker:: _s2sin(char *h,int p,struct sockaddr_in6 *psin)
{
psin->sin6_family = AF_INET6;
psin->sin6_port = htons(p);
if( h ){
if( inet_pton(AF_INET6, h, &psin->sin6_addr ) != 1 ){
struct hostent *ph = gethostbyname2(h,AF_INET6);
if( !ph || ph->h_addrtype != AF_INET6){
memset(psin,0,sizeof(struct sockaddr_in6));
return -1;
}
memcpy(&psin->sin6_addr,ph->h_addr_list[0],sizeof(struct in6_addr));
}
}else
memcpy(&psin->sin6_addr, &in6addr_any, sizeof(struct in6_addr) );
return 0;
}
// do modyfikacji (lista perow inaczej przesy³ana)
int btTracker::_UpdatePeerList(char *buf,size_t bufsiz)
{
char tmphost[MAXHOSTNAMELEN];
const char *ps;
size_t i,pos,tmpport;
size_t cnt = 0;
struct sockaddr_in6 addr;
if( decode_query(buf,bufsiz,"failure reason",&ps,&i,QUERY_STR) ){
char failreason[1024];
if( i < 1024 ){
memcpy(failreason, ps, i);
failreason[i] = '\0';
}else{
memcpy(failreason, ps, 1000);
failreason[1000] = '\0';
strcat(failreason,"...");
}
fprintf(stderr,"TRACKER FAILURE REASON: %s\n",failreason);
return -1;
}
if(!decode_query(buf,bufsiz,"interval",(const char**) 0,&i,QUERY_INT)){return -1;}
if(m_interval != (time_t)i) m_interval = (time_t)i;
pos = decode_query(buf,bufsiz,"peers",(const char**) 0,(size_t *) 0,QUERY_POS);
if( !pos ){
return -1;
}
if(4 > bufsiz - pos){return -1; } // peers list ̫С
buf += (pos + 1); bufsiz -= (pos + 1);
ps = buf-1; // bankowo do modyfikacji, sprawdzic dane od trakera, lista perrów !! z ipv4 do ipv6
if (*ps != 'l') { // binary peers section if not 'l'
addr.sin6_family = AF_INET6;
i = 0;
while (*ps != ':' ) i = i * 10 + (*ps++ - '0'); // konwersja liszby string na inta=a
i /= 18; // z 6 zmieniono na 18, ipv6 adres to jest 16bajtow + 2 bajty na port // ile adresów ip w binarce
ps++;
while (i-- > 0) {
// if peer is not us
if(memcmp(&Self.m_sin.sin6_addr,ps,sizeof(struct in6_addr))) {
memcpy(&addr.sin6_addr,ps,sizeof(struct in6_addr));
memcpy(&addr.sin6_port,ps+sizeof(struct in6_addr),sizeof(unsigned short));
cnt++;
IPQUEUE.Add(&addr);
}
ps += 18;
}
}
else
for( ;bufsiz && *buf!='e'; buf += pos, bufsiz -= pos ){
pos = decode_dict(buf,bufsiz,(char*) 0);
if(!pos) break;
if(!decode_query(buf,pos,"ip",&ps,&i,QUERY_STR) || MAXHOSTNAMELEN < i) continue;
memcpy(tmphost,ps,i); tmphost[i] = '\0';
if(!decode_query(buf,pos,"port",(const char**) 0,&tmpport,QUERY_INT)) continue;
if(!decode_query(buf,pos,"peer id",&ps,&i,QUERY_STR) && i != 20 ) continue;
if(_IPsin(tmphost,tmpport,&addr) < 0){
fprintf(stderr,"warn, detected invalid ip address %s.\n",tmphost);
continue;
}
if( !Self.IpEquiv(addr) ){
cnt++;
IPQUEUE.Add(&addr);
}
}
if( !cnt ) fprintf(stderr,"warn, peers list received from tracker is empty.\n");
return 0;
}
int btTracker::CheckReponse()
{
#define MAX_LINE_SIZ 32
char *pdata;
ssize_t r;
size_t q, hlen, dlen;
r = m_reponse_buffer.FeedIn(m_sock);
if( r > 0 ) return 0;
q = m_reponse_buffer.Count();
Reset( (-1 == r) ? 15 : 0 );
if( !q ){
int error = 0;
socklen_t n = sizeof(error);
if(getsockopt(m_sock, SOL_SOCKET,SO_ERROR,&error,&n) < 0 ||
error != 0 ){
fprintf(stderr,"warn, received nothing from tracker! %s\n",strerror(error));
}
return -1;
}
hlen = Http_split(m_reponse_buffer.BasePointer(), q, &pdata,&dlen);
if( !hlen ){
fprintf(stderr,"warn, tracker reponse invalid. No html header found.\n");
return -1;
}
r = Http_reponse_code(m_reponse_buffer.BasePointer(),hlen);
if ( r != 200 ){
if( r == 301 || r == 302 ){
char redirect[MAXPATHLEN],ih_buf[20 * 3 + 1],pi_buf[20 * 3 + 1],tmppath[MAXPATHLEN];
if( Http_get_header(m_reponse_buffer.BasePointer(), hlen, "Location", redirect) < 0 )
return -1;
if( Http_url_analyse(redirect,m_host,&m_port,m_path) < 0){
fprintf(stderr,"warn, tracker redirect to an invalid url %s!\n", redirect);
return -1;
}
strcpy(tmppath,m_path);
if(MAXPATHLEN < snprintf(m_path,MAXPATHLEN,REQ_URL_P1_FMT,
tmppath,
Http_url_encode(ih_buf, (char*)BTCONTENT.GetInfoHash(), 20),
Http_url_encode(pi_buf, (char*)BTCONTENT.GetPeerId(), 20),
cfg_listen_port)){
return -1;
}
return Connect();
}else if( r >= 400 ){
fprintf(stderr,"\nTracker reponse code >= 400 !!! The file is not registered on this tracker, or it may have been removed. IF YOU SEE THIS MESSAGE FOR A LONG TIME AND DOWNLOAD DOESN'T BEGIN, RECOMMEND YOU STOP NOW!!!\n");
fprintf(stderr,"\nTracker reponse data DUMP:\n");
if( pdata && dlen ) write(STDERR_FILENO, pdata, dlen);
fprintf(stderr,"\n== DUMP OVER==\n");
return -1;
}else
return 0;
}
if ( !pdata ) return 0;
if( !m_f_started ) m_f_started = 1;
m_connect_refuse_click = 0;
return _UpdatePeerList(pdata,dlen);
}
int btTracker::Initial()
{
char ih_buf[20 * 3 + 1],pi_buf[20 * 3 + 1],tmppath[MAXPATHLEN];
if(Http_url_analyse(BTCONTENT.GetAnnounce(),m_host,&m_port,m_path) < 0){
fprintf(stderr,"error, invalid tracker url format!\n");
return -1;
}
strcpy(tmppath,m_path);
if(MAXPATHLEN < snprintf((char*)m_path,MAXPATHLEN,REQ_URL_P1_FMT,
tmppath,
Http_url_encode(ih_buf,(char*)BTCONTENT.GetInfoHash(),20),
Http_url_encode(pi_buf,(char*)BTCONTENT.GetPeerId(),20),
cfg_listen_port)){
return -1;
}
/* get local ip address */
// 1st: if behind firewall, this only gets local side
{
struct sockaddr_in6 addr;
socklen_t addrlen = sizeof(struct sockaddr_in6);
if(getsockname(m_sock,(struct sockaddr*)&addr,&addrlen) == 0)
Self.SetIp(addr);
}
// 2nd: better to use addr of our domain
{
struct hostent *h;
char hostname[128];
char *hostdots[2]={0,0}, *hdptr=hostname;
if (gethostname(hostname, 128) == -1) return -1;
// printf("%s\n", hostname);
while(*hdptr) if(*hdptr++ == '.') {
hostdots[0] = hostdots[1];
hostdots[1] = hdptr;
}
if (hostdots[0] == 0) return -1;
// printf("%s\n", hostdots[0]);
if ((h = gethostbyname2(hostdots[0],AF_INET6)) == NULL) return -1;
//printf("Host domain : %s\n", h->h_name);
//printf("IP Address : %s\n", inet_ntoa(*((struct in_addr *)h->h_addr)));
memcpy(&Self.m_sin.sin6_addr,h->h_addr,sizeof(struct in6_addr));
}
return 0;
}
int btTracker::Connect()
{
ssize_t r;
time(&m_last_timestamp);
if(_s2sin(m_host,m_port,&m_sin) < 0) {
fprintf(stderr,"warn, get tracker's ip address failed.");
return -1;
}
m_sock = socket(AF_INET6,SOCK_STREAM,0);
if(INVALID_SOCKET == m_sock) return -1;
if(setfd_nonblock(m_sock) < 0) {CLOSE_SOCKET(m_sock); return -1; }
r = connect_nonb(m_sock,(struct sockaddr*)&m_sin);
if( r == -1 ){ CLOSE_SOCKET(m_sock); return -1;}
else if( r == -2 ) m_status = T_CONNECTING;
else{
if( 0 == SendRequest()) m_status = T_READY;
else{ CLOSE_SOCKET(m_sock); return -1;}
}
return 0;
}
int btTracker::SendRequest()
{
char *event,*str_event[] = {"started","stopped","completed" };
char REQ_BUFFER[MAXPATHLEN];
socklen_t addrlen;
struct sockaddr_in6 addr;
addrlen = sizeof(struct sockaddr_in6);
/* get local ip address */
if(getsockname(m_sock,(struct sockaddr*)&addr,&addrlen) < 0){ return -1;}
//jc Self.SetIp(addr);
// fprintf(stdout,"Old Set Self:");
// fprintf(stdout,"%s\n", inet_ntoa(Self.m_sin.sin_addr));
if( m_f_stoped ) /* stopped */
event = str_event[1];
else if( BTCONTENT.pBF->IsFull()) /* download complete */
event = str_event[2];
else if( m_f_started ) /* interval */
event = (char*) 0;
else
event = str_event[0]; /* started */
if(event){
if(MAXPATHLEN < snprintf(REQ_BUFFER,MAXPATHLEN,REQ_URL_P2_FMT,
m_path,
(size_t)Self.TotalUL(),
(size_t)Self.TotalDL(),
(size_t)BTCONTENT.GetLeftBytes(),
event)){
return -1;
}
}else{
if(MAXPATHLEN < snprintf(REQ_BUFFER,MAXPATHLEN,REQ_URL_P3_FMT,
m_path,
(size_t)Self.TotalUL(),
(size_t)Self.TotalDL(),
(size_t)BTCONTENT.GetLeftBytes()
)){
return -1;
}
}
if(_IPsin(m_host, m_port, &addr) < 0){
char REQ_HOST[MAXHOSTNAMELEN];
if(MAXHOSTNAMELEN < snprintf(REQ_HOST,MAXHOSTNAMELEN,"\r\nHost: %s",m_host)) return -1;
strcat(REQ_BUFFER, REQ_HOST);
}
strcat(REQ_BUFFER,"\r\n\r\n");
// hc
//fprintf(stderr,"SendRequest: %s\n", REQ_BUFFER);
if( 0 != m_reponse_buffer.PutFlush(m_sock,REQ_BUFFER,strlen((char*)REQ_BUFFER))){
fprintf(stderr,"warn, send request to tracker failed. %s\n",strerror(errno));
return -1;
}
return 0;
}
int btTracker::IntervalCheck(const time_t *pnow, fd_set *rfdp, fd_set *wfdp)
{
/* tracker communication */
if( T_FREE == m_status ){
if((*pnow - m_last_timestamp >= m_interval) &&
(cfg_min_peers > WORLD.TotalPeers())){
if(Connect() < 0){ Reset(15); return -1; }
if( m_status == T_CONNECTING ){
FD_SET(m_sock, rfdp);
FD_SET(m_sock, wfdp);
}else{
FD_SET(m_sock, rfdp);
}
}
}else{
if( m_status == T_CONNECTING ){
FD_SET(m_sock, rfdp);
FD_SET(m_sock, wfdp);
}else{
FD_SET(m_sock, rfdp);
}
}
return m_sock;
}
int btTracker::SocketReady(fd_set *rfdp, fd_set *wfdp, int *nfds)
{
if( T_FREE == m_status ) return 0;
if( T_CONNECTING == m_status &&
(FD_ISSET(m_sock, wfdp) || FD_ISSET(m_sock,wfdp)) ){
int error = 0;
socklen_t n = sizeof(error);
(*nfds)--;
FD_CLR(m_sock, wfdp);
if(getsockopt(m_sock, SOL_SOCKET,SO_ERROR,&error,&n) < 0 ||
error != 0 ){
if( ECONNREFUSED != error )
fprintf(stderr,"warn, connect to tracker failed. %s\n",strerror(error));
else
m_connect_refuse_click++;
Reset(15);
return -1;
}else{
if( SendRequest() == 0 ) m_status = T_READY;
else { Reset(15); return -1; }
}
}else if(FD_ISSET(m_sock, rfdp) ){
(*nfds)--;
FD_CLR(m_sock,rfdp);
CheckReponse();
}
return 0;
}