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; }
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; } }