[
  {
    "path": ".gitignore",
    "content": "# Object files\n*.o\n*.ko\n*.obj\n*.elf\n\n# Libraries\n*.lib\n*.a\n\n# Shared objects (inc. Windows DLLs)\n*.dll\n*.so\n*.so.*\n*.dylib\n\n# Executables\n*.exe\n*.out\n*.app\n*.i*86\n*.x86_64\n*.hex\n\na.out\nhellsend\nhellrecv\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2014 Kia <>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": "CC=clang\nCFLAGS=-Wall -ggdb -Weverything # -fsanitize=integer -fsanitize=address -fsanitize=undefined\n\nall: hellrecv hellsend\n\n%.o : %.c\n\t$(CC) -c $(CFLAGS) $< -o $@\n\nhellrecv: src/recv.o src/net.o\n\t$(CC) $(CFLAGS) -o $@ $^\n\nhellsend: src/send.o src/net.o\n\t$(CC) $(CFLAGS) -o $@ $^\n\nclean:\n\trm -f hellsend hellrecv src/*.o\n"
  },
  {
    "path": "README.md",
    "content": "hellcat\n=======\n\nnetcat that takes unfair advantage of traffic shaping systems that apply a\nhigher rate limit (more bandwidth) for the first k bytes (or t seconds, configs\nvary) of a TCP connection than for the rest of the connection. This is usually\ndone to make web browsing feel faster while still throttling big downloads. In\norder to exploit this behavior, we send k bytes over a TCP connection, then we\nclose it and then create another one. That way, data transfer always happens in\nthe initial regime (the one with a higher rate limit), increasing the average\nrate of data transfer.\n\nYou *need* to give the same chunksize value to the receiving end and the\ntransmitting end otherwise you will have incomplete transfer.\n\nTODO: transfer the chunksize over the control connection from the sender to the\nreceiver so you don't have to specify it once on each end.\n\n"
  },
  {
    "path": "src/net.c",
    "content": "/* copyright (c) 2014 Kia <> */\n/* this file contains functions that make dealing with the network a bit\n   easier */\n#include \"net.h\"\n\n/* this function just runs send(2) in a loop until the entire buffer is sent or\n   an error happens */\n\nssize_t send_all(int socket, const uint8_t *buffer, size_t length, int flag)\n{\n    size_t bytes_sent = 0;\n    size_t bytes_unsent = length;\n\n    ssize_t sent;\n\n    while (bytes_sent < length)\n    {\n        sent = send(socket, buffer + bytes_sent, bytes_unsent, flag);\n        if (sent == -1)\n            return -1;\n        bytes_sent += (size_t)sent;\n        bytes_unsent -= (size_t)sent;\n    }\n\n    return (ssize_t)bytes_sent;\n}\n\n/* just runs recv(2) in a loop until an error happens or our peer shuts down\n   the connection */\n\nssize_t recv_all(int socket, uint8_t *buffer, size_t length, int flag)\n{\n    size_t bytes_received = 0;\n    size_t bytes_unreceived = length;\n\n    ssize_t received;\n\n    while (bytes_received < length)\n    {\n        received = recv(socket, buffer + bytes_received, bytes_unreceived, flag);\n        if (received == -1)\n            return -1;\n        if (received == 0)\n            return 0;\n        bytes_received += (size_t)received;\n        bytes_unreceived -= (size_t)received;\n    }\n\n    return (ssize_t)bytes_received;\n}\n\n\n\nssize_t read_all(int fd, uint8_t *buffer, size_t length)\n{\n    size_t bytes_received = 0;\n    size_t bytes_unreceived = length;\n\n    ssize_t received;\n\n    while (bytes_received < length)\n    {\n        received = read(fd, buffer + bytes_received, bytes_unreceived);\n        if (received == -1)\n            return -1;\n        if (received == 0)\n            return (ssize_t) bytes_received;\n        bytes_received += (size_t)received;\n        bytes_unreceived -= (size_t)received;\n    }\n\n    return (ssize_t)bytes_received;\n}\n\n\nssize_t write_all(int fd, const uint8_t *buffer, size_t count)\n{\n    size_t bytes_sent = 0;\n    size_t bytes_unsent = count;\n\n    ssize_t sent;\n\n    while (bytes_sent < count)\n    {\n        sent = write(fd, buffer + bytes_sent, bytes_unsent);\n        if (sent == -1)\n            return -1;\n        bytes_sent += (size_t)sent;\n        bytes_unsent -= (size_t)sent;\n    }\n\n    return (ssize_t)bytes_sent;\n}\n\n\nint make_socket(char *node, char *port) {\n    int rval, fd = -1;\n    struct addrinfo hints;\n    struct addrinfo *result, *rp;\n\n    /* let's do fun stuff with getaddrinfo now */\n\n    memset(&hints, 0, sizeof(struct addrinfo));\n    hints.ai_family = AF_UNSPEC; /* either ipv4 or ipv6, we don't care */\n    hints.ai_socktype = SOCK_STREAM; /* TCP */\n    hints.ai_flags = 0;\n    hints.ai_protocol = 0;\n\n    rval = getaddrinfo(node, port, &hints, &result);\n    if (rval != 0) {\n        fprintf(stderr, \"getaddrinfo: %s\\n\", gai_strerror(rval));\n        exit(1);\n    }\n    /* now we iterate over the lists of results that getaddrinfo returned\n       until we can successfully make a socket and connect with it */\n    for (rp = result; rp != NULL; rp = rp->ai_next) {\n        fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);\n        if (fd == -1) {\n            /* the socket making failed, so we need to do a different\n               address */\n            continue;\n        }\n\n        /* we made a socket, now we try to connect */\n        if (connect(fd, rp->ai_addr, rp->ai_addrlen) != -1) {\n            break;  /* we successfully connected, let's exit this loop */\n        }\n\n        close(fd); /* making the socket worked but connect() failed so we\n                      close this socket */\n    }\n    if (rp == NULL) { /* no address worked */\n        fprintf(stderr, \"Could not connect to %s:%s\\n\",node, port);\n        exit(1);\n    }\n    freeaddrinfo(result);\n    return fd;\n}\n\n\n\nint make_listener(char *port)\n{\n    int rval, fd = -1;\n    struct addrinfo hints;\n    struct addrinfo *result, *rp;\n    int optval = 1;\n\n    /* let's do fun stuff with getaddrinfo now */\n\n    memset(&hints, 0, sizeof(struct addrinfo));\n    hints.ai_family = AF_UNSPEC; /* either ipv4 or ipv6, we don't care */\n    hints.ai_socktype = SOCK_STREAM; /* TCP */\n    hints.ai_flags = AI_PASSIVE;\n    hints.ai_protocol = 0;\n\n\n    rval = getaddrinfo(NULL, port, &hints, &result);\n    if (rval != 0)\n    {\n        fprintf(stderr, \"getaddrinfo: %s\\n\", gai_strerror(rval));\n        exit(1);\n    }\n    /* now we iterate over the lists of results that getaddrinfo returned\n       until we can successfully make a socket and connect with it */\n    for (rp = result; rp != NULL; rp = rp->ai_next)\n    {\n        fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);\n        setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval);\n\n        if (fd == -1)\n        {\n            /* the socket making failed, so we need to do a different\n               address */\n            continue;\n        }\n\n        /* we made a socket, now we try to bind  */\n        if (bind(fd, rp->ai_addr, rp->ai_addrlen) != -1)\n        {\n            break;  /* we successfully bound, let's exit this loop */\n        }\n        perror(\"bind bind bind\");\n\n        close(fd); /* making the socket worked but bind() failed so we\n                      close this socket */\n    }\n    if (rp == NULL) /* no address worked */\n    {\n        perror(\"bind\");\n        fprintf(stderr, \"Could not bind to %s\\n\", port);\n        exit(1);\n    }\n    freeaddrinfo(result);\n    listen(fd, 1);\n    return fd;\n}\n"
  },
  {
    "path": "src/net.h",
    "content": "/* copyright (c) 2013 Kia <> */\n\n#define _GNU_SOURCE\n#include <fcntl.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <arpa/inet.h>\n#include <stdio.h>\n#include <assert.h>\n#include <string.h>\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <netdb.h>\n#include <unistd.h>\n#include <errno.h>\n\nssize_t send_all(int socket, const uint8_t *buffer, size_t length, int flag);\nssize_t recv_all(int socket, uint8_t *buffer, size_t length, int flag);\nssize_t read_all(int fd, uint8_t *buffer, size_t length);\nssize_t write_all(int fd, const uint8_t *buffer, size_t count);\nint make_socket(char *node, char *port);\nint make_listener(char *port);\n\n#define CONT 0xcc\n#define DONE 0xdd\n"
  },
  {
    "path": "src/recv.c",
    "content": "/* copyright (c) 2014 Kia <> */\n\n/* the functions in this file handle the whole \"receiving\" thing */\n\n#include \"net.h\"\n#include <sys/time.h>\n\n\n\nint main(int argc, char* argv[])\n{\n    int datafd, controlfd, control_listener, data_listener, listening;\n    ssize_t rval, rval_send;\n    uint8_t statusbyte, cont = CONT;\n    uint8_t *buf;\n    size_t bufsize, totalsize = 0;\n    struct timeval start, end;\n    float elapsed;\n\n    if ((argc != 5) && (argc != 4)) {\n        fprintf(stderr, \"usage: %s chunksize dataport controlport [host]\\nchunksize is in bytes\\n\", argv[0]);\n        exit(10);\n    }\n\n    if (argc == 4) {\n        listening = 1;\n    } else {\n        listening = 0;\n    }\n\n    bufsize = (size_t) atol(argv[1]);\n    buf = malloc(bufsize);\n    assert(buf != NULL);\n\n    if (listening == 0) {\n        controlfd = make_socket(argv[4], argv[3]);\n    } else {\n        control_listener = make_listener(argv[3]);\n        data_listener = make_listener(argv[2]);\n        controlfd = accept(control_listener, NULL, NULL);\n        if (controlfd == -1) {\n            perror(\"accept on control port\");\n            exit(1);\n        }\n    }\n\n    sleep(1);\n    gettimeofday(&start, NULL);\n\n    while (1) {\n        rval = read(controlfd, &statusbyte, 1);\n\n        if (rval != 1) {\n            perror(\"read on controlfd\");\n            exit(1);\n        }\n\n        if (statusbyte == DONE) {\n            fprintf(stderr, \"received DONE\\n\");\n            gettimeofday(&end, NULL);\n            elapsed = (end.tv_sec - start.tv_sec) + (end.tv_usec - start.tv_usec) / 1000000;\n            fprintf(stderr, \"transferred %zu bytes in %.3f seconds\\n\", totalsize, elapsed);\n\n            break;\n        }\n\n        assert (statusbyte == CONT);\n\n        if (listening == 0) {\n            datafd = make_socket(argv[4], argv[2]);\n        } else {\n            datafd = accept(data_listener, NULL, NULL);\n            if (datafd == -1) {\n                perror(\"accept on data port\");\n                exit(1);\n            }\n        }\n\n        rval = read_all(datafd, buf, bufsize);\n\n        if (rval == -1) {\n            perror(\"read on datafd\");\n            exit(1);\n        }\n\n        if (rval == 0) { /* eof on datafd */\n            fprintf(stderr, \"unexpected EOF on datafd\\n\");\n            exit(1);\n        }\n\n        rval_send = write_all(1, buf, (size_t)rval);\n\n        if (rval_send != rval) {\n            perror(\"write on stdout\");\n            exit(1);\n        }\n\n        if (listening == 1) {\n            rval = write(controlfd, &cont, 1);\n            if (rval != 1) {\n                perror(\"write on controlfd\");\n                exit(1);\n            }\n\n        }\n\n        totalsize += (size_t) rval;\n        close(datafd);\n    }\n\n    close(controlfd);\n\n    return 0;\n}\n\n"
  },
  {
    "path": "src/send.c",
    "content": "/* copyright (c) 2014 Kia <> */\n\n/* the functions in this file handle the whole \"sending data out\" thing */\n\n#include \"net.h\"\n\n\n\n\nint main(int argc, char* argv[])\n{\n    int data_listener, controlfd, control_listener, listening, datafd = -1;\n    ssize_t rval, numbytes, rval_send;\n    uint8_t *buf;\n    size_t bufsize;\n    uint8_t cont = CONT;\n    uint8_t done = DONE;\n    uint8_t statusbyte;\n\n    if ((argc != 4) && (argc != 5)) {\n        fprintf(stderr, \"usage: %s chunksize dataport controlport [host]\\nchunksize is in bytes\\n\", argv[0]);\n        exit(10);\n    }\n\n    if (argc == 4) {\n        listening = 1;\n    } else {\n        listening = 0;\n    }\n\n\n    bufsize = (size_t) atol(argv[1]);\n    buf = malloc(bufsize);\n    assert(buf != NULL);\n\n    if (listening == 1) {\n        control_listener = make_listener(argv[3]);\n        data_listener = make_listener(argv[2]);\n        controlfd = accept(control_listener, NULL, NULL);\n        if (controlfd == -1) {\n            perror(\"accept on control port\");\n            exit(1);\n        }\n\n    } else {\n        controlfd = make_socket(argv[4], argv[3]);\n    }\n\n    while (1) {\n\n        numbytes = read_all(0, buf, bufsize);\n        if (numbytes == -1) {\n            perror(\"read on stdin\");\n            exit(1);\n        }\n        if (numbytes == 0) { /* eof on stdin */\n            rval = write(controlfd, &done, 1);\n            if (rval != 1) {\n                perror(\"write on controlfd (while exiting, which makes this even more humiliating)\");\n                exit(1);\n            }\n\n            break;\n        }\n        rval = write(controlfd, &cont, 1);\n\n        if (rval != 1) {\n            perror(\"write on controlfd\");\n            exit(1);\n        }\n\n        if (listening == 1) {\n            datafd = accept(data_listener, NULL, NULL);\n        } else {\n            datafd = make_socket(argv[4], argv[2]);\n        }\n\n\n        if (datafd == -1) {\n            perror(\"data port connect / accept failure?\");\n            exit(1);\n        }\n        rval_send = write_all(datafd, buf, (size_t) numbytes);\n\n        if (rval_send != numbytes) {\n            perror(\"incomplete write on datafd\");\n            exit(1);\n        }\n\n        rval = close(datafd);\n\n        if (rval != 0) {\n            perror(\"close\");\n            fprintf(stderr,\"close returned %d\\n\", rval);\n            exit(1);\n        }\n\n        if (listening == 0) {\n            rval = read(controlfd, &statusbyte, 1);\n            if (rval != 1) {\n                perror(\"read on controlfd\");\n                fprintf(stderr, \"%d\\n\", rval);\n                exit(1);\n            }\n            assert(statusbyte == CONT);\n        }\n\n\n    }\n\n\n    free(buf);\n    if (datafd != -1) {\n        close(datafd);\n    }\n\n    close(controlfd);\n\n    return 0;\n}\n\n"
  }
]