/*
* swift.cpp
- * serp++
+ * swift the multiparty transport protocol
*
- * Created by Victor Grishchenko on 3/6/09.
- * Copyright 2009 Delft University of Technology. All rights reserved.
+ * Created by Victor Grishchenko on 2/15/10.
+ * Copyright 2010 Delft University of Technology. All rights reserved.
*
*/
-
+#include <stdio.h>
#include <stdlib.h>
-#include <fcntl.h>
-#ifndef _WIN32
-#include <sys/select.h>
-#include <sys/time.h>
-#include <sys/mman.h>
-#include <arpa/inet.h>
-#include <unistd.h>
-#endif
-#include <sys/stat.h>
-#include <string.h>
-
-//#include <glog/logging.h>
+#include "compat.h"
#include "swift.h"
-#include "datagram.h"
-using namespace std;
using namespace swift;
-swift::tint Channel::last_tick = 0;
-int Channel::MAX_REORDERING = 4;
-bool Channel::SELF_CONN_OK = false;
-swift::tint Channel::TIMEOUT = TINT_SEC*60;
-std::vector<Channel*> Channel::channels(1);
-SOCKET Channel::sockets[8] = {0,0,0,0,0,0,0,0};
-int Channel::socket_count = 0;
-Address Channel::tracker;
-tbheap Channel::send_queue;
-#include "ext/simple_selector.cpp"
-PeerSelector* Channel::peer_selector = new SimpleSelector();
+#define quit(...) {fprintf(stderr,__VA_ARGS__); exit(1); }
+SOCKET InstallHTTPGateway (Address addr);
+
+
+int main (int argc, char** argv) {
+
+ static struct option long_options[] =
+ {
+ {"hash", required_argument, 0, 'h'},
+ {"file", required_argument, 0, 'f'},
+ {"daemon", no_argument, 0, 'd'},
+ {"listen", required_argument, 0, 'l'},
+ {"tracker", required_argument, 0, 't'},
+ {"debug", no_argument, 0, 'D'},
+ {"progress",no_argument, 0, 'p'},
+ {"http", optional_argument, 0, 'g'},
+ {"wait", optional_argument, 0, 'w'},
+ {0, 0, 0, 0}
+ };
+
+ Sha1Hash root_hash;
+ char* filename = 0;
+ bool daemonize = false, report_progress = false;
+ Address bindaddr;
+ Address tracker;
+ Address http_gw;
+ tint wait_time = 0;
+
+ LibraryInit();
+
+ int c;
+ while ( -1 != (c = getopt_long (argc, argv, ":h:f:dl:t:Dpg::w::", long_options, 0)) ) {
+
+ switch (c) {
+ case 'h':
+ if (strlen(optarg)!=40)
+ quit("SHA1 hash must be 40 hex symbols\n");
+ root_hash = Sha1Hash(true,optarg); // FIXME ambiguity
+ if (root_hash==Sha1Hash::ZERO)
+ quit("SHA1 hash must be 40 hex symbols\n");
+ break;
+ case 'f':
+ filename = strdup(optarg);
+ break;
+ case 'd':
+ daemonize = true;
+ break;
+ case 'l':
+ bindaddr = Address(optarg);
+ if (bindaddr==Address())
+ quit("address must be hostname:port, ip:port or just port\n");
+ wait_time = TINT_NEVER;
+ break;
+ case 't':
+ tracker = Address(optarg);
+ if (tracker==Address())
+ quit("address must be hostname:port, ip:port or just port\n");
+ SetTracker(tracker);
+ break;
+ case 'D':
+ Channel::debug_file = optarg ? fopen(optarg,"a") : stdout;
+ break;
+ case 'p':
+ report_progress = true;
+ break;
+ case 'g':
+ http_gw = optarg ? Address(optarg) : Address(Address::LOCALHOST,8080);
+ if (wait_time==-1)
+ wait_time = TINT_NEVER; // seed
+ break;
+ case 'w':
+ if (optarg) {
+ char unit = 'u';
+ if (sscanf(optarg,"%lli%c",&wait_time,&unit)!=2)
+ quit("time format: 1234[umsMHD], e.g. 1M = one minute\n");
+ switch (unit) {
+ case 'D': wait_time *= 24;
+ case 'H': wait_time *= 60;
+ case 'M': wait_time *= 60;
+ case 's': wait_time *= 1000;
+ case 'm': wait_time *= 1000;
+ case 'u': break;
+ default: quit("time format: 1234[umsMHD], e.g. 1D = one day\n");
+ }
+ } else
+ wait_time = TINT_NEVER;
+ break;
+ }
-Channel::Channel (FileTransfer* transfer, int socket, Address peer_addr) :
- transfer_(transfer), peer_(peer_addr), peer_channel_id_(0), pex_out_(0),
- socket_(socket==-1?sockets[0]:socket), // FIXME
- data_out_cap_(bin64_t::ALL), last_data_out_time_(0), last_data_in_time_(0),
- own_id_mentioned_(false), next_send_time_(0), last_send_time_(0),
- last_recv_time_(0), rtt_avg_(TINT_SEC), dev_avg_(0), dip_avg_(TINT_SEC),
- data_in_dbl_(bin64_t::NONE), hint_out_size_(0),
- cwnd_(1), send_interval_(TINT_SEC), send_control_(PING_PONG_CONTROL),
- sent_since_recv_(0), ack_rcvd_recent_(0), ack_not_rcvd_recent_(0),
- last_loss_time_(0), owd_min_bin_(0), owd_min_bin_start_(NOW),
- owd_cur_bin_(0), dgrams_sent_(0), dgrams_rcvd_(0),
- data_in_(TINT_NEVER,bin64_t::NONE)
-{
- if (peer_==Address())
- peer_ = tracker;
- this->id_ = channels.size();
- channels.push_back(this);
- transfer_->hs_in_.push_back(id_);
- for(int i=0; i<4; i++) {
- owd_min_bins_[i] = TINT_NEVER;
- owd_current_[i] = TINT_NEVER;
+ } // arguments parsed
+
+
+ if (bindaddr!=Address()) { // seeding
+ if (Listen(bindaddr)<=0)
+ quit("cant listen to %s\n",bindaddr.str())
+ } else if (tracker!=Address() || http_gw!=Address()) { // leeching
+ for (int i=0; i<=10; i++) {
+ bindaddr = Address((uint32_t)INADDR_ANY,0);
+ if (Listen(bindaddr)>0)
+ break;
+ if (i==10)
+ quit("cant listen on %s\n",bindaddr.str());
+ }
+ }
+
+ if (tracker!=Address())
+ SetTracker(tracker);
+
+ if (http_gw!=Address())
+ InstallHTTPGateway(http_gw);
+
+ if (root_hash!=Sha1Hash::ZERO && !filename)
+ filename = strdup(root_hash.hex().c_str());
+
+ int file = -1;
+ if (filename) {
+ file = Open(filename,root_hash);
+ if (file<=0)
+ quit("cannot open file %s",filename);
+ printf("Root hash: %s\n", RootMerkleHash(file).hex().c_str());
}
- Reschedule();
- dprintf("%s #%u init %s\n",tintstr(),id_,peer_.str());
-}
-
-
-Channel::~Channel () {
- channels[id_] = NULL;
-}
-
-
-void swift::SetTracker(const Address& tracker) {
- Channel::tracker = tracker;
-}
-
-
-int Channel::DecodeID(int scrambled) {
- return scrambled ^ (int)Datagram::start;
-}
-int Channel::EncodeID(int unscrambled) {
- return unscrambled ^ (int)Datagram::start;
-}
-
-
-int swift::Listen (Address addr) {
- int sock = Datagram::Bind(addr);
- if (sock!=INVALID_SOCKET)
- Channel::sockets[Channel::socket_count++] = sock;
- return sock;
-}
+ if (bindaddr==Address() && file==-1 && http_gw==Address()) {
+ fprintf(stderr,"Usage:\n");
+ fprintf(stderr," -h, --hash\troot Merkle hash for the transmission\n");
+ fprintf(stderr," -f, --file\tname of file to use (root hash by default)\n");
+ fprintf(stderr," -l, --listen\t[ip:|host:]port to listen to (default: random)\n");
+ fprintf(stderr," -t, --tracker\t[ip:|host:]port of the tracker (default: none)\n");
+ fprintf(stderr," -D, --debug\tfile name for debugging logs (default: stdout)\n");
+ fprintf(stderr," -p, --progress\treport transfer progress\n");
+ fprintf(stderr," -g, --http\t[ip:|host:]port to bind HTTP gateway to (default localhost:8080)\n");
+ fprintf(stderr," -w, --wait\tlimit running time, e.g. 1[DHMs] (default: infinite with -l, -g)\n");
+ return 1;
+ }
-void swift::Shutdown (int sock_des) {
- for(int i=0; i<Channel::socket_count; i++)
- if (sock_des==-1 || Channel::sockets[i]==sock_des) {
- Datagram::Close(Channel::sockets[i]);
- Channel::sockets[i] = Channel::sockets[--Channel::socket_count];
+ tint start_time = NOW;
+
+ while ( (file>=0 && !IsComplete(file)) ||
+ (start_time+wait_time>NOW) ) {
+ swift::Loop(TINT_SEC);
+ if (report_progress && file>=0) {
+ fprintf(stderr,
+ "%s %lli of %lli (seq %lli) %lli dgram %lli bytes up, "\
+ "%lli dgram %lli bytes down\n",
+ IsComplete(file) ? "DONE" : "done",
+ Complete(file), Size(file), SeqComplete(file),
+ Datagram::dgrams_up, Datagram::bytes_up,
+ Datagram::dgrams_down, Datagram::bytes_down );
}
-}
-
-
-void swift::Loop (tint till) {
- Channel::Loop(till);
-}
-
-
-int swift::Open (const char* filename, const Sha1Hash& hash) {
- FileTransfer* ft = new FileTransfer(filename, hash);
- if (ft && ft->file().file_descriptor()) {
-
- /*if (FileTransfer::files.size()<fdes) // FIXME duplication
- FileTransfer::files.resize(fdes);
- FileTransfer::files[fdes] = ft;*/
-
- // initiate tracker connections
- if (Channel::tracker!=Address())
- new Channel(ft);
-
- return ft->file().file_descriptor();
- } else {
- if (ft)
- delete ft;
- return -1;
}
+
+ if (file!=-1)
+ Close(file);
+
+ if (Channel::debug_file)
+ fclose(Channel::debug_file);
+
+ swift::Shutdown();
+
+ return 0;
+
}
-
-void swift::Close (int fd) {
- // FIXME delete all channels
- if (fd>FileTransfer::files.size() && FileTransfer::files[fd])
- delete FileTransfer::files[fd];
-}
-
-
-void swift::AddPeer (Address address, const Sha1Hash& root) {
- Channel::peer_selector->AddPeer(address,root);
-}
-
-
-uint64_t swift::Size (int fdes) {
- if (FileTransfer::files.size()>fdes && FileTransfer::files[fdes])
- return FileTransfer::files[fdes]->file().size();
- else
- return 0;
-}
-
-
-bool swift::IsComplete (int fdes) {
- if (FileTransfer::files.size()>fdes && FileTransfer::files[fdes])
- return FileTransfer::files[fdes]->file().is_complete();
- else
- return 0;
-}
-
-
-uint64_t swift::Complete (int fdes) {
- if (FileTransfer::files.size()>fdes && FileTransfer::files[fdes])
- return FileTransfer::files[fdes]->file().complete();
- else
- return 0;
-}
-
-
-uint64_t swift::SeqComplete (int fdes) {
- if (FileTransfer::files.size()>fdes && FileTransfer::files[fdes])
- return FileTransfer::files[fdes]->file().seq_complete();
- else
- return 0;
-}
-
-
-const Sha1Hash& swift::RootMerkleHash (int file) {
- FileTransfer* trans = FileTransfer::file(file);
- if (!trans)
- return Sha1Hash::ZERO;
- return trans->file().root_hash();
-}
-
-
-/** <h2> swift handshake </h2>
- Basic rules:
- <ul>
- <li> to send a datagram, a channel must be created
- (channels are cheap and easily recycled)
- <li> a datagram must contain either the receiving
- channel id (scrambled) or the root hash
- </ul>
- <b>Note:</b>
- */