Example #1
0
/* UDP implementation according to http://xbtt.sourceforge.net/udp_tracker_protocol.html */
int handle_udp6( int64 serversocket, struct ot_workstruct *ws ) {
  ot_ip6      remoteip;
  uint32_t   *inpacket = (uint32_t*)ws->inbuf;
  uint32_t   *outpacket = (uint32_t*)ws->outbuf;
  uint32_t    numwant, left, event, scopeid;
  uint32_t    connid[2];
  uint16_t    port, remoteport;
  size_t      byte_count, scrape_count;

  byte_count = socket_recv6( serversocket, ws->inbuf, G_INBUF_SIZE, remoteip, &remoteport, &scopeid );
  if( !byte_count ) return 0;

  stats_issue_event( EVENT_ACCEPT, FLAG_UDP, (uintptr_t)remoteip );
  stats_issue_event( EVENT_READ, FLAG_UDP, byte_count );

  /* Minimum udp tracker packet size, also catches error */
  if( byte_count < 16 )
    return 1;

  /* Generate the connection id we give out and expect to and from
     the requesting ip address, this prevents udp spoofing */
  udp_make_connectionid( connid, remoteip, 0 );

  /* Initialise hash pointer */
  ws->hash = NULL;
  ws->peer_id = NULL;

  /* If action is not a ntohl(a) == a == 0, then we
     expect the derived connection id in first 64 bit */
  if( inpacket[2] && ( inpacket[0] != connid[0] || inpacket[1] != connid[1] ) ) {
    /* If connection id does not match, try the one that was
       valid in the previous hour. Only if this also does not
       match, return an error packet */
    udp_make_connectionid( connid, remoteip, 1 );
    if( inpacket[0] != connid[0] || inpacket[1] != connid[1] ) {
      const size_t s = sizeof( "Connection ID missmatch." );
      outpacket[0] = 3; outpacket[1] = inpacket[3];
      memcpy( &outpacket[2], "Connection ID missmatch.", s );
      socket_send6( serversocket, ws->outbuf, 8 + s, remoteip, remoteport, 0 );
      stats_issue_event( EVENT_CONNID_MISSMATCH, FLAG_UDP, 8 + s );
      return 1;
    }
  }

  switch( ntohl( inpacket[2] ) ) {
    case 0: /* This is a connect action */
      /* look for udp bittorrent magic id */
      if( (ntohl(inpacket[0]) != 0x00000417) || (ntohl(inpacket[1]) != 0x27101980) )
        return 1;

      outpacket[0] = 0;
      outpacket[1] = inpacket[3];
      outpacket[2] = connid[0];
      outpacket[3] = connid[1];

      socket_send6( serversocket, ws->outbuf, 16, remoteip, remoteport, 0 );
      stats_issue_event( EVENT_CONNECT, FLAG_UDP, 16 );
      break;
    case 1: /* This is an announce action */
      /* Minimum udp announce packet size */
      if( byte_count < 98 )
        return 1;

      /* We do only want to know, if it is zero */
      left  = inpacket[64/4] | inpacket[68/4];

      numwant = ntohl( inpacket[92/4] );
      if (numwant > 200) numwant = 200;

      event    = ntohl( inpacket[80/4] );
      port     = *(uint16_t*)( ((char*)inpacket) + 96 );
      ws->hash = (ot_hash*)( ((char*)inpacket) + 16 );

      OT_SETIP( &ws->peer, remoteip );
      OT_SETPORT( &ws->peer, &port );
      OT_PEERFLAG( &ws->peer ) = 0;

      switch( event ) {
        case 1: OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_COMPLETED; break;
        case 3: OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_STOPPED; break;
        default: break;
      }

      if( !left )
        OT_PEERFLAG( &ws->peer )         |= PEER_FLAG_SEEDING;

      outpacket[0] = htonl( 1 );    /* announce action */
      outpacket[1] = inpacket[12/4];

      if( OT_PEERFLAG( &ws->peer ) & PEER_FLAG_STOPPED ) { /* Peer is gone. */
        ws->reply      = ws->outbuf;
        ws->reply_size = remove_peer_from_torrent( FLAG_UDP, ws );
      } else {
        ws->reply      = ws->outbuf + 8;
        ws->reply_size = 8 + add_peer_to_torrent_and_return_peers( FLAG_UDP, ws, numwant );
      }

      socket_send6( serversocket, ws->outbuf, ws->reply_size, remoteip, remoteport, 0 );
      stats_issue_event( EVENT_ANNOUNCE, FLAG_UDP, ws->reply_size );
      break;

    case 2: /* This is a scrape action */
      outpacket[0] = htonl( 2 );    /* scrape action */
      outpacket[1] = inpacket[12/4];

      for( scrape_count = 0; ( scrape_count * 20 < byte_count - 16) && ( scrape_count <= 74 ); scrape_count++ )
        return_udp_scrape_for_torrent( *(ot_hash*)( ((char*)inpacket) + 16 + 20 * scrape_count ), ((char*)outpacket) + 8 + 12 * scrape_count );

      socket_send6( serversocket, ws->outbuf, 8 + 12 * scrape_count, remoteip, remoteport, 0 );
      stats_issue_event( EVENT_SCRAPE, FLAG_UDP, scrape_count );
      break;
  }
  return 1;
}
Example #2
0
static ssize_t http_handle_announce( const int64 sock, struct ot_workstruct *ws, char *read_ptr ) {
  int               numwant, tmp, scanon;
  unsigned short    port = 0;
  char             *write_ptr;
  ssize_t           len;
  struct http_data *cookie = io_getcookie( sock );

  /* This is to hack around stupid clients that send "announce ?info_hash" */
  if( read_ptr[-1] != '?' ) {
    while( ( *read_ptr != '?' ) && ( *read_ptr != '\n' ) ) ++read_ptr;
    if( *read_ptr == '\n' ) HTTPERROR_400_PARAM;
    ++read_ptr;
  }

#ifdef WANT_IP_FROM_PROXY
  if( accesslist_isblessed( cookie->ip, OT_PERMISSION_MAY_PROXY ) ) {
    ot_ip6 proxied_ip;
    char *fwd = http_header( ws->request, ws->header_size, "x-forwarded-for" );
    if( fwd && scan_ip6( fwd, proxied_ip ) )
      OT_SETIP( &ws->peer, proxied_ip );
    else
      OT_SETIP( &ws->peer, cookie->ip );
  } else
#endif
  OT_SETIP( &ws->peer, cookie->ip );

  ws->peer_id = NULL;
  ws->hash = NULL;

  OT_SETPORT( &ws->peer, &port );
  OT_PEERFLAG( &ws->peer ) = 0;
  numwant = 50;
  scanon = 1;

  while( scanon ) {
    switch( scan_find_keywords(keywords_announce, &read_ptr, SCAN_SEARCHPATH_PARAM ) ) {
    case -2: scanon = 0; break;   /* TERMINATOR */
    case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */
    case -3: scan_urlencoded_skipvalue( &read_ptr ); break;
    case 1: /* matched "port" */
      len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE );
      if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &tmp ) || ( tmp > 0xffff ) ) HTTPERROR_400_PARAM;
      port = htons( tmp ); OT_SETPORT( &ws->peer, &port );
      break;
    case 2: /* matched "left" */
      if( ( len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM;
      if( scan_fixed_int( write_ptr, len, &tmp ) ) tmp = 0;
      if( !tmp ) OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_SEEDING;
      break;
    case 3: /* matched "event" */
      switch( scan_find_keywords( keywords_announce_event, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) {
        case -1: HTTPERROR_400_PARAM;
        case  1: /* matched "completed" */
          OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_COMPLETED;
          break;
        case  2: /* matched "stopped" */
          OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_STOPPED;
          break;
        default:
          break;
      }
      break;
    case 4: /* matched "numwant" */
      len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE );
      if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &numwant ) ) HTTPERROR_400_PARAM;
      if( numwant < 0 ) numwant = 50;
      if( numwant > 200 ) numwant = 200;
      break;
    case 5: /* matched "compact" */
      len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE );
      if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &tmp ) ) HTTPERROR_400_PARAM;
      if( !tmp ) HTTPERROR_400_COMPACT;
      break;
    case 6: /* matched "info_hash" */
      if( ws->hash ) HTTPERROR_400_DOUBLEHASH;
      /* ignore this, when we have less than 20 bytes */
      if( scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM;
        ws->hash = (ot_hash*)write_ptr;
      break;
#ifdef WANT_IP_FROM_QUERY_STRING
    case  7: /* matched "ip" */
      {
        char *tmp_buf1 = ws->reply, *tmp_buf2 = ws->reply+16;
        len = scan_urlencoded_query( &read_ptr, tmp_buf2, SCAN_SEARCHPATH_VALUE );
        tmp_buf2[len] = 0;
        if( ( len <= 0 ) || !scan_ip6( tmp_buf2, tmp_buf1 ) ) HTTPERROR_400_PARAM;
        OT_SETIP( &ws->peer, tmp_buf1 );
      }
      break;
#endif
#ifdef WANT_FULLLOG_NETWORKS
      case 8: /* matched "lognet" */
      {
        //if( accesslist_isblessed( cookie->ip, OT_PERMISSION_MAY_STAT ) ) {
          char *tmp_buf = ws->reply;
          ot_net net;
          signed short parsed, bits;

          len = scan_urlencoded_query( &read_ptr, tmp_buf, SCAN_SEARCHPATH_VALUE );
          tmp_buf[len] = 0;
          if( len <= 0 ) HTTPERROR_400_PARAM;
          if( *tmp_buf == '-' ) {
            loglist_reset( );
            return ws->reply_size = sprintf( ws->reply, "Successfully removed.\n" );
          }
          parsed = scan_ip6( tmp_buf, net.address );
          if( !parsed ) HTTPERROR_400_PARAM;
          if( tmp_buf[parsed++] != '/' )
            bits = 128;
          else {
            parsed = scan_short( tmp_buf + parsed, &bits );
            if( !parsed ) HTTPERROR_400_PARAM; 
            if( ip6_isv4mapped( net.address ) )
              bits += 96;
          }
          net.bits = bits;
          loglist_add_network( &net );
          return ws->reply_size = sprintf( ws->reply, "Successfully added.\n" );
        //}
      }
#endif
        break;
      case 9: /* matched "peer_id" */
        /* ignore this, when we have less than 20 bytes */
        if( scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM;
        ws->peer_id = write_ptr;
        break;
    }
  }

#ifdef WANT_LOG_NUMWANT
  numwants[numwant]++;
#endif

  /* XXX DEBUG
  stats_issue_event( EVENT_ACCEPT, FLAG_TCP, (uintptr_t)ws->reply );
  */

  /* Scanned whole query string */
  if( !ws->hash )
    return ws->reply_size = sprintf( ws->reply, "d14:failure reason80:Your client forgot to send your torrent's info_hash. Please upgrade your client.e" );

  if( OT_PEERFLAG( &ws->peer ) & PEER_FLAG_STOPPED )
    ws->reply_size = remove_peer_from_torrent( FLAG_TCP, ws );
  else
    ws->reply_size = add_peer_to_torrent_and_return_peers( FLAG_TCP, ws, numwant );

  stats_issue_event( EVENT_ANNOUNCE, FLAG_TCP, ws->reply_size);
  return ws->reply_size;
}