int main(int argc, char** argv) { if (argc != 4) { printf("USAGE: %s BITCOIND_ADDRESS BITCOIND_PORT LOCAL_ADDRESS\n", argv[0]); return -1; } #ifdef WIN32 WSADATA wsaData; if (WSAStartup(MAKEWORD(2,2), &wsaData)) return -1; #endif struct sockaddr_in6 addr; if (!lookup_address(argv[1], &addr)) { printf("Failed to lookup hostname\n"); return -1; } std::string host(gethostname(&addr)); P2PClient* inbound; P2PClient outbound(argv[1], std::stoul(argv[2]), [&](std::vector<unsigned char>& bytes, struct timeval) { struct timeval tv; gettimeofday(&tv, NULL); inbound->receive_block(bytes); std::vector<unsigned char> fullhash(32); CSHA256 hash; // Probably not BE-safe hash.Write(&bytes[sizeof(struct bitcoin_msg_header)], 80).Finalize(&fullhash[0]); hash.Reset().Write(&fullhash[0], fullhash.size()).Finalize(&fullhash[0]); for (unsigned int i = 0; i < fullhash.size(); i++) printf("%02x", fullhash[fullhash.size() - i - 1]); printf(" recv'd %s %lu\n", argv[1], uint64_t(tv.tv_sec)*1000 + uint64_t(tv.tv_usec)/1000); }, [&](std::shared_ptr<std::vector<unsigned char> >& bytes) { inbound->receive_transaction(bytes); }); inbound = new P2PClient(argv[3], 8334, [&](std::vector<unsigned char>& bytes, struct timeval) { outbound.receive_block(bytes); }, [&](std::shared_ptr<std::vector<unsigned char> >& bytes) { }); while (true) { sleep(1000); } }
void net_process() { recv_tx_cache.clear(); send_tx_cache.clear(); while (true) { relay_msg_header header; if (read_all(sock, (char*)&header, 4*3) != 4*3) return disconnect("failed to read message header"); if (header.magic != RELAY_MAGIC_BYTES) return disconnect("invalid magic bytes"); uint32_t message_size = ntohl(header.length); if (message_size > 1000000) return disconnect("got message too large"); if (header.type == VERSION_TYPE) { char data[message_size]; if (read_all(sock, data, message_size) < (int64_t)(message_size)) return disconnect("failed to read version message"); if (strncmp(VERSION_STRING, data, std::min(sizeof(VERSION_STRING), size_t(message_size)))) { relay_msg_header version_header = { RELAY_MAGIC_BYTES, MAX_VERSION_TYPE, htonl(strlen(VERSION_STRING)) }; if (send_all(sock, (char*)&version_header, sizeof(version_header)) != sizeof(version_header)) return disconnect("failed to write max version header"); if (send_all(sock, VERSION_STRING, strlen(VERSION_STRING)) != strlen(VERSION_STRING)) return disconnect("failed to write max version string"); return disconnect("unknown version string"); } relay_msg_header version_header = { RELAY_MAGIC_BYTES, VERSION_TYPE, htonl(strlen(VERSION_STRING)) }; if (send_all(sock, (char*)&version_header, sizeof(version_header)) != sizeof(version_header)) return disconnect("failed to write version header"); if (send_all(sock, VERSION_STRING, strlen(VERSION_STRING)) != strlen(VERSION_STRING)) return disconnect("failed to write version string"); connected = 2; send_mutex.unlock(); printf("%s Connected to relay node with protocol version %s\n", host.c_str(), VERSION_STRING); } else if (connected != 2) { return disconnect("got non-version before version"); } else if (header.type == MAX_VERSION_TYPE) { char data[message_size]; if (read_all(sock, data, message_size) < (int64_t)(message_size)) return disconnect("failed to read max_version string"); if (strncmp(VERSION_STRING, data, std::min(sizeof(VERSION_STRING), size_t(message_size)))) printf("%s peer sent us a MAX_VERSION message\n", host.c_str()); else return disconnect("got MAX_VERSION of same version as us"); } else if (header.type == BLOCK_TYPE) { struct timeval start, finish_read, finish_send; gettimeofday(&start, NULL); auto res = decompressRelayBlock(sock, message_size); if (std::get<2>(res)) return disconnect(std::get<2>(res)); gettimeofday(&finish_read, NULL); std::vector<unsigned char> fullhash(32); CSHA256 hash; // Probably not BE-safe hash.Write(&(*std::get<1>(res))[sizeof(struct bitcoin_msg_header)], 80).Finalize(&fullhash[0]); hash.Reset().Write(&fullhash[0], fullhash.size()).Finalize(&fullhash[0]); blocksAlreadySeen.insert(fullhash); bool relayed = provide_block(this, std::get<1>(res)); gettimeofday(&finish_send, NULL); if (relayed) { for (unsigned int i = 0; i < fullhash.size(); i++) printf("%02x", fullhash[fullhash.size() - i - 1]); printf(" BLOCK %lu %s UNTRUSTED_RELAY %u / %u TIMES: %ld %ld\n", uint64_t(finish_send.tv_sec)*1000 + uint64_t(finish_send.tv_usec)/1000, host.c_str(), (unsigned)std::get<0>(res), (unsigned)std::get<1>(res)->size(), int64_t(finish_read.tv_sec - start.tv_sec)*1000 + (int64_t(finish_read.tv_usec) - start.tv_usec)/1000, int64_t(finish_send.tv_sec - finish_read.tv_sec)*1000 + (int64_t(finish_send.tv_usec) - finish_read.tv_usec)/1000); } } else if (header.type == END_BLOCK_TYPE) { } else if (header.type == TRANSACTION_TYPE) { if (message_size > MAX_RELAY_TRANSACTION_BYTES && (recv_tx_cache.flagCount() >= MAX_EXTRA_OVERSIZE_TRANSACTIONS || message_size > MAX_RELAY_OVERSIZE_TRANSACTION_BYTES)) return disconnect("got freely relayed transaction too large"); auto tx = std::make_shared<std::vector<unsigned char> > (message_size); if (read_all(sock, (char*)&(*tx)[0], message_size) < (int64_t)(message_size)) return disconnect("failed to read loose transaction data"); recv_tx_cache.add(tx, message_size > MAX_RELAY_TRANSACTION_BYTES); provide_transaction(this, tx); } else return disconnect("got unknown message type"); } }
int main(int argc, char** argv) { if (argc != 2 || strlen(argv[1]) != 7) { printf("USAGE %s 7-char-LOCATION\n", argv[0]); return -1; } location = argv[1]; int blockonly_fd, txes_fd, new_fd; struct sockaddr_in6 addr; fd_set twofds; FD_ZERO(&twofds); if ((blockonly_fd = socket(AF_INET6, SOCK_STREAM, 0)) < 0 || (txes_fd = socket(AF_INET6, SOCK_STREAM, 0)) < 0) { printf("Failed to create socket\n"); return -1; } memset(&addr, 0, sizeof(addr)); addr.sin6_family = AF_INET6; addr.sin6_addr = in6addr_any; addr.sin6_port = htons(8334); int reuse = 1; if (setsockopt(blockonly_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) || bind(blockonly_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0 || listen(blockonly_fd, 3) < 0) { printf("Failed to bind 8334: %s\n", strerror(errno)); return -1; } addr.sin6_port = htons(8335); if (setsockopt(txes_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) || bind(txes_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0 || listen(txes_fd, 3) < 0) { printf("Failed to bind 8335: %s\n", strerror(errno)); return -1; } std::mutex list_mutex; std::set<P2PConnection*> blockSet; std::set<P2PConnection*> txesSet; std::set<P2PConnection*> localSet; std::function<void (P2PConnection*, std::shared_ptr<std::vector<unsigned char> >&, struct timeval)> relayBlock = [&](P2PConnection* from, std::shared_ptr<std::vector<unsigned char>> & bytes, struct timeval start_recv) { struct timeval start_send, finish_send; gettimeofday(&start_send, NULL); if (bytes->size() < 80) return; std::vector<unsigned char> fullhash(32); getblockhash(fullhash, *bytes, sizeof(struct bitcoin_msg_header)); { std::lock_guard<std::mutex> lock(list_mutex); std::set<P2PConnection*> *set; if (localSet.count(from)) set = &blockSet; else set = &localSet; for (auto it = set->begin(); it != set->end(); it++) { if (!(*it)->getDisconnectFlags()) (*it)->receive_block(fullhash, bytes); } } gettimeofday(&finish_send, NULL); for (unsigned int i = 0; i < fullhash.size(); i++) printf("%02x", fullhash[fullhash.size() - i - 1]); printf(" BLOCK %lu %s %s %u / %u TIMES: %ld %ld\n", uint64_t(start_send.tv_sec) * 1000 + uint64_t(start_send.tv_usec) / 1000, from->host.c_str(), localSet.count(from) ? "LOCALRELAY" : "REMOTEP2P", (unsigned)bytes->size(), (unsigned)bytes->size(), int64_t(start_send.tv_sec - start_recv.tv_sec)*1000 + (int64_t(start_send.tv_usec) - start_recv.tv_usec)/1000, int64_t(finish_send.tv_sec - start_send.tv_sec)*1000 + (int64_t(finish_send.tv_usec) - start_send.tv_usec)/1000); }; std::function<void (P2PConnection*, std::shared_ptr<std::vector<unsigned char> >&)> relayTx = [&](P2PConnection* from, std::shared_ptr<std::vector<unsigned char> >& bytes) { std::vector<unsigned char> fullhash(32); double_sha256(&(*bytes)[sizeof(struct bitcoin_msg_header)], &fullhash[0], bytes->size() - sizeof(struct bitcoin_msg_header)); std::lock_guard<std::mutex> lock(list_mutex); std::set<P2PConnection*> *set; if (localSet.count(from)) set = &txesSet; else set = &localSet; for (auto it = set->begin(); it != set->end(); it++) { if (!(*it)->getDisconnectFlags()) (*it)->receive_transaction(fullhash, bytes); } }; printf("Awaiting connections\n"); while (true) { FD_SET(blockonly_fd, &twofds); FD_SET(txes_fd, &twofds); struct timeval timeout; timeout.tv_sec = 30; timeout.tv_usec = 0; if (select(FD_SETSIZE, &twofds, NULL, NULL, &timeout) < 0) { printf("Failed to select (%s)\n", strerror(errno)); return -1; } socklen_t addr_size = sizeof(addr); std::string localhost("::ffff:127.0.0.1/"); std::string droppostfix(".uptimerobot.com"); if (FD_ISSET(blockonly_fd, &twofds)) { if ((new_fd = accept(blockonly_fd, (struct sockaddr *) &addr, &addr_size)) < 0) { printf("Failed to accept\n"); return -1; } std::string host = gethostname(&addr); if (host.length() > droppostfix.length() && !host.compare(host.length() - droppostfix.length(), droppostfix.length(), droppostfix)) close(new_fd); else { std::lock_guard<std::mutex> lock(list_mutex); P2PConnection *relay = new P2PConnection(new_fd, host, relayBlock, relayTx); if (!host.compare(0, localhost.size(), localhost)) localSet.insert(relay); else blockSet.insert(relay); } } if (FD_ISSET(txes_fd, &twofds)) { if ((new_fd = accept(txes_fd, (struct sockaddr *) &addr, &addr_size)) < 0) { printf("Failed to accept\n"); return -1; } std::string host = gethostname(&addr); if (host.length() > droppostfix.length() && !host.compare(host.length() - droppostfix.length(), droppostfix.length(), droppostfix)) close(new_fd); else { std::lock_guard<std::mutex> lock(list_mutex); P2PConnection *relay = new P2PConnection(new_fd, host, relayBlock, relayTx); if (!host.compare(0, localhost.size(), localhost)) localSet.insert(relay); else { blockSet.insert(relay); txesSet.insert(relay); } } } std::lock_guard<std::mutex> lock(list_mutex); for (auto it = blockSet.begin(); it != blockSet.end();) { if ((*it)->getDisconnectFlags() & DISCONNECT_COMPLETE) { auto rm = it++; auto item = *rm; txesSet.erase(item); blockSet.erase(rm); delete item; } else it++; } for (auto it = localSet.begin(); it != localSet.end();) { if ((*it)->getDisconnectFlags() & DISCONNECT_COMPLETE) { auto rm = it++; auto item = *rm; localSet.erase(rm); delete item; } else it++; } fprintf(stderr, "Have %lu local connection(s), %lu block connection(s) and %lu txes conenction(s)\n", localSet.size(), blockSet.size() - txesSet.size(), txesSet.size()); } }