示例#1
0
static ssize_t handle_read( const int64 clientsocket ) {
  struct http_data* h = io_getcookie( clientsocket );
  ssize_t l;

  if( ( l = io_tryread( clientsocket, static_inbuf, sizeof static_inbuf ) ) <= 0 ) {
    handle_dead( clientsocket );
    return 0;
  }

  /* If we get the whole request in one packet, handle it without copying */
  if( !array_start( &h->request ) ) {
    if( memchr( static_inbuf, '\n', l ) )
      return http_handle_request( clientsocket, static_inbuf, l );
    h->flag |= STRUCT_HTTP_FLAG_ARRAY_USED;
    array_catb( &h->request, static_inbuf, l );
    return 0;
  }

  h->flag |= STRUCT_HTTP_FLAG_ARRAY_USED;
  array_catb( &h->request, static_inbuf, l );

  if( array_failed( &h->request ) )
    return http_issue_error( clientsocket, CODE_HTTPERROR_500 );

  if( ( array_bytes( &h->request ) > 8192 ) && !accesslist_isblessed( (char*)&h->ip, OT_PERMISSION_MAY_SYNC ) )
     return http_issue_error( clientsocket, CODE_HTTPERROR_500 );

  if( memchr( array_start( &h->request ), '\n', array_bytes( &h->request ) ) )
    return http_handle_request( clientsocket, array_start( &h->request ), array_bytes( &h->request ) );

  return 0;
}
示例#2
0
static void server_mainloop() {
  int64 sock;

  /* inlined livesync_init() */
  memset( g_peerbuffer_start, 0, sizeof( g_peerbuffer_start ) ); 
  g_peerbuffer_pos = g_peerbuffer_start;
  memcpy( g_peerbuffer_pos, &g_tracker_id, sizeof( g_tracker_id ) );
  uint32_pack_big( (char*)g_peerbuffer_pos + sizeof( g_tracker_id ), OT_SYNC_PEER);
  g_peerbuffer_pos += sizeof( g_tracker_id ) + sizeof( uint32_t);
  g_next_packet_time = time(NULL) + LIVESYNC_MAXDELAY;

  while(1) {
    /* See, if we need to connect to anyone */
    if( time(NULL) > g_connection_reconn )
      handle_reconnects( );

    /* Wait for io events until next approx reconn check time */
    io_waituntil2( 30*1000 );

    /* Loop over readable sockets */
    while( ( sock = io_canread( ) ) != -1 ) {
      const void *cookie = io_getcookie( sock );
      if( (uintptr_t)cookie == FLAG_SERVERSOCKET )
        handle_accept( sock );
      else
        handle_read( sock );
    }

    /* Loop over writable sockets */
    while( ( sock = io_canwrite( ) ) != -1 )
      handle_write( sock );

    livesync_ticker( );
  }
}
示例#3
0
ssize_t http_sendiovecdata( const int64 sock, struct ot_workstruct *ws, int iovec_entries, struct iovec *iovector ) {
  struct http_data *cookie = io_getcookie( sock );
  char *header;
  int i;
  size_t header_size, size = iovec_length( &iovec_entries, &iovector );
  tai6464 t;

  /* No cookie? Bad socket. Leave. */
  if( !cookie ) {
    iovec_free( &iovec_entries, &iovector );
    HTTPERROR_500;
  }

  /* If this socket collected request in a buffer, free it now */
  array_reset( &cookie->request );

  /* If we came here, wait for the answer is over */
  cookie->flag &= ~STRUCT_HTTP_FLAG_WAITINGFORTASK;

  /* Our answers never are 0 vectors. Return an error. */
  if( !iovec_entries ) {
    HTTPERROR_500;
  }

  /* Prepare space for http header */
  header = malloc( SUCCESS_HTTP_HEADER_LENGTH + SUCCESS_HTTP_HEADER_LENGTH_CONTENT_ENCODING );
  if( !header ) {
    iovec_free( &iovec_entries, &iovector );
    HTTPERROR_500;
  }

  if( cookie->flag & STRUCT_HTTP_FLAG_GZIP )
    header_size = sprintf( header, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Encoding: gzip\r\nContent-Length: %zd\r\n\r\n", size );
  else if( cookie->flag & STRUCT_HTTP_FLAG_BZIP2 )
    header_size = sprintf( header, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Encoding: bzip2\r\nContent-Length: %zd\r\n\r\n", size );
  else
    header_size = sprintf( header, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r\n", size );

  iob_reset( &cookie->batch );
  iob_addbuf_free( &cookie->batch, header, header_size );

  /* Will move to ot_iovec.c */
  for( i=0; i<iovec_entries; ++i )
    iob_addbuf_munmap( &cookie->batch, iovector[i].iov_base, iovector[i].iov_len );
  free( iovector );

  /* writeable sockets timeout after 10 minutes */
  taia_now( &t ); taia_addsec( &t, &t, OT_CLIENT_TIMEOUT_SEND );
  io_timeout( sock, t );
  io_dontwantread( sock );
  io_wantwrite( sock );
  return 0;
}
示例#4
0
static void handle_dead( const int64 socket ) {
  struct http_data* h=io_getcookie( socket );
  if( h ) {
    if( h->flag & STRUCT_HTTP_FLAG_IOB_USED )
      iob_reset( &h->batch );
    if( h->flag & STRUCT_HTTP_FLAG_ARRAY_USED )
      array_reset( &h->request );
    if( h->flag & STRUCT_HTTP_FLAG_WAITINGFORTASK )
      mutex_workqueue_canceltask( socket );
    free( h );
  }
  io_close( socket );
}
示例#5
0
/* Can write new sync data to the stream */
static void handle_write( int64 peersocket ) {
  proxy_peer *peer = io_getcookie( peersocket );

  if( !peer ) {
    /* Can't happen ;) */
    io_close( peersocket );
    return;
  }

  switch( peer->state & FLAG_MASK ) {
  case FLAG_DISCONNECTED:
  default: /* Should not happen */
    io_close( peersocket );
    break;
  case FLAG_CONNECTING:
    /* Ensure that the connection is established and handle connection error */
    if( peer->state & FLAG_OUTGOING && !socket_connected( peersocket ) ) {
        fprintf( stderr, "failed\n" );
        reset_info_block( peer );
        io_close( peersocket );
        break;
    }

    if( io_trywrite( peersocket, (void*)&g_tracker_id, sizeof( g_tracker_id ) ) == sizeof( g_tracker_id ) ) {
      PROXYPEER_SETWAITTRACKERID( peer->state );
      io_dontwantwrite( peersocket );
      io_wantread( peersocket );
    } else {
      fprintf( stderr, "Handshake incomplete, closing socket\n" );
      io_close( peersocket );
      reset_info_block( peer );
    }
    break;
  case FLAG_CONNECTED:
    switch( iob_send( peersocket, &peer->outdata ) ) {
    case 0: /* all data sent */
      io_dontwantwrite( peersocket );
      break;
    case -3: /* an error occured */
      io_close( peersocket );
      reset_info_block( peer );
      break;
    default: /* Normal operation or eagain */
      break;
    }
    break;
  }

  return;
}
示例#6
0
static ssize_t http_handle_fullscrape( const int64 sock, struct ot_workstruct *ws ) {
  struct http_data* cookie = io_getcookie( sock );
  int format = 0;
  tai6464 t;

#ifdef WANT_MODEST_FULLSCRAPES
  {
    ot_scrape_log this_peer, *new_peer;
    int exactmatch;
    memcpy( this_peer.ip, cookie->ip, sizeof(ot_ip6));
    this_peer.last_fullscrape = g_now_seconds;
    pthread_mutex_lock(&g_modest_fullscrape_mutex);
    new_peer = vector_find_or_insert( &g_modest_fullscrape_timeouts, &this_peer, sizeof(ot_scrape_log), sizeof(ot_ip6), &exactmatch );
    if( !new_peer ) {
      pthread_mutex_unlock(&g_modest_fullscrape_mutex);
      HTTPERROR_500;
    }
    if( exactmatch && ( this_peer.last_fullscrape - new_peer->last_fullscrape ) < OT_MODEST_PEER_TIMEOUT ) {
      pthread_mutex_unlock(&g_modest_fullscrape_mutex);
      HTTPERROR_402_NOTMODEST;
    }
    memcpy( new_peer, &this_peer, sizeof(ot_scrape_log));
    pthread_mutex_unlock(&g_modest_fullscrape_mutex);
  }
#endif

#ifdef WANT_COMPRESSION_GZIP
  ws->request[ws->request_size-1] = 0;
  if( strstr( ws->request, "gzip" ) ) {
    cookie->flag |= STRUCT_HTTP_FLAG_GZIP;
    format = TASK_FLAG_GZIP;
    stats_issue_event( EVENT_FULLSCRAPE_REQUEST_GZIP, 0, (uintptr_t)cookie->ip );
  } else
#endif
    stats_issue_event( EVENT_FULLSCRAPE_REQUEST, 0, (uintptr_t)cookie->ip );

#ifdef _DEBUG_HTTPERROR
  fprintf( stderr, "%s", ws->debugbuf );
#endif

  /* Pass this task to the worker thread */
  cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK;
  /* Clients waiting for us should not easily timeout */
  taia_uint( &t, 0 ); io_timeout( sock, t );
  fullscrape_deliver( sock, TASK_FULLSCRAPE | format );
  io_dontwantread( sock );
  return ws->reply_size = -2;
}
示例#7
0
static void http_senddata( const int64 sock, struct ot_workstruct *ws ) {
  struct http_data *cookie = io_getcookie( sock );
  ssize_t written_size;

  if( !cookie ) { io_close(sock); return; }

  /* whoever sends data is not interested in its input-array */
  if( ws->keep_alive && ws->header_size != ws->request_size ) {
    size_t rest = ws->request_size - ws->header_size;
    if( array_start(&cookie->request) ) {
      memmove( array_start(&cookie->request), ws->request + ws->header_size, rest );
      array_truncate( &cookie->request, 1, rest );
    } else
      array_catb(&cookie->request, ws->request + ws->header_size, rest );    
  } else
    array_reset( &cookie->request );

  written_size = write( sock, ws->reply, ws->reply_size );
  if( ( written_size < 0 ) || ( ( written_size == ws->reply_size ) && !ws->keep_alive ) ) {
    array_reset( &cookie->request );
    free( cookie ); io_close( sock ); return;
  }

  if( written_size < ws->reply_size ) {
    char * outbuf;
    tai6464 t;

    if( !( outbuf = malloc( ws->reply_size - written_size ) ) ) {
      array_reset( &cookie->request );
      free(cookie); io_close( sock );
      return;
    }

    memcpy( outbuf, ws->reply + written_size, ws->reply_size - written_size );
    iob_addbuf_free( &cookie->batch, outbuf, ws->reply_size - written_size );

    /* writeable short data sockets just have a tcp timeout */
    if( !ws->keep_alive ) {
      taia_uint( &t, 0 ); io_timeout( sock, t );
      io_dontwantread( sock );
    }
    io_wantwrite( sock );
  }
}
示例#8
0
static void server_mainloop( ) {
  static time_t ot_last_clean_time;
  time_t        next_timeout_check = g_now + OT_CLIENT_TIMEOUT_CHECKINTERVAL;
  struct        iovec *iovector;
  int           iovec_entries;

  for( ; ; ) {
    int64 i;

    io_wait();

    while( ( i = io_canread( ) ) != -1 ) {
      const void *cookie = io_getcookie( i );
      if( cookie == FLAG_TCP )
        handle_accept( i );
      else if( cookie == FLAG_UDP )
        handle_udp4( i );
      else
        handle_read( i );
    }

    while( ( i = mutex_workqueue_popresult( &iovec_entries, &iovector ) ) != -1 )
      http_sendiovecdata( i, iovec_entries, iovector );

    while( ( i = io_canwrite( ) ) != -1 )
      handle_write( i );

    if( g_now > next_timeout_check ) {
      while( ( i = io_timeouted() ) != -1 )
        handle_dead( i );
      next_timeout_check = g_now + OT_CLIENT_TIMEOUT_CHECKINTERVAL;
    }

    /* See if we need to move our pools */
    if( NOW != ot_last_clean_time ) {
      ot_last_clean_time = NOW;
      clean_all_torrents();
    }

    /* Enforce setting the clock */
    signal_handler( SIGALRM );
  }
}
示例#9
0
static void handle_write( const int64 clientsocket ) {
  struct http_data* h=io_getcookie( clientsocket );
  if( !h || ( iob_send( clientsocket, &h->batch ) <= 0 ) )
    handle_dead( clientsocket );
}
示例#10
0
int main(int argc,char* argv[]) {
  char server[1024];
  int* fds;
  int* avail;
  int* keepleft;
  long long* expected;
  unsigned long n=10000;	/* requests */
  unsigned long c=10;		/* concurrency */
  unsigned long t=0;		/* time limit in seconds */
  unsigned long k=0;		/* keep-alive */
  unsigned long K=1;		/* keep-alive counter */
  int report=0;
  unsigned long long errors=0;
  unsigned long long bytes=0;
  int v=0;
  unsigned long i,done;
  uint16 port=80;
  uint32 scope_id=0;
  stralloc ips={0};
  char* request,* krequest;
  unsigned long rlen, krlen;
  tai6464 first,now,next,last;
  enum { SAME, REPLAY } mode;
  char* hostname;

  server[0]=0;

  errmsg_iam("bench");
#ifndef __MINGW32__
  signal(SIGPIPE,SIG_IGN);
#endif

  for (;;) {
    int i;
    int ch=getopt(argc,argv,"n:c:t:kvK:C:r");
    if (ch==-1) break;
    switch (ch) {
    case 'r':
      report=1;
      break;
    case 'n':
      i=scan_ulong(optarg,&n);
      if (i==0) die(1,"could not parse -n argument \"",optarg,"\".\n");
      break;
    case 'c':
      i=scan_ulong(optarg,&c);
      if (i==0) die(1,"could not parse -c argument \"",optarg,"\".\n");
      break;
    case 't':
      i=scan_ulong(optarg,&t);
      if (i==0) die(1,"could not parse -t argument \"",optarg,"\".\n");
      break;
    case 'k':
      k=1;
      break;
    case 'K':
      i=scan_ulong(optarg,&K);
      break;
    case 'v':
      v=1;
      break;
    case 'C':
      cookiefile(optarg);
      break;
    case '?':
      break;
    default:
      usage();
    }
  }
  if (n<1 || c<1 || !argv[optind]) usage();

  if (argv[optind][0]=='@') {
    mode=REPLAY;
    char* host;
    n=(unsigned long)-1;
    host=argv[optind]+1;
    {
      int tmp;
      tmp=str_chr(host,'/');
      if (host[tmp]) {
	host[tmp]=0;
	if (!scan_ushort(host+tmp+1,&port)) usage();
      }
      tmp=str_chr(host,'%');
      if (host[tmp]) {
	host[tmp]=0;
	scope_id=socket_getifidx(host+tmp+1);
	if (scope_id==0)
	  carp("warning: network interface \"",host+tmp+1,"\" not found.");
      }
    }

    {
      stralloc a={0};
      stralloc_copys(&a,host);
      if (dns_ip6(&ips,&a)==-1)
	die(1,"could not find IP for \"",host,"\"!");
    }
    hostname=host;
    request=krequest=0;
    rlen=krlen=0;
  } else {
    char* host=argv[optind];
    int colon;
    int slash;
    char* c;
    mode=SAME;
    if (byte_equal(host,7,"http://")) host+=7;
    colon=str_chr(host,':');
    slash=str_chr(host,'/');
    if (host[0]=='[') {	/* ipv6 IP notation */
      int tmp;
      ++host;
      --colon; --slash;
      tmp=str_chr(host,']');
      if (host[tmp]==']') host[tmp]=0;
      if (host[tmp+1]==':') colon=tmp+1;
      if (colon<tmp+1) colon=tmp+1+str_len(host+tmp+1);
    }
    if (colon<slash) {
      host[colon]=0;
      c=host+colon+1;
      if (c[scan_ushort(c,&port)]!='/') usage();
      *c=0;
    }
    host[colon]=0;
    c=host+slash;
    *c=0;
    {
      char* tmp=alloca(str_len(host)+1);
      tmp[fmt_str(tmp,host)]=0;
      host=tmp;
    }
    *c='/';
    {
      int tmp=str_chr(host,'%');
      if (host[tmp]) {
	host[tmp]=0;
	scope_id=socket_getifidx(host+tmp+1);
	if (scope_id==0)
	  carp("warning: network interface \"",host+tmp+1,"\" not found.");
      }
    }

    {
      stralloc a={0};
      stralloc_copys(&a,host);
      if (dns_ip6(&ips,&a)==-1)
	die(1,"could not find IP for \"",host,"\"!");
    }

    request=alloca(1300+str_len(host)+3*str_len(c));
    krequest=alloca(1300+str_len(host)+3*str_len(c));
    {
      int i,j;
      i=fmt_str(request,"GET ");
      i+=fmt_urlencoded(request+i,c,str_len(c));
      i+=fmt_str(request+i," HTTP/1.0\r\nHost: ");
      i+=fmt_str(request+i,host);
      i+=fmt_str(request+i,":");
      i+=fmt_ulong(request+i,port);
      i+=fmt_str(request+i,"\r\nUser-Agent: bench/1.0\r\nConnection: ");
      j=i;
      i+=fmt_str(request+i,"close\r\n\r\n");
      rlen=i; request[rlen]=0;
      byte_copy(krequest,rlen,request);
      i=j+fmt_str(krequest+j,"keep-alive\r\n\r\n");
      krlen=i; krequest[krlen]=0;
    }

    hostname=host;

  }

  fds=alloca(c*sizeof(*fds));
  avail=alloca(c*sizeof(*avail));
  expected=alloca(c*sizeof(*expected));
  keepleft=alloca(c*sizeof(*keepleft));
  last.sec.x=23;
  if (!k) K=1;
  for (i=0; i<c; ++i) { fds[i]=-1; avail[i]=1; keepleft[i]=K; }

  taia_now(&first);

  for (done=0; done<n; ) {

    if (t) {
      /* calculate timeout */
      taia_now(&now);
      if (now.sec.x != last.sec.x) {
	byte_copy(&last,sizeof(now),&now);
	byte_copy(&next,sizeof(now),&now);
	next.sec.x += t;
	while ((i=io_timeouted())!=-1) {
	  unsigned long j;
	  char numbuf[FMT_ULONG];
	  numbuf[fmt_ulong(numbuf,i)]=0;
	  carp("timeout on fd ",numbuf,"!");
	  j=(unsigned long)io_getcookie(i);
	  io_close(i);
	  avail[j]=1;
	  fds[j]=-1;
	}
      }
    }

    /* first, fill available connections */
    for (i=0; i<c; ++i)
      if (avail[i]==1 && fds[i]==-1) {
	fds[i]=make_connection(ips.s,port,scope_id,-1);
	if (fds[i]==-1) diesys(1,"socket/connect");
	avail[i]=2;
	if (io_fd(fds[i])==0) diesys(1,"io_fd");
	io_setcookie(fds[i],(void*)i);
//	io_wantread(fds[i]);
	io_wantwrite(fds[i]);
      }

    if (t)
      io_waituntil(next);
    else
      io_wait();

    /* second, see if we can write on a connection */
    while ((i=io_canwrite())!=-1) {
      int j;
      j=(unsigned long)io_getcookie(i);
      if (avail[j]==2) {
	if (make_connection(ips.s,port,scope_id,i)==-1) {
	  ++errors;
	  if (v) write(1,"!",1);
	  io_close(i);
	  avail[j]=1;
	  fds[j]=-1;
	  continue;
	}
      }
      {
	char* towrite;
	int writelen;
	if (mode==REPLAY) {
	  static long lines;
	  char line[1024];
	  char req[2048];
	  int len;
	  int i;
	  char* c;
	  char* host;
	  int hlen;
	  if ((len=buffer_getline(buffer_0,line,sizeof(line)))) {
	    ++lines;
	    if (line[len]!='\n')
	      die(0,"line too long: ",line);
	    line[len]=0;
	    c=line;
	    if (str_start(line,"http://")) c+=7;
	    if (c[0]=='/') {
	      host=hostname;
	      hlen=strlen(hostname);
	    } else {
	      host=c;
	      c+=(hlen=str_chr(c,'/'));
	    }
	    if (!*c)
	      c="/";

	    i=fmt_str(req,"GET ");
	    i+=fmt_urlencoded(req+i,c,str_len(c));
	    i+=fmt_str(req+i," HTTP/1.0\r\nHost: ");
	    byte_copy(req+i,hlen,host); i+=hlen;
	    i+=fmt_str(req+i,":");
	    i+=fmt_ulong(req+i,port);
	    if (cookies) {
	      int j;
	      i+=fmt_str(req+i,"\r\n");
	      j=nextcookie(req+i,sizeof(req)-i-100);
	      if (j!=-1) i+=j; else i-=2;
	    }
	    i+=fmt_str(req+i,"\r\nUser-Agent: bench/1.0\r\nConnection: ");
	    i+=fmt_str(req+i,keepleft[j]>1?"keep-alive\r\n\r\n":"close\r\n\r\n");
	    req[i]=0;
	    towrite=req;
	    writelen=i;
	  } else {
	    n=done;
	    break;
	  }
	} else {
	  if (keepleft[j]>1) {
	    towrite=krequest;
	    writelen=krlen;
	  } else {
	    towrite=request;
	    writelen=rlen;
	  }
	  if (cookies) {
	    int i=writelen-2;
	    int j=nextcookie(towrite+i,900);
	    if (j!=-1) i+=j;
	    i+=fmt_str(towrite+i,"\r\n\r\n");
	    writelen=i;
	  }
	}
	if (io_trywrite(i,towrite,writelen)!=writelen) {
	  ++errors;
	  if (v) write(1,"-",1);
	  io_close(i);
	  avail[j]=1;
	  fds[j]=-1;
	  continue;
	}
      }
      io_dontwantwrite(i);
      io_wantread(i);
      expected[j]=-1;
      if (v) write(1,"+",1);
    }

    /* third, see if we got served */
    while ((i=io_canread())!=-1) {
      char buf[8193];
      int l,j;
      buf[8192]=0;
      j=(unsigned long)io_getcookie(i);
      if ((l=io_tryread(i,buf,sizeof(buf)-1))<=0) {
	if (l==0) { /* EOF.  Mhh. */
	  if (expected[j]>0) {
	    ++errors;
	    if (v) write(1,"-",1);	/* so whine a little */
	  }
	  if (expected[j]==-2)
	    ++done;
	  io_close(i);
	  avail[j]=1;
	  fds[j]=-1;
	} else if (l==-3) {
	  ++errors;
	  if (v) write(1,"!",1);
//	  carpsys("read");
	}
      } else {
	bytes+=l;
	if (v) write(1,".",1);
	/* read something */
	if (expected[j]==-1) {	/* expecting header */
	  int k;
	  /* OK, so this is a very simplistic header parser.  No
	   * buffering.  At all.  We expect the Content-Length header to
	   * come in one piece. */
	  if (l>10 && !memcmp(buf,"HTTP/1.",7)) {
	    if (buf[9]>='0' && buf[9]<='9')
	      r[buf[9]-'0']++;
	    else {
	      write(1,buf,15); write(1,"\n",1);
	    }
	  }
	  expected[j]=-2;
	  if (!done) {
	    for (k=0; k<l; ++k)
	      if (str_start(buf+k,"\nServer: ")) {
		char* tmp=buf+(k+=9);
		for (; k<l; ++k)
		  if (buf[k]=='\r') break;
		k=buf+k-tmp;
		if (k>sizeof(server)-1) k=sizeof(server)-1;
		byte_copy(server,k,tmp);
		server[k]=0;
		break;
	      }
	  }
	  for (k=0; k<l; ++k) {
	    if (str_start(buf+k,"\nContent-Length: ")) {
	      k+=17;
	      if (buf[k+scan_ulonglong(buf+k,(unsigned long long*)expected+j)] != '\r')
		die(1,"parse error in HTTP header!");
	    } else if (str_start(buf+k,"\r\n\r\n"))
	      break;
	  }
	  if (expected[j]>0) {
	    if (l-(k+4)>expected[j])
	      expected[j]=0;
	    else
	      expected[j]-=l-(k+4);
	  }
	} else if (expected[j]==-2) {
	  /* no content-length header, eat everything until EOF */
	} else {
	  if (l>expected[j]) {
	    carp("got more than expected!");
	    expected[j]=0;
	  } else
	    expected[j]-=l;
	}
	if (expected[j]==0) {
	  ++done;	/* one down! */
	  avail[j]=1;
//	  printf("fd %d: keepleft[%d]=%d\n",i,j,keepleft[j]);
	  if (keepleft[j]>1) {
	    --keepleft[j];
	    io_dontwantread(i);
	    io_wantwrite(i);
	    expected[j]=0;
	  } else {
	    keepleft[j]=K;
	    io_close(i);
	    fds[j]=-1;
	  }
	}
      }
    }
  }

  taia_now(&now);
  taia_sub(&now,&now,&first);
  {
    char a[FMT_ULONG];
    char b[FMT_ULONG];
    char C[FMT_ULONG];
    char d[FMT_ULONG];
    char e[FMT_ULONG];
    char f[FMT_ULONG];
    char g[FMT_ULONG];
    char h[FMT_ULONG];
    char i[FMT_ULONG];
    char j[FMT_ULONG];
    unsigned long long l;
    a[fmt_ulong(a,now.sec.x)]=0;
    b[fmt_ulong0(b,(now.nano%1000000000)/100000,4)]=0;
    C[fmt_ulong(C,done)]=0;
    d[fmt_ulonglong(d,errors)]=0;
    e[fmt_ulonglong(e,bytes)]=0;

    /* let's say bytes = 10 MB, time = 1.2 sec.
    * then we want 10*1024*1024/1.2 == 8 MB/sec */
    l = (now.sec.x * 1024) + now.nano/976562;
    if (l) {
      int i;
      l=bytes/l;
      if (report)
	i=fmt_ulong(f,l);
      else {
	i=fmt_humank(f,l*1024);
	i+=fmt_str(f+i,"iB/sec");
      }
      f[i]=0;
    } else
      strcpy(f,"n/a");

    l = (now.sec.x * 1000) + now.nano/1000000;
    l = l ? (done*10000) / l : 0;
    g[fmt_ulong(g,l/10)]=0;
    h[fmt_ulong(h,c)]=0;
    i[fmt_ulong(i,K)]=0;
    j[fmt_ulong(j,kaputt)]=0;

    if (server[0]) msg("Server: ",server);
    if (report) {
      errmsg_iam(0);
      msg("req\terr\tconcur\tkeep\tkbytes\tsec\ttput\tr/s\treset");
      msg(C,"\t",d,"\t",h,"\t",i,"\t",e,"\t",a,".",b,"\t",f,"\t",g,"\t",j);
    } else {
      msg(C," requests, ",d," errors.");
      msg(e," bytes in ",a,".",b," seconds.");
      msg("Throughput: ",f);
      msg("Requests per second: ",g);
      msg("Connection refused/reset by peer: ",j);
    }

    {
      int i;
      for (i=0; i<9; ++i) {
	a[fmt_ulong(a,r[i])]=0;
	b[0]=i+'0'; b[1]=0;
	msg(b,"xx: ",a);
      }
    }
  }

  return 0;
}
示例#11
0
文件: proxy.c 项目: rsenn/dirlist
int
main(int argc, char* argv[]) {
  int s = socket_tcp6();
  uint32 scope_id;
  char ip[16];
  uint16 port;
  char hisip[16];
  uint16 hisport;
  uint32 hisscope_id;
  static char seed[128];
  static stralloc fqdn;
  static stralloc out;

  if(argc != 4) {
  usage:
    buffer_putsflush(buffer_2,
                     "usage: proxy myip myport hisip hisport\n"
                     "\n"
                     "e.g.: proxy 0 119 news.fu-berlin.de 119\n");
    return 0;
  }

  if(argv[1][scan_ip6if(argv[1], ip, &scope_id)]) {
    if(str_equal(argv[1], "0")) {
      byte_zero(ip, 16);
      scope_id = 0;
    } else
      goto usage;
  }
  if(argv[2][scan_ushort(argv[2], &port)])
    goto usage;
  if(argv[3][scan_ip6if(argv[3], hisip, &hisscope_id)]) {
    dns_random_init(seed);
    if(!stralloc_copys(&fqdn, argv[3]))
      goto nomem;
    if(dns_ip4(&out, &fqdn) == -1) {
      buffer_puts(buffer_2, "unable to find IP address for ");
      buffer_puts(buffer_2, argv[3]);
      buffer_puts(buffer_2, ": ");
      buffer_puterror(buffer_2);
      buffer_putnlflush(buffer_2);
      return 111;
    }
  } else if(!stralloc_catb(&out, hisip, 16)) {
  nomem:
    buffer_putsflush(buffer_2, "out of memory\n");
    return 111;
  }
  if(argv[4][scan_ushort(argv[4], &hisport)])
    goto usage;

  if(socket_bind6_reuse(s, ip, port, scope_id) == -1) {
    buffer_puts(buffer_2, "socket_bind6_reuse: ");
    buffer_puterror(buffer_2);
    buffer_putnlflush(buffer_2);
    return 111;
  }
  if(socket_listen(s, 16) == -1) {
    buffer_puts(buffer_2, "socket_listen: ");
    buffer_puterror(buffer_2);
    buffer_putnlflush(buffer_2);
    return 111;
  }
  if(!io_fd(s)) {
    buffer_puts(buffer_2, "io_fd: ");
    buffer_puterror(buffer_2);
    buffer_putnlflush(buffer_2);
    return 111;
  }
  io_wantread(s);
  for(;;) {
    int64 i;
    io_wait();
    while((i = io_canread()) != -1) {
      if(i == s) {
        /* the read event is on the server socket */
        /* that means it's an incoming connection */
        int n;
        while((n = socket_accept6(s, ip, &port, &scope_id)) != -1) {
          int x = socket_tcp6();
          if(x == -1) {
            buffer_puts(buffer_2, "socket_tcp6 failed: ");
          fail:
            buffer_puterror(buffer_2);
            buffer_putnlflush(buffer_2);
            io_close(n);
          } else {
            struct state* s = malloc(sizeof(struct state));
            if(!s)
              goto closefail;
            s->a = n;
            s->b = x;
            s->connected = 0;
            s->done = s->todo = 0;
            s->dir = UNDECIDED;
            io_nonblock(x);
            socket_connect6(x, out.s, hisport, hisscope_id);
            if(!io_fd(x) || !io_fd(n)) {
              buffer_puts(buffer_2, "io_fd failed: ");
            closefail:
              free(s);
              io_close(x);
              goto fail;
            }
            io_setcookie(x, s);
            io_setcookie(n, s);
            io_wantwrite(x);
          }
        }
        if(errno != EAGAIN) {
          buffer_puts(buffer_2, "socket_accept6 failed: ");
          buffer_puterror(buffer_2);
          buffer_putnlflush(buffer_2);
        }
      } else {
        /* read event on an established connection */
        struct state* s = io_getcookie(i);
        int l = io_tryread(i, s->buf, sizeof(s->buf));
        if(l == -1) {
          buffer_puts(buffer_2, "io_tryread(");
          buffer_putulong(buffer_2, i);
          buffer_puts(buffer_2, "): ");
          buffer_puterror(buffer_2);
          buffer_putnlflush(buffer_2);
          io_close(s->a);
          io_close(s->b);
        } else if(l == 0) {
          buffer_puts(buffer_2, "eof on fd #");
          buffer_putulong(buffer_2, i);
          buffer_putnlflush(buffer_2);
          io_close(i);
        } else {
          int r;
          switch(r = io_trywrite(i, s->buf, l)) {
            case -1:
              buffer_puts(buffer_2, "io_tryread(");
              buffer_putulong(buffer_2, i);
              buffer_puts(buffer_2, "): ");
              buffer_puterror(buffer_2);
              buffer_putnlflush(buffer_2);
              io_close(i);
              break;
            case 0:
              buffer_puts(buffer_2, "write eof on fd #");
              buffer_putulong(buffer_2, i);
              buffer_putnlflush(buffer_2);
              io_close(i);
            default:
              if(r != l) {
                buffer_puts(buffer_2, "short write on fd #");
                buffer_putulong(buffer_2, i);
                buffer_puts(buffer_2, ": wrote ");
                buffer_putulong(buffer_2, r);
                buffer_puts(buffer_2, ", wanted to write ");
                buffer_putulong(buffer_2, l);
                buffer_putsflush(buffer_2, ").\n");
              }
          }
        }
      }
    }
  }
  return 0;
}
示例#12
0
ssize_t http_handle_request( const int64 sock, struct ot_workstruct *ws ) {
  ssize_t reply_off, len;
  char   *read_ptr = ws->request, *write_ptr;

#ifdef WANT_FULLLOG_NETWORKS
  struct http_data *cookie = io_getcookie( sock );
  if( loglist_check_address( cookie->ip ) ) {
    ot_log *log = malloc( sizeof( ot_log ) );
    if( log ) {
      log->size = ws->request_size;
      log->data = malloc( ws->request_size );
      log->next = 0;
      log->time = g_now_seconds;
      memcpy( log->ip, cookie->ip, sizeof(ot_ip6));
      if( log->data ) {
        memcpy( log->data, ws->request, ws->request_size );
        if( !g_logchain_first )
          g_logchain_first = g_logchain_last = log;
        else {
          g_logchain_last->next = log;
          g_logchain_last = log;  
        }        
      } else
        free( log );
    }
  }
#endif

#ifdef _DEBUG_HTTPERROR
  reply_off = ws->request_size;
  if( ws->request_size >= G_DEBUGBUF_SIZE )
    reply_off = G_DEBUGBUF_SIZE - 1;
  memcpy( ws->debugbuf, ws->request, reply_off );
  ws->debugbuf[ reply_off ] = 0;
#endif
  
  /* Tell subroutines where to put reply data */
  ws->reply = ws->outbuf + SUCCESS_HTTP_HEADER_LENGTH;

  /* This one implicitely tests strlen < 5, too -- remember, it is \n terminated */
  if( memcmp( read_ptr, "GET /", 5) ) HTTPERROR_400;

  /* Skip leading '/' */
  for( read_ptr+=4; *read_ptr == '/'; ++read_ptr);

  /* Try to parse the request.
     In reality we abandoned requiring the url to be correct. This now
     only decodes url encoded characters, we check for announces and
     scrapes by looking for "a*" or "sc" */
  len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_PATH );

  /* If parsing returned an error, leave with not found */
  if( g_redirecturl && ( len == -2 ) ) HTTPERROR_302;
  if( len <= 0 ) HTTPERROR_404;

  /* This is the hardcore match for announce*/
  if( ( *write_ptr == 'a' ) || ( *write_ptr == '?' ) )
    http_handle_announce( sock, ws, read_ptr );
#ifdef WANT_FULLSCRAPE
  else if( !memcmp( write_ptr, "scrape HTTP/", 12 ) )
    http_handle_fullscrape( sock, ws );
#endif
  /* This is the hardcore match for scrape */
  else if( !memcmp( write_ptr, "sc", 2 ) )
    http_handle_scrape( sock, ws, read_ptr );
  /* All the rest is matched the standard way */
  else if( len == g_stats_path_len && !memcmp( write_ptr, g_stats_path, len ) )
    http_handle_stats( sock, ws, read_ptr );
  else
    HTTPERROR_404;

  /* Find out if the client wants to keep this connection alive */
  ws->keep_alive = 0;
#ifdef WANT_KEEPALIVE
  read_ptr=http_header( ws->request, ws->header_size, "connection");
  if( read_ptr && ( *read_ptr == 'K' || *read_ptr == 'k' ) ) ws->keep_alive = 1;
#endif

  /* If routines handled sending themselves, just return */
  if( ws->reply_size == -2 ) return 0;
  /* If routine failed, let http error take over */
  if( ws->reply_size <= 0 ) HTTPERROR_500;

  /* This one is rather ugly, so I take you step by step through it.

     1. In order to avoid having two buffers, one for header and one for content, we allow all above functions from trackerlogic to
     write to a fixed location, leaving SUCCESS_HTTP_HEADER_LENGTH bytes in our work buffer, which is enough for the static string
     plus dynamic space needed to expand our Content-Length value. We reserve SUCCESS_HTTP_SIZE_OFF for its expansion and calculate
     the space NOT needed to expand in reply_off
  */
  reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf( ws->outbuf, 0, "%zd", ws->reply_size );
  ws->reply = ws->outbuf + reply_off;

  /* 2. Now we sprintf our header so that sprintf writes its terminating '\0' exactly one byte before content starts. Complete
     packet size is increased by size of header plus one byte '\n', we will copy over '\0' in next step */
  ws->reply_size += 1 + sprintf( ws->reply, "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r", ws->reply_size );

  /* 3. Finally we join both blocks neatly */
  ws->outbuf[ SUCCESS_HTTP_HEADER_LENGTH - 1 ] = '\n';

  http_senddata( sock, ws );
  return ws->reply_size;
}
示例#13
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;
}
示例#14
0
static ssize_t http_handle_stats( const int64 sock, struct ot_workstruct *ws, char *read_ptr ) {
static const ot_keywords keywords_main[] =
  { { "mode", 1 }, {"format", 2 }, { NULL, -3 } };
static const ot_keywords keywords_mode[] =
  { { "peer", TASK_STATS_PEERS }, { "conn", TASK_STATS_CONNS }, { "scrp", TASK_STATS_SCRAPE }, { "udp4", TASK_STATS_UDP }, { "tcp4", TASK_STATS_TCP },
    { "busy", TASK_STATS_BUSY_NETWORKS }, { "torr", TASK_STATS_TORRENTS }, { "fscr", TASK_STATS_FULLSCRAPE },
    { "s24s", TASK_STATS_SLASH24S }, { "tpbs", TASK_STATS_TPB }, { "herr", TASK_STATS_HTTPERRORS }, { "completed", TASK_STATS_COMPLETED },
    { "top100", TASK_STATS_TOP100 }, { "top10", TASK_STATS_TOP10 }, { "renew", TASK_STATS_RENEW }, { "syncs", TASK_STATS_SYNCS }, { "version", TASK_STATS_VERSION },
    { "everything", TASK_STATS_EVERYTHING }, { "statedump", TASK_FULLSCRAPE_TRACKERSTATE }, { "fulllog", TASK_STATS_FULLLOG },
    { "woodpeckers", TASK_STATS_WOODPECKERS},
#ifdef WANT_LOG_NUMWANT
    { "numwants", TASK_STATS_NUMWANTS},
#endif
    { NULL, -3 } };
static const ot_keywords keywords_format[] =
  { { "bin", TASK_FULLSCRAPE_TPB_BINARY }, { "ben", TASK_FULLSCRAPE }, { "url", TASK_FULLSCRAPE_TPB_URLENCODED },
    { "txt", TASK_FULLSCRAPE_TPB_ASCII }, { NULL, -3 } };

  int mode = TASK_STATS_PEERS, scanon = 1, format = 0;

#ifdef WANT_RESTRICT_STATS
  struct http_data *cookie = io_getcookie( sock );

  if( !cookie || !accesslist_isblessed( cookie->ip, OT_PERMISSION_MAY_STAT ) )
    HTTPERROR_403_IP;
#endif

  while( scanon ) {
    switch( scan_find_keywords( keywords_main, &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 "mode" */
      if( ( mode = scan_find_keywords( keywords_mode, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM;
      break;
    case  2: /* matched "format" */
      if( ( format = scan_find_keywords( keywords_format, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM;
      break;
    }
  }

#ifdef WANT_FULLSCRAPE
  if( mode == TASK_FULLSCRAPE_TRACKERSTATE ) {
    format = mode; mode = TASK_STATS_TPB;
  }

  if( mode == TASK_STATS_TPB ) {
    struct http_data* cookie = io_getcookie( sock );
    tai6464 t;
#ifdef WANT_COMPRESSION_GZIP
    ws->request[ws->request_size] = 0;
#ifdef WANT_COMPRESSION_GZIP_ALWAYS
    if( strstr( read_ptr - 1, "gzip" ) ) {
#endif
      cookie->flag |= STRUCT_HTTP_FLAG_GZIP;
      format |= TASK_FLAG_GZIP;
#ifdef WANT_COMPRESSION_GZIP_ALWAYS
    }
#endif
#endif
    /* Pass this task to the worker thread */
    cookie->flag |= STRUCT_HTTP_FLAG_WAITINGFORTASK;

    /* Clients waiting for us should not easily timeout */
    taia_uint( &t, 0 ); io_timeout( sock, t );
    fullscrape_deliver( sock, format );
    io_dontwantread( sock );
    return ws->reply_size = -2;
  }
#endif

  /* default format for now */
  if( ( mode & TASK_CLASS_MASK ) == TASK_STATS ) {
    tai6464 t;
    /* Complex stats also include expensive memory debugging tools */
    taia_uint( &t, 0 ); io_timeout( sock, t );
    stats_deliver( sock, mode );
    return ws->reply_size = -2;
  }

  /* Simple stats can be answerred immediately */
  return ws->reply_size = return_stats_for_tracker( ws->reply, mode, 0 );
}
示例#15
0
/* New sync data on the stream */
static void handle_read( int64 peersocket ) {
  int i;
  int64 datalen;
  uint32_t tracker_id;
  proxy_peer *peer = io_getcookie( peersocket );

  if( !peer ) {
    /* Can't happen ;) */
    io_close( peersocket );
    return;
  }
  switch( peer->state & FLAG_MASK ) {
  case FLAG_DISCONNECTED:
    io_close( peersocket );
    break; /* Shouldnt happen */
  case FLAG_CONNECTING:
  case FLAG_WAITTRACKERID:
    /* We want at least the first four bytes to come at once, to avoid keeping extra states (for now)
       This also catches 0 bytes reads == EOF and negative values, denoting connection errors */
    if( io_tryread( peersocket, (void*)&tracker_id, sizeof( tracker_id ) ) != sizeof( tracker_id ) )
      goto close_socket;

    /* See, if we already have a connection to that peer */
    for( i=0; i<MAX_PEERS; ++i )
      if( ( g_connections[i].state & FLAG_MASK ) == FLAG_CONNECTED &&
            g_connections[i].tracker_id == tracker_id ) {
        fprintf( stderr, "Peer already connected. Closing connection.\n" );
        goto close_socket;
      }

    /* Also no need for soliloquy */
    if( tracker_id == g_tracker_id )
      goto close_socket;

    /* The new connection is good, send our tracker_id on incoming connections */
    if( peer->state == FLAG_CONNECTING )
      if( io_trywrite( peersocket, (void*)&g_tracker_id, sizeof( g_tracker_id ) ) != sizeof( g_tracker_id ) )
        goto close_socket;

    peer->tracker_id = tracker_id;
    PROXYPEER_SETCONNECTED( peer->state );

    if( peer->state & FLAG_OUTGOING )
      fprintf( stderr, "succeeded.\n" );
    else
      fprintf( stderr, "Incoming connection successful.\n" );

    break;
close_socket:
    fprintf( stderr, "Handshake incomplete, closing socket\n" );
    io_close( peersocket );
    reset_info_block( peer );
    break;
  case FLAG_CONNECTED:
    /* Here we acutally expect data from peer
       indata_length should be less than 20+256*7 bytes, for incomplete torrent entries */
    datalen = io_tryread( peersocket, (void*)(peer->indata + peer->indata_length), sizeof( peer->indata ) - peer->indata_length );
    if( !datalen || datalen < -1 ) {
      fprintf( stderr, "Connection closed by remote peer.\n" );
      io_close( peersocket );
      reset_info_block( peer );
    } else if( datalen > 0 ) {
      peer->indata_length += datalen;
      process_indata( peer );
    }
    break;
  }
}