Example #1
0
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); }
}
Example #2
0
	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());
	}
}