TorFlowManager* torflowmanager_new(gint argc, gchar* argv[], ShadowLogFunc slogf, ShadowCreateCallbackFunc scbf) {
	g_assert(slogf);
	g_assert(scbf);

	if(argc != 9) {
		slogf(SHADOW_LOG_LEVEL_WARNING, __FUNCTION__, USAGE);
		return NULL;
	}

	/* argv[0] is the 'program name' and should be ignored */
	gchar* v3bwPath = argv[1];
	gint pausetime = atoi(argv[2]);
	gint numWorkers = atoi(argv[3]);

	if(numWorkers < 1) {
		slogf(SHADOW_LOG_LEVEL_WARNING, __FUNCTION__,
            "Invalid number of torflow workers (%d). torflow will not operate.", numWorkers);
		return NULL;
	}

	gint slicesize = atoi(argv[4]);
	gdouble nodeCap = atof(argv[5]);

	gint hostControlPort = atoi(argv[6]);
	g_assert(hostControlPort <= G_MAXUINT16); // TODO log error instead
    in_port_t netControlPort = htons((in_port_t)hostControlPort);

    gint hostSocksPort = atoi(argv[7]);
    g_assert(hostSocksPort <= G_MAXUINT16); // TODO log error instead
    in_port_t netSocksPort = htons((in_port_t)hostSocksPort);

    /* get file server infos */
    GQueue* fileservers = g_queue_new();
    gchar** fsparts = g_strsplit(argv[8], ",", 0);
    gchar* fspart = NULL;
    for(gint i = 0; (fspart = fsparts[i]) != NULL; i++) {
        gchar** parts = g_strsplit(fspart, ":", 0);
        g_assert(parts[0] && parts[1]);

        /* the server domain name */
        gchar* name = parts[0];

        /* port in host order */
        gchar* hostFilePortStr = parts[1];
        gint hostFilePort = atoi(hostFilePortStr);
        g_assert(hostFilePort <= G_MAXUINT16); // TODO log error instead
        in_port_t netFilePort = htons((in_port_t)hostFilePort);

        TorFlowFileServer* fs = torflowfileserver_new(name, netFilePort);
        g_assert(fs);
        g_queue_push_tail(fileservers, fs);
        g_strfreev(parts);

        slogf(SHADOW_LOG_LEVEL_INFO, __FUNCTION__,
                "parsed file server %s at %s:%u",
                torflowfileserver_getName(fs),
                torflowfileserver_getHostIPStr(fs),
                ntohs(torflowfileserver_getNetPort(fs)));
    }
    g_strfreev(fsparts);

    g_assert(g_queue_get_length(fileservers) > 0); // TODO log error instead

    /* use epoll to asynchronously watch events for all of our sockets */
    gint mainEpollDescriptor = epoll_create(1);
    g_assert(mainEpollDescriptor > 0); // TODO log error instead

	TorFlowManager* tfm = g_new0(TorFlowManager, 1);
	tfm->slogf = slogf;
	tfm->scbf = scbf;
	tfm->workers = numWorkers;
	tfm->ed = mainEpollDescriptor;
	tfm->slicesize = slicesize;

	tfm->AllRelaysByFingerprint = g_hash_table_new_full(g_str_hash,
	        g_str_equal, NULL, (GDestroyNotify)_torflowmanager_freeRelay);
	tfm->currentSlices = g_queue_new();

    /* now start our controller to fetch descriptors */
	tfm->baseED = epoll_create(1);
	g_assert(tfm->baseED > 0); // TODO log error
	torflowutil_epoll(tfm->ed, tfm->baseED, EPOLL_CTL_ADD, EPOLLIN, tfm->slogf);
    TorFlowEventCallbacks handlers;
    memset(&handlers, 0, sizeof(TorFlowEventCallbacks));
    handlers.onBootstrapComplete = (BootstrapCompleteFunc) _torflowmanager_onBootstrapComplete;
    handlers.onDescriptorsReceived = (DescriptorsReceivedFunc) _torflowmanager_onDescriptorsReceived;
    torflowbase_init(&tfm->_base, &handlers, slogf, scbf, netControlPort, tfm->baseED, 0);
    torflowbase_start(&tfm->_base);

    /* helper to manage stat reports and create v3bw files */
    tfm->tfa = torflowaggregator_new(slogf, v3bwPath, nodeCap);

    /* workers that will probe the relays */
    tfm->probers = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) torflowbase_free);

    for(gint i = 1; i <= numWorkers; i++) {
        /* get the next fileserver */
        TorFlowFileServer* probeFileServer = g_queue_pop_head(fileservers);

        TorFlowProber* prober = torflowprober_new(slogf, scbf, tfm, i, numWorkers, pausetime,
                netControlPort, netSocksPort, probeFileServer);
        g_assert(prober); // TODO log error instead

        /* make sure we watch the prober events on our main epoll */
        gint proberED = torflow_getEpollDescriptor((TorFlow*)prober);
        torflowutil_epoll(tfm->ed, proberED, EPOLL_CTL_ADD, EPOLLIN, tfm->slogf);

        /* store the prober by its unique epoll descriptor */
        g_hash_table_replace(tfm->probers, GINT_TO_POINTER(proberED), prober);

        /* reuse the file server in round robin fashion */
        g_queue_push_tail(fileservers, probeFileServer);
    }

    /* the used file servers have been reffed by the probers;
     * the rest will be safely freed */
    g_queue_free_full(fileservers, (GDestroyNotify)torflowfileserver_unref);

    tfm->slogf(SHADOW_LOG_LEVEL_MESSAGE, __FUNCTION__,
                    "started torflow with %i workers on control port %i and socks port %i",
                    numWorkers, hostControlPort, hostSocksPort);

    return tfm;
}
Example #2
0
static void _torflow_activateSocksDownload(TorFlow* tf, TorFlowDownload* tfd, uint32_t events) {
	g_assert(tf);
	g_assert(tfd);

	if(events & EPOLLOUT) {
		tf->_base.slogf(SHADOW_LOG_LEVEL_DEBUG, tf->_base.id, "EPOLLOUT is set");
	}
	if(events & EPOLLIN) {
		tf->_base.slogf(SHADOW_LOG_LEVEL_DEBUG, tf->_base.id, "EPOLLIN is set");
	}

beginsocks:
	switch(tfd->socksState) {
	case S_CONNECTING: {
		if(events & EPOLLOUT) {
			/* we are now connected */
			tfd->socksState = S_SOCKSSENDINIT;
			goto beginsocks;
		}
		break;
	}

	case S_SOCKSSENDINIT: {
		g_assert(events & EPOLLOUT);

		gchar sendbuf[16];
		sendbuf[0] = 0x05;
		sendbuf[1] = 0x01;
		sendbuf[2] = 0x00;
		gint bytes = send(tfd->socksd, sendbuf, 3, 0);

		g_assert(bytes == 3);

		tf->_base.slogf(SHADOW_LOG_LEVEL_INFO, tf->_base.id, "socks sent init");

		torflowutil_epoll(tf->internal->epolld, tfd->socksd, EPOLL_CTL_MOD, EPOLLIN, tf->_base.slogf);
		tfd->socksState = S_SOCKSRECVINIT;
		break;
	}

	case S_SOCKSRECVINIT: {
		g_assert(events & EPOLLIN);

		gint bytes = recv(tfd->socksd, tfd->recvbuf, BUFSIZE, 0);

		if(bytes != 2 || tfd->recvbuf[0] != 0x05 || tfd->recvbuf[1] != 0x00) {
			tf->_base.slogf(SHADOW_LOG_LEVEL_CRITICAL, tf->_base.id,
				"socks init error (read %i bytes)", bytes);
		} else {
			tf->_base.slogf(SHADOW_LOG_LEVEL_INFO, tf->_base.id, "socks init success");
			torflowutil_epoll(tf->internal->epolld, tfd->socksd, EPOLL_CTL_MOD, EPOLLOUT, tf->_base.slogf);
			tfd->socksState = S_SOCKSSENDCONNECT;
		}

		break;
	}

	case S_SOCKSSENDCONNECT: {
		g_assert(events & EPOLLOUT);

		in_addr_t netIP = torflowfileserver_getNetIP(tfd->fileserver);
		in_port_t netPort = torflowfileserver_getNetPort(tfd->fileserver);

		gchar sendbuf[64];
		memset(sendbuf, 0, sizeof(gchar)*64);
		sendbuf[0] = 0x05;
		sendbuf[1] = 0x01;
		sendbuf[2] = 0x00;
		sendbuf[3] = 0x01;
		memcpy(&sendbuf[4], &(netIP), 4);
		memcpy(&sendbuf[8], &(netPort), 2);

		gint bytes = send(tfd->socksd, sendbuf, 10, 0);

		g_assert(bytes == 10);

		tf->_base.slogf(SHADOW_LOG_LEVEL_INFO, tf->_base.id,
		        "sent socks server connect to %s at %s:%u",
		        torflowfileserver_getName(tfd->fileserver),
		        torflowfileserver_getHostIPStr(tfd->fileserver), ntohs(netPort));

		torflowutil_epoll(tf->internal->epolld, tfd->socksd, EPOLL_CTL_MOD, EPOLLIN, tf->_base.slogf);
		tfd->socksState = S_SOCKSRECVCONNECT;
		break;
	}

	case S_SOCKSRECVCONNECT: {
		g_assert(events & EPOLLIN);

		gint bytes = recv(tfd->socksd, tfd->recvbuf+tfd->recvOffset, BUFSIZE-tfd->recvOffset, 0);
		tfd->recvOffset += bytes;

		if(tfd->recvOffset < 10) {
			/* wait until they send us more */
			break;
		}

		if(tfd->recvbuf[0] == 0x05 && tfd->recvbuf[1] == 0x00 && tfd->recvbuf[3] == 0x01) {
			/* socks server may tell us to connect somewhere else ... */
			in_addr_t serverAddress;
			in_port_t serverPort;
			memcpy(&serverAddress, &(tfd->recvbuf[4]), 4);
			memcpy(&serverPort, &(tfd->recvbuf[8]), 2);

			/* ... but we dont support it */
			g_assert(serverAddress == 0 && serverPort == 0);

			tf->_base.slogf(SHADOW_LOG_LEVEL_INFO, tf->_base.id, "socks connect success");

			torflowutil_epoll(tf->internal->epolld, tfd->socksd, EPOLL_CTL_MOD, 0, tf->_base.slogf);
			tfd->socksState = S_READY;

			if(tf->internal->eventHandlers.onFileServerConnected) {
				/* wait a second to give the stream success msg a chance to get delivered */
				CallbackData* cd = g_new0(CallbackData, 1);
				cd->tf = tf;
				cd->socksd = tfd->socksd;
				tf->_base.scbf((ShadowPluginCallbackFunc)_torflow_callFileServerConnected, cd, 1000);
			}
		} else if(tfd->recvbuf[0] == 0x05 && tfd->recvbuf[1] == 0x06 && tfd->recvbuf[2] == 0x00 && tfd->recvbuf[3] == 0x01) {
			tf->_base.slogf(SHADOW_LOG_LEVEL_MESSAGE, tf->_base.id,
				"socks connect timed out");
			if(tf->internal->eventHandlers.onFileServerTimeout) {
				CallbackData* cd = g_new0(CallbackData, 1);
				cd->tf = tf;
				cd->socksd = 0;
				tf->_base.scbf((ShadowPluginCallbackFunc)_torflow_callFileServerTimeout, cd, 1000);
			}	
		} else {
			tf->_base.slogf(SHADOW_LOG_LEVEL_CRITICAL, tf->_base.id,
				"socks connect error (read %i bytes, code %x%x%x%x)", bytes,
				tfd->recvbuf[0], tfd->recvbuf[1], tfd->recvbuf[2], tfd->recvbuf[3]);
			//tf->_base.slogf(SHADOW_LOG_LEVEL_CRITICAL, tf->_base.id,
			//	"socks connect error (read %i bytes)", bytes);
		}

		/* reset */
		tfd->recvOffset = 0;
		memset(tfd->recvbuf, 0, BUFSIZE);

		break;
	}

	case S_HTTPSENDREQUEST: {
		g_assert(events & EPOLLOUT);

		GString* request = g_string_new(NULL);
		g_string_printf(request, "GET %s HTTP/1.1\r\nHost: %s\r\n\r\n", tfd->filePath,
		        torflowfileserver_getName(tfd->fileserver));
		gint bytes = send(tfd->socksd, request->str, request->len, 0);

		g_assert(bytes == request->len);
		g_string_free(request, TRUE);

		clock_gettime(CLOCK_REALTIME, &(tfd->start));

		torflowutil_epoll(tf->internal->epolld, tfd->socksd, EPOLL_CTL_MOD, EPOLLIN, tf->_base.slogf);
		tfd->socksState = S_HTTPRECVREPLY;
		break;
	}

	case S_HTTPRECVREPLY: {
		g_assert(events & EPOLLIN);

		gint bytes = recv(tfd->socksd, tfd->recvbuf+tfd->recvOffset, BUFSIZE-tfd->recvOffset, 0);
		tfd->recvOffset += bytes;
		tfd->recvbuf[tfd->recvOffset] = 0x0;

		if(bytes == 0) {
			tf->_base.slogf(SHADOW_LOG_LEVEL_WARNING, tf->_base.id, "socks socket %i was closed", tfd->socksd);
			torflowutil_epoll(tf->internal->epolld, tfd->socksd, EPOLL_CTL_DEL, 0, tf->_base.slogf);
			close(tfd->socksd);
			break;
		}

		if(!tfd->contentLength) {
			gchar* error404 = g_strstr_len(tfd->recvbuf, bytes, "HTTP/1.1 404 NOT FOUND\r\n");
			g_assert(!error404);

			gchar* ok200 = g_strstr_len(tfd->recvbuf, bytes, "HTTP/1.1 200 OK\r\n");
			g_assert(ok200);

			gchar* clen = g_strstr_len(tfd->recvbuf, bytes, "Content-Length: ");
			if(clen) {
				clen += 16;
			}

			gchar* payload = g_strstr_len(tfd->recvbuf, bytes, "\r\n\r\n");
			if(payload) {
				clock_gettime(CLOCK_REALTIME, &(tfd->first));
				payload[0] = 0x0; // so we can get content length
				payload += 4;
				tfd->contentLength = (gsize) atoi(clen);
				tfd->remaining = tfd->contentLength - (bytes - (payload - tfd->recvbuf));
				tfd->recvOffset = 0;
				tf->_base.slogf(SHADOW_LOG_LEVEL_INFO, tf->_base.id, "http payload will be %i bytes", tfd->contentLength);
			}
		} else {
			tfd->remaining -= bytes;
			tfd->recvOffset = 0;
			tf->_base.slogf(SHADOW_LOG_LEVEL_INFO, tf->_base.id, "got %i bytes", bytes);
		}

		/* finished a download probe - only the prober will get here bc senders stop reading */
		if(!tfd->remaining) {
			clock_gettime(CLOCK_REALTIME, &(tfd->end));

			gsize roundTripTime = torflowutil_computeTime(&tfd->start, &tfd->first);
			gsize payloadTime = torflowutil_computeTime(&tfd->first, &tfd->end);
			gsize totalTime = torflowutil_computeTime(&tfd->start, &tfd->end);
			gint contentLength = tfd->contentLength;

			tfd->contentLength = 0;
			tfd->recvOffset = 0;
			memset(tfd->recvbuf, 0, BUFSIZE);

			torflowutil_epoll(tf->internal->epolld, tfd->socksd, EPOLL_CTL_MOD, 0, tf->_base.slogf);
			tfd->socksState = S_READY;

			if(tf->internal->eventHandlers.onFileDownloadComplete) {
				tf->internal->eventHandlers.onFileDownloadComplete(tf, contentLength, roundTripTime, payloadTime, totalTime);
			}
		}

		break;
	}

	case S_READY: {
		torflowutil_epoll(tf->internal->epolld, tfd->socksd, EPOLL_CTL_MOD, 0, tf->_base.slogf);
		break;
	}

	default:
		break;
	}
}