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