Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# derived from https://docs.github.com/en/packages/managing-github-packages-using-github-actions-workflows/publishing-and-installing-a-package-with-github-actions
name: Create and publish Docker image
on:
push:
branches:
- "*"
tags:
- "*"

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
build-and-push-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
- name: Checkout repository
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab #v3.5.2

- name: Log in to the Container registry
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a # v2.1.0
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@c4ee3adeed93b1fa6a762f209fb01608c1a22f1e # v4.4.0
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

- name: Build and push Docker image
uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 # v4.0.0
with:
file: Dockerfile
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sloxy
16 changes: 16 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from alpine:latest as build

RUN apk update && apk add gcc musl-dev

ADD sloxy.c /sloxy.c

RUN gcc sloxy.c -lm -o sloxy

CMD sloxy

from alpine:latest

COPY --from=build /sloxy /bin/sloxy
ADD entrypoint.sh /bin/entrypoint.sh

CMD /bin/entrypoint.sh
7 changes: 7 additions & 0 deletions build-ubuntu.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/bash

#https://stackoverflow.com/a/8671386

set -x

gcc sloxy.c -lm -o sloxy
34 changes: 34 additions & 0 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/bin/sh

if [ -z "$LISTEN_ADDRESS" ]; then
LISTEN_ADDRESS="0.0.0.0"
fi
if [ -z "$LISTEN_PORT" ]; then
LISTEN_PORT=2000
fi
if [ -z "$TARGET_ADDRESS" ]; then
if [ -z "$TARGET_HOST" ]; then
echo "Either TARGET_ADDRESS or TARGET_HOST must be set"
exit 1
fi
TARGET_ADDRESS=$( getent ahostsv4 "$TARGET_HOST" | head -n 1 | awk '{ print $1 }' )
if [ -z "$TARGET_ADDRESS" ]; then
echo "Unable to resolve '$TARGET_HOST'"
exit 1
fi
fi
if [ -z "$TARGET_PORT" ]; then
echo "TARGET_PORT must be set"
exit 1
fi
if [ -z "$LIMIT_BYTES_PER_SECOND" ]; then
echo "LIMIT_BYTES_PER_SECOND must be set"
exit 1
fi
if [ -z "$DELAY_SECONDS" ]; then
echo "DELAY_SECONDS must be set"
exit 1
fi

echo "Running: /bin/sloxy $LISTEN_ADDRESS $LISTEN_PORT $TARGET_ADDRESS $TARGET_PORT $LIMIT_BYTES_PER_SECOND $DELAY_SECONDS"
exec /bin/sloxy $LISTEN_ADDRESS $LISTEN_PORT $TARGET_ADDRESS $TARGET_PORT $LIMIT_BYTES_PER_SECOND $DELAY_SECONDS
97 changes: 52 additions & 45 deletions sloxy.c
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
#import <stdlib.h>
#import <stdio.h>
#import <strings.h>
#import <sys/types.h>
#import <sys/uio.h>
#import <sys/socket.h>
#import <sys/un.h>
#import <sys/select.h>
#import <sys/time.h>
#import <netdb.h>
#import <unistd.h>
#import <arpa/inet.h>
#import <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/select.h>
#include <sys/time.h>
#include <netdb.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <math.h>
#include <time.h>

struct forward_task {
int incoming;
Expand All @@ -26,73 +27,76 @@ double delay = 0.1;
int readwrite(int from, int to);

int main(int argc, char **argv) {

if (argc != 7) {
fprintf(stderr, "Usage: %s listen_addr listen_port destination_addr destination_port speed_limit delay\n", argv[0]);
exit(1);
}

char *listen_address = argv[1];
uint16_t listen_port = atoi(argv[2]);
char *target_address = argv[3];
uint16_t target_port = atoi(argv[4]);

speedlimit = atof(argv[5]);
delay = atof(argv[6]);

buffer_size = speedlimit;
if (buffer_size > 1000000) buffer_size = 1000000;
buffer = malloc(buffer_size);
buffer = malloc(buffer_size);

printf(" Delay: %gms\n", delay * 1000);
printf("Speed Limit: %gKB/s\n", speedlimit/1000 );
printf("Buffer Size: %d bytes\n", buffer_size );

printf("\n");

int listen_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (!listen_socket) {
perror("socket()");
exit(1);
}

struct sockaddr_in *listen_sockaddr = malloc(sizeof *listen_sockaddr);
bzero(listen_sockaddr, sizeof *listen_sockaddr);
listen_sockaddr->sin_family = AF_INET;
listen_sockaddr->sin_len = sizeof *listen_sockaddr;

//Ubuntu error: ‘struct sockaddr_in’ has no member named ‘sin_len’;
//listen_sockaddr->sin_len = sizeof *listen_sockaddr;

listen_sockaddr->sin_port = htons(listen_port);
if (INADDR_NONE == (listen_sockaddr->sin_addr.s_addr = inet_addr(listen_address))) {
fprintf(stderr, "listen_address is not a valid IPv4 address\n");
exit(1);
}

int yes = 1;
setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));

