diff --git a/build/jam/HaikuImage b/build/jam/HaikuImage index 39504b2f8d..82f690e7f6 100644 --- a/build/jam/HaikuImage +++ b/build/jam/HaikuImage @@ -35,11 +35,11 @@ BEOS_BIN = "[" addattr alert arp basename bc beep cat cardctl catattr chgrp query quit release renice rescan rlog rm rmattr rmindex rmdir roster route safemode screen_blanker sed settype setversion setvolume seq sh shar shutdown sleep sort split strace stty su sum sync sysinfo tac tail tar - tcpdump tee telnet telnetd test top touch tput tr traceroute translate true tsort - tty uname unchop unexpand unmount uniq unrar unshar unzip unzipsfx updatedb - uptime usb_dev_info uudecode uuencode vdir version vim waitfor wc wget - whoami xargs xres yes zdiff zforce zgrep zip zipcloak zipgrep zipnote - zipsplit zmore znew + tcpdump tcptester tee telnet telnetd test top touch tput tr traceroute + translate true tsort tty uname unchop unexpand unmount uniq unrar unshar + unzip unzipsfx updatedb uptime usb_dev_info uudecode uuencode vdir + version vim waitfor wc wget whoami xargs xres yes zdiff zforce zgrep zip + zipcloak zipgrep zipnote zipsplit zmore znew ; BEOS_APPS = Terminal Expander People ShowImage Pulse ProcessController diff --git a/src/bin/network/Jamfile b/src/bin/network/Jamfile index eac345f241..8671640ebf 100644 --- a/src/bin/network/Jamfile +++ b/src/bin/network/Jamfile @@ -11,6 +11,7 @@ SubInclude HAIKU_TOP src bin network netstat ; SubInclude HAIKU_TOP src bin network ping ; SubInclude HAIKU_TOP src bin network route ; SubInclude HAIKU_TOP src bin network tcpdump ; +SubInclude HAIKU_TOP src bin network tcptester ; SubInclude HAIKU_TOP src bin network telnet ; SubInclude HAIKU_TOP src bin network telnetd ; SubInclude HAIKU_TOP src bin network traceroute ; diff --git a/src/bin/network/tcptester/Jamfile b/src/bin/network/tcptester/Jamfile new file mode 100644 index 0000000000..d95ea65880 --- /dev/null +++ b/src/bin/network/tcptester/Jamfile @@ -0,0 +1,17 @@ +SubDir HAIKU_TOP src bin network tcptester ; + +BinCommand tcptester : + tcptester.cpp + : libnetwork.so +; + +# Installation -- in the test directory for the time being +HaikuInstall install-networking + : [ FDirName $(HAIKU_TEST_DIR) kits net ] + : tcptester ; + +HaikuInstall install-userland-networking + : [ FDirName $(HAIKU_TEST_DIR) kits net userland ] + : tcptester + : installed-userland-networking +; diff --git a/src/bin/network/tcptester/tcptester.cpp b/src/bin/network/tcptester/tcptester.cpp new file mode 100644 index 0000000000..1238fc71e8 --- /dev/null +++ b/src/bin/network/tcptester/tcptester.cpp @@ -0,0 +1,249 @@ +/* + * A very simple controlable traffic generator for TCP testing. + * + * Copyright 2007, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Hugo Santos, hugosantos@gmail.com + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +struct context { + int sock; + uint8 generator; + int index; + int8_t buffer[256]; +}; + +static int process_command(context *ctx); + +static int +number(context *ctx) +{ + int result = 0; + + while (isdigit(ctx->buffer[ctx->index])) { + result *= 10; + result += ctx->buffer[ctx->index] - '0'; + ctx->index++; + } + + return result; +} + + +static int +value(context *ctx) +{ + if (ctx->buffer[ctx->index] == '[') { + ctx->index++; + int upper, lower = number(ctx); + if (ctx->buffer[ctx->index] == ',') { + ctx->index++; + upper = number(ctx); + } else { + upper = lower + 50; + lower -= 50; + } + + return lower + rand() % (upper - lower + 1); + } + + return number(ctx); +} + + +static int +repeat(context *ctx) +{ + int max, saved, count = number(ctx); + + max = saved = ctx->index; + for (int i = 0; i < count; i++) { + ctx->index = saved; + if (process_command(ctx) < 0) + return -1; + if (ctx->index > max) + max = ctx->index; + } + + ctx->index = max; + return 0; +} + + +static void +send_packet(context *ctx, size_t bytes) +{ + uint8_t buffer[1024]; + uint8_t *ptr = buffer; + + if (bytes > sizeof(buffer)) + ptr = new uint8_t[bytes]; + + for (size_t i = 0; i < bytes; i++) { + ptr[i] = ctx->generator + '0'; + ctx->generator = (ctx->generator + 1) % 10; + } + + send(ctx->sock, ptr, bytes, 0); + + if (ptr != buffer) + delete [] ptr; +} + + +static int +process_command(context *ctx) +{ + while (ctx->buffer[ctx->index] != '.') { + ctx->index++; + + switch (ctx->buffer[ctx->index - 1]) { + case 'r': + if (repeat(ctx) < 0) + return -1; + break; + + case 'b': + send_packet(ctx, 1); + break; + + case 'p': + send_packet(ctx, value(ctx)); + break; + + case 's': + usleep(value(ctx) * 1000); + break; + + case 'W': + { + int value = number(ctx); + setsockopt(ctx->sock, SOL_SOCKET, SO_SNDBUF, &value, + sizeof(value)); + break; + } + + case 'k': + return -1; + } + } + + return 0; +} + + +static int +read_command(context *ctx) +{ + int index = 0; + + do { + int size = recv(ctx->sock, ctx->buffer + index, 1, 0); + if (size == 0) + return -1; + else if (size < 0) + continue; + + index++; + } while (ctx->buffer[index - 1] != '.'); + + ctx->index = 0; + return process_command(ctx); +} + + +static int32 +handle_client(void *data) +{ + context ctx = { *(int *)data, 0 }; + + while (read_command(&ctx) == 0); + + fprintf(stderr, "Client %d leaving.\n", ctx.sock); + + close(ctx.sock); + + return 0; +} + + +int +main(int argc, char *argv[]) +{ + int port = 12345; + + for (int i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-p")) { + i++; + assert(i < argc); + port = atoi(argv[i]); + } else if (!strcmp(argv[i], "-h")) { + fprintf(stderr, "tcptester [-p port]\n"); + return 1; + } + } + + int sock = socket(AF_INET, SOCK_STREAM, 0); + + if (sock < 0) { + perror("socket()"); + return -1; + } + + sockaddr_in sin; + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + + if (bind(sock, (sockaddr *)&sin, sizeof(sockaddr_in)) < 0) { + perror("bind()"); + return -1; + } + + if (listen(sock, 5) < 0) { + perror("listen()"); + return -1; + } + + while (1) { + sockaddr_in peer; + socklen_t peerLen = sizeof(peer); + + int newSock = accept(sock, (sockaddr *)&peer, &peerLen); + if (newSock < 0) { + perror("accept()"); + return -1; + } + + char buf[64]; + inet_ntop(AF_INET, &peer.sin_addr, buf, sizeof(buf)); + + thread_id newThread = spawn_thread(handle_client, "client", + B_NORMAL_PRIORITY, &newSock); + + fprintf(stderr, "New client %d from %s with thread id %ld.\n", + newSock, buf, (int32)newThread); + + resume_thread(newThread); + } + + return 0; +} +