Skip to content
Closed
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
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*.o
*.out
out
78 changes: 78 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
name: Docker Image CI

on:
push:
branches: [ master, 'claude/**' ]
tags: [ v* ]
pull_request:

jobs:
compile:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Build microsocks
run: make

- name: Build with SOMARK
run: make clean && make CFLAGS="-DSOMARK"

- name: Test version flag
run: ./microsocks -V

docker:
runs-on: ubuntu-latest
needs: compile
if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/v')
permissions:
contents: read
packages: write

steps:
- uses: actions/checkout@v4

- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Docker meta
id: docker-meta
uses: docker/metadata-action@v5
with:
images: |
ghcr.io/${{ github.repository }}
tags: |
type=sha,prefix=
type=ref,event=tag

- name: Build and push
uses: docker/build-push-action@v6
with:
push: true
platforms: linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8,linux/ppc64le,linux/riscv64,linux/s390x
tags: ${{ steps.docker-meta.outputs.tags }}
labels: ${{ steps.docker-meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
outputs: type=local,dest=${{ github.workspace }}/out

- name: Get metadata for artifact naming
id: meta
run: |
echo "commit-hash=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
echo "repo-name=${GITHUB_REPOSITORY#*/}" >> $GITHUB_OUTPUT

- name: Upload built binaries
uses: actions/upload-artifact@v4
with:
name: ${{ steps.meta.outputs.repo-name }}-${{ steps.meta.outputs.commit-hash }}
path: ${{ github.workspace }}/out
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
*.o
*.out
out
microsocks

11 changes: 11 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM alpine AS builder
RUN apk add --no-cache make gcc libc-dev
WORKDIR /app
ENV LDFLAGS=-static
COPY . .
RUN make

FROM scratch
LABEL org.opencontainers.image.licenses=MIT
COPY --from=builder /app/microsocks /
ENTRYPOINT ["/microsocks"]
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ prefix = /usr/local
bindir = $(prefix)/bin

PROG = microsocks
SRCS = sockssrv.c server.c sblist.c sblist_delete.c
SRCS = sockssrv.c server.c sblist.c sblist_delete.c bind2device.c
OBJS = $(SRCS:.c=.o)

LIBS = -lpthread
Expand Down
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ libc is not even 50 KB. that's easily usable even on the cheapest routers.
command line options
--------------------

microsocks -1 -q -i listenip -p port -u user -P passw -b bindaddr -w wl
microsocks -1 -q -i listenip -p port -u user -P passw -b bindaddr -B bind2device -w wl

all arguments are optional.
by default listenip is 0.0.0.0 and port 1080.
Expand Down Expand Up @@ -69,6 +69,29 @@ Supported SOCKS5 Features
- IPv4, IPv6, DNS
- TCP (no UDP at this time)

compile time options
--------------------

make CFLAGS=-DSOMARK

microsocks can be compiled with SO_MARK support on linux 2.6.25+. This
enables 'marking' of outgoing packets for use with policy-based routing
which allows to route packets through a non-default interface. E.g.:

ip rule add fwmark 1000 table 200
ip route add default dev tun1 table 200
microsocks -m 1000

will route all connections through device `tun1`

Docker container
----------------
You can run microsocks in a docker container:

docker run --init -d -p 7777:1080 ghcr.io/rofl0r/microsocks

Replace 7777 with the port microsocks will be accessible on.

Troubleshooting
---------------

Expand Down
87 changes: 87 additions & 0 deletions UPSTREAM_PR_ANALYSIS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Upstream microsocks Open Pull Requests Analysis

> Source: https://github.com/rofl0r/microsocks/pulls
> Date: 2026-02-01
> Total open PRs: 11

## Features (8 PRs)

### PR #98 — Add Dockerfile and GitHub Actions workflow
- **Author:** meanwhile131 | **Created:** 2026-01-06
- **URL:** https://github.com/rofl0r/microsocks/pull/98
- **Files:** `.dockerignore`, `.github/workflows/build.yml`, `.gitignore`, `Dockerfile`, `README.md` (+85 lines)
- **Summary:** Adds a multi-stage Dockerfile (Alpine builder to scratch runtime) and a CI/CD workflow that builds Docker images for 8 architectures (386, amd64, arm/v6, arm/v7, arm64/v8, ppc64le, riscv64, s390x). Images are published to GHCR and statically-built binaries are uploaded as artifacts.

### PR #96 — Add SOCKS5 forwarding rules support
- **Author:** lwb1978 | **Created:** 2025-12-22
- **URL:** https://github.com/rofl0r/microsocks/pull/96
- **Files:** `sockssrv.c` (+305/-7 lines)
- **Summary:** Builds on PR #93's concept. Adds a `-f` flag for forwarding rules that selectively route matching destinations through upstream SOCKS5 proxies with optional authentication. Includes upstream handshake logic, socket timeouts (5s), and `-V` version flag.

### PR #95 — Allow building with Windows + MinGW (Draft)
- **Author:** ccuser44 | **Created:** 2025-12-07
- **URL:** https://github.com/rofl0r/microsocks/pull/95
- **Files:** `dprintf.c` (new), `server.h`, `sockssrv.c`, `wsa2unix.h` (new) (+136/-7 lines)
- **Summary:** Adds Windows cross-compilation support via MinGW. Provides a `dprintf()` implementation, a `wsa2unix.h` compatibility header mapping Winsock error codes to Unix equivalents, conditional includes for `winsock2.h`/`ws2tcpip.h`, and replaces `poll()` with `WSAPoll()` on Windows. Author notes it is "mostly functional" with known `dprintf` caveats.

### PR #93 — Forwarding rules
- **Author:** ohwgiles | **Created:** 2025-10-04
- **URL:** https://github.com/rofl0r/microsocks/pull/93
- **Files:** `sockssrv.c` (+126/-3 lines)
- **Summary:** Original implementation of selective forwarding rules using the syntax `match_name:match_port,[user:pass@]upstream_name:upstream_port,remote_name:remote_port`. Allows microsocks to act as a gateway to remote private networks via upstream SOCKS5 servers. PR #96 is a more complete evolution of this work.

### PR #79 — Add `-B` bind device option
- **Author:** peppergrayxyz | **Created:** 2024-09-28
- **URL:** https://github.com/rofl0r/microsocks/pull/79
- **Files:** `Makefile`, `README.md`, `bind2device.c` (new), `bind2device.h` (new), `sockssrv.c` (+73/-4 lines)
- **Summary:** Adds a `-B` flag to bind outgoing sockets to a specific network interface. Uses `SO_BINDTODEVICE` on Linux and `IP_BOUND_IF`/`IPV6_BOUND_IF` on BSD/macOS. Includes a no-op stub for unsupported platforms. Follows up on issue #29.

### PR #64 — Add `-t` option for idle exit timeout
- **Author:** chetan-reddy | **Created:** 2023-08-18
- **URL:** https://github.com/rofl0r/microsocks/pull/64
- **Files:** `sockssrv.c` (+41/-3 lines)
- **Summary:** Adds a `-t` flag specifying an idle exit timeout in seconds. When no connections arrive within the timeout period and no active threads exist, the server exits automatically. Uses non-blocking sockets via `fcntl()` and `poll()`. Useful for on-demand launches in resource-constrained environments. Tested on Linux and macOS.

### PR #38 — Enable SO_MARK support on Linux via compile-time flag
- **Author:** grandrew | **Created:** 2021-06-14
- **URL:** https://github.com/rofl0r/microsocks/pull/38
- **Files:** `README.md`, `sockssrv.c` (+39 lines)
- **Summary:** Adds a `-m <mark_id>` option to mark outgoing packets with Linux `SO_MARK` for policy-based routing. Enabled via a compile-time `SOMARK` flag. Includes README documentation with example commands showing how to route connections through specific network interfaces (e.g., `tun1`).

### PR #29 — Allow binding to another (non-default) interface
- **Author:** tahajahangir | **Created:** 2020-09-25
- **URL:** https://github.com/rofl0r/microsocks/pull/29
- **Files:** `README.md`, `sockssrv.c` (+9/-2 lines)
- **Summary:** Adds a `-B` flag using `SO_BINDTODEVICE` to bind outgoing sockets to a specific network interface. A simpler, Linux-only predecessor to PR #79, which adds cross-platform support for the same feature.

## Improvements (2 PRs)

### PR #90 — Add timestamps to log output
- **Author:** ZsBT | **Created:** 2025-09-04
- **URL:** https://github.com/rofl0r/microsocks/pull/90
- **Files:** `sockssrv.c` (+17/-1 lines)
- **Summary:** Converts the `dolog` macro into a `static inline` function that prepends `[YYYY-MM-DD HH:MM:SS]` timestamps using `localtime_r()` and `vdprintf()`. Also adds a startup log message displaying the listening address and port.

### PR #70 — Print timestamps in logs
- **Author:** Xenapte | **Created:** 2023-12-19
- **URL:** https://github.com/rofl0r/microsocks/pull/70
- **Files:** `sockssrv.c` (+6/-1 lines)
- **Summary:** Adds a `LOGTS()` macro that prepends `[MM-DD HH:MM:SS]` timestamps to log output using `strftime()` and `localtime_r()`. A lighter-weight predecessor to PR #90 which uses a different timestamp format (`YYYY-MM-DD`) and a different implementation approach.

## Fixes (1 PR)

### PR #86 — Fix minor nits in the manual page
- **Author:** ppentchev | **Created:** 2025-02-14
- **URL:** https://github.com/rofl0r/microsocks/pull/86
- **Files:** `microsocks.1` (+22/-13 lines)
- **Summary:** Fixes formatting issues in the man page: removes a stray `.Oc` bracket, improves grammar/punctuation in option descriptions (`-i`, `-w`), and applies the FreeBSD documentation convention of starting each sentence on a new line.

## Notable Observations

- **PR #93 and #96 overlap:** Both implement SOCKS5 forwarding rules. PR #96 by lwb1978 explicitly builds on PR #93 by ohwgiles and is more comprehensive (+305 lines vs +126 lines), adding authentication support and socket timeouts.
- **PR #29 and #79 overlap:** Both add `-B` bind-to-device support. PR #29 (2020) is Linux-only using `SO_BINDTODEVICE`; PR #79 (2024) is cross-platform, adding macOS support via `IP_BOUND_IF`/`IPV6_BOUND_IF`.
- **PR #70 and #90 overlap:** Both add timestamp prefixes to log output. PR #70 (2023) uses a macro with `[MM-DD HH:MM:SS]` format; PR #90 (2025) uses a function with `[YYYY-MM-DD HH:MM:SS]` format and also adds a startup message.
- **PR #95 (Windows/MinGW)** is a Draft PR, marked by its author as "mostly functional" with known caveats around `dprintf`.
- **PR #29 is the oldest** open PR (Sept 2020, over 5 years old).
- **PR #86 (man page fixes)** is the most straightforward and lowest-risk change.
- **Three pairs of related PRs** exist (#29/#79, #70/#90, #93/#96), where later PRs supersede or extend earlier ones.
55 changes: 55 additions & 0 deletions bind2device.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200809L

#define _GNU_SOURCE
#define _DARWIN_C_SOURCE

#include <errno.h>
#include <netinet/in.h>
#include <net/if.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>

#include "bind2device.h"

#if (defined(IP_BOUND_IF) || defined(IPV6_BOUND_IF))

int bind2device(int sockfd, int socket_family, const char *device)
{
int ifindex = if_nametoindex(device);
if (ifindex == 0)
return -1;
switch (socket_family)
{
#if defined(IPV6_BOUND_IF)
case AF_INET6:
return setsockopt(sockfd, IPPROTO_IPV6, IPV6_BOUND_IF, &ifindex, sizeof(ifindex));
#endif
#if defined(IP_BOUND_IF)
case AF_INET:
return setsockopt(sockfd, IPPROTO_IP, IP_BOUND_IF, &ifindex, sizeof(ifindex));
#endif
default: /* can't bind to interface for selected socket_family: operation not supported on socket */
errno = EOPNOTSUPP;
return -1;
}
}

#elif defined(SO_BINDTODEVICE)

int bind2device(int sockfd, int socket_family, const char *device)
{
return setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device) + 1);
}

#else
#pragma message "Platform does not support bind2device, generating stub."

int bind2device(int sockfd, int socket_family, const char *device)
{
errno = ENOSYS; /* unsupported platform: not implemented */
return -1;
}

#endif
6 changes: 6 additions & 0 deletions bind2device.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#ifndef BIND2DEVICE_H
#define BIND2DEVICE_H

int bind2device(int sockfd, int socket_family, const char* device);

#endif
36 changes: 36 additions & 0 deletions dprintf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>

int dprintf(int fd, const char *fmt, ...)
{
char stack_buffer[256];
char *buf = stack_buffer;
int len = sizeof(stack_buffer);
va_list ap;
int ret = -1;

va_start(ap, fmt);

/* try to just print it to stack if possible */
ret = vsnprintf(buf, len, fmt, ap);

if (ret >= len) {
/* dynamically allocate buffer */
len = ret + 1;
buf = malloc(len);
if (buf) {
va_end(ap);
va_start(ap, fmt);
ret = vsnprintf(buf, len, fmt, ap);
} else {
ret = -1;
}
}
if (ret >= 0 && (write(fd, buf, ret) != ret)) {
ret = -1;
}
if (buf != stack_buffer) free(buf);
va_end(ap);
return ret;
}
Loading