int bind_result = bind(listen_socket, (struct sockaddr *)listen_sockaddr, sizeof(*listen_sockaddr));
if (bind_result != 0) {
perror("bind()");
exit(1);
}
free(listen_sockaddr);

int listen_result = listen(listen_socket, 1);
if (listen_result) {
perror("listen()");
exit(1);
}


struct forward_task *fwtask = NULL;

int max_socket = listen_socket;

while (1) {
fd_set rfds;
FD_ZERO(&rfds);

FD_SET(listen_socket, &rfds);

{
struct forward_task *t = fwtask;
while (t) {
Expand All @@ -101,24 +105,24 @@ int main(int argc, char **argv) {
t = t->next;
}
}

if (!fwtask) {
printf("Listening on %s:%d\n", listen_address, listen_port);
}

int select_result = select(max_socket+1, &rfds, NULL, NULL, NULL);
if(select_result < 0)
{
perror("select()");
exit(1);
}

if (FD_ISSET(listen_socket, &rfds)) {
printf("Connecting to %s:%d\n", target_address, target_port);
struct forward_task **lastaddr = &fwtask;
while (*lastaddr) lastaddr = &((*lastaddr)->next);
struct forward_task *newTask = *lastaddr = calloc(sizeof(struct forward_task), 1);

newTask->incoming = accept(listen_socket, NULL, NULL);
if(newTask->incoming <= 0)
{
Expand All @@ -130,29 +134,32 @@ int main(int argc, char **argv) {
struct sockaddr_in *target_sockaddr = malloc(sizeof *target_sockaddr);
bzero(target_sockaddr, sizeof *target_sockaddr);
target_sockaddr->sin_family = AF_INET;
target_sockaddr->sin_len = sizeof *target_sockaddr;

//Ubuntu error: ‘struct sockaddr_in’ has no member named ‘sin_len’;
//target_sockaddr->sin_len = sizeof *target_sockaddr;

target_sockaddr->sin_port = htons(target_port);
if (INADDR_NONE == (target_sockaddr->sin_addr.s_addr = inet_addr(target_address))) {
fprintf(stderr, "destination_addr is not a valid IPv4 address\n");
exit(1);
}

newTask->outgoing = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (!newTask->outgoing) {
perror("socket()");
exit(1);
}
if (newTask->outgoing > max_socket) max_socket = newTask->outgoing;

int connect_result = connect(newTask->outgoing, (struct sockaddr *)target_sockaddr, sizeof(*target_sockaddr));
if (connect_result) {
perror("connect()");
exit(1);
}

printf("Connected.\n");
}

{
struct forward_task **taddr = &fwtask;
while (*taddr) {
Expand All @@ -163,7 +170,7 @@ int main(int argc, char **argv) {
}
if (!shouldClose && FD_ISSET(t->outgoing, &rfds)) {
shouldClose = readwrite(t->outgoing, t->incoming);

}
if (shouldClose) {
close(t->incoming);
Expand All @@ -188,15 +195,15 @@ int readwrite(int from, int to) {
perror("read()");
exit(1);
}

double wait_time = readbytes / speedlimit;
if (readbytes<buffer_size) wait_time += delay;

struct timespec tv = {0};
tv.tv_sec = floor(wait_time);
tv.tv_nsec = (wait_time-floor(wait_time)) * 1e9;
nanosleep(&tv, NULL);

char *writebuffer = buffer;
while (readbytes) {
int writtenbytes = write(to, writebuffer, readbytes);
Expand All @@ -208,4 +215,4 @@ int readwrite(int from, int to) {
readbytes -= writtenbytes;
}
return 0;
}
}