#include "tracker.h" #ifndef WINDOWS #include #include #include #include #endif #include #include #include #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; }