etc~ scripts and random things |
| git clone https://github.com/djnnvx/etc.git |
| log | files | refs | archive |
net_send.c (4512B)
1 /* 2 * net_send.c — TCP bridge for QEMU-backed AFL++ fuzzing. 3 * 4 * Reads the AFL++ test-case file (argv[1]), opens a TCP connection to 5 * host:port (argv[2]:argv[3]), sends the raw bytes to usbipd running inside 6 * the QEMU VM, then exits. 7 * 8 * Exit codes: 9 * 0 — sent OK (or server closed connection normally) 10 * 1 — ECONNRESET / SIGPIPE (daemon crashed → AFL++ marks as crash) 11 * 12 * Retries the connection 3× with 100 ms backoff to handle the brief window 13 * when usbipd is being restarted by the watchdog in /init. 14 * 15 * Usage: 16 * afl-fuzz -S worker -i corpus -o output -t 2000 -- \ 17 * ./net_send @@ 127.0.0.1 13240 18 * 19 * Compile: 20 * gcc -O2 -o net_send net_send.c 21 */ 22 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <stdint.h> 27 #include <errno.h> 28 #include <fcntl.h> 29 #include <unistd.h> 30 #include <signal.h> 31 #include <time.h> 32 #include <sys/socket.h> 33 #include <sys/stat.h> 34 #include <sys/time.h> 35 #include <netinet/in.h> 36 #include <netinet/tcp.h> 37 #include <arpa/inet.h> 38 39 #define MAX_INPUT (1 << 16) /* 64 KB — matches FUZZ_BUF_MAX */ 40 #define MAX_RETRIES 3 41 #define RETRY_MS 100 42 43 static void sleep_ms(int ms) 44 { 45 struct timespec ts = { .tv_sec = ms / 1000, .tv_nsec = (ms % 1000) * 1000000L }; 46 nanosleep(&ts, NULL); 47 } 48 49 static uint8_t ibuf[MAX_INPUT]; 50 51 int main(int argc, char *argv[]) 52 { 53 if (argc < 4) { 54 fprintf(stderr, "usage: %s <input_file> <host> <port>\n", argv[0]); 55 return 1; 56 } 57 58 const char *path = argv[1]; 59 const char *host = argv[2]; 60 int port = atoi(argv[3]); 61 62 /* ── read input file ──────────────────────────────────────────── */ 63 int fd = open(path, O_RDONLY); 64 if (fd < 0) { 65 perror("open"); 66 return 1; 67 } 68 ssize_t len = read(fd, ibuf, sizeof(ibuf)); 69 close(fd); 70 if (len <= 0) 71 return 0; /* empty input — nothing to send */ 72 73 /* ── ignore SIGPIPE (treat as connection error, not fatal signal) */ 74 signal(SIGPIPE, SIG_IGN); 75 76 /* ── connect with retries ─────────────────────────────────────── */ 77 struct sockaddr_in addr; 78 memset(&addr, 0, sizeof(addr)); 79 addr.sin_family = AF_INET; 80 addr.sin_port = htons((uint16_t)port); 81 if (inet_pton(AF_INET, host, &addr.sin_addr) != 1) { 82 fprintf(stderr, "bad host: %s\n", host); 83 return 1; 84 } 85 86 int sock = -1; 87 for (int i = 0; i < MAX_RETRIES; i++) { 88 sock = socket(AF_INET, SOCK_STREAM, 0); 89 if (sock < 0) { 90 perror("socket"); 91 return 1; 92 } 93 94 /* TCP_NODELAY: no nagle buffering — send immediately */ 95 int one = 1; 96 setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)); 97 98 /* recv timeout: 2 s — prevents blocking forever when usbipd 99 * reads the fuzz bytes but never replies (which is normal behaviour 100 * for most malformed packets). Without this every calibration seed 101 * times out and AFL++ aborts with "All test cases time out". */ 102 struct timeval tv = { .tv_sec = 2, .tv_usec = 0 }; /* 2 s */ 103 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); 104 105 if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) 106 break; 107 108 close(sock); 109 sock = -1; 110 111 if (errno == ECONNREFUSED) { 112 /* usbipd is restarting; wait and retry */ 113 sleep_ms(RETRY_MS); 114 continue; 115 } 116 /* Any other error is fatal */ 117 perror("connect"); 118 return 1; 119 } 120 121 if (sock < 0) { 122 /* Could not connect after retries — treat as timeout, not crash */ 123 return 0; 124 } 125 126 /* ── send the fuzz payload ────────────────────────────────────── */ 127 ssize_t sent = send(sock, ibuf, (size_t)len, MSG_NOSIGNAL); 128 129 if (sent < 0) { 130 close(sock); 131 if (errno == ECONNRESET || errno == EPIPE) 132 return 1; /* daemon crashed */ 133 return 0; 134 } 135 136 /* 137 * Wait briefly for a response (or connection close). 138 * If we get ECONNRESET the daemon crashed → return 1. 139 */ 140 uint8_t rbuf[256]; 141 ssize_t r = recv(sock, rbuf, sizeof(rbuf), 0); 142 close(sock); 143 144 if (r < 0 && (errno == ECONNRESET || errno == EPIPE)) 145 return 1; 146 147 return 0; 148 }