blob: c091f10aab478bc9e760b517d8e3288e1b767ea3 [file] [log] [blame]
Willy Tarreau9557bac2016-11-12 17:53:16 +01001/*
2 * TCP client and server for bug hunting
3 *
4 * Copyright (C) 2016 Willy Tarreau <w@1wt.eu>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 * OTHER DEALINGS IN THE SOFTWARE.
25 */
26
Willy Tarreaue5f72b82022-11-25 16:15:20 +010027#define _GNU_SOURCE // for POLLRDHUP
Willy Tarreau84393aa2016-11-12 11:29:46 +010028#include <sys/resource.h>
29#include <sys/select.h>
30#include <sys/types.h>
31#include <sys/socket.h>
32#include <sys/stat.h>
33#include <sys/time.h>
34#include <sys/ioctl.h>
Willy Tarreaub714e112023-01-11 10:54:59 +010035#include <sys/un.h>
Willy Tarreau95a6b782016-11-12 13:25:53 +010036#include <sys/wait.h>
Willy Tarreaud8a6a322022-11-25 17:05:05 +010037
38#ifdef __linux__
39#include <sys/epoll.h>
40#endif
41
Willy Tarreau84393aa2016-11-12 11:29:46 +010042#include <arpa/inet.h>
43#include <netinet/in.h>
44#include <netinet/tcp.h>
45
46#include <ctype.h>
47#include <errno.h>
48#include <fcntl.h>
Willy Tarreau5cd60672016-12-16 08:02:21 +010049#include <limits.h>
Willy Tarreau84393aa2016-11-12 11:29:46 +010050#include <netdb.h>
51#include <poll.h>
52#include <signal.h>
53#include <stdarg.h>
54#include <stdio.h>
55#include <stdlib.h>
56#include <string.h>
57#include <time.h>
58#include <unistd.h>
59
Willy Tarreaue5f72b82022-11-25 16:15:20 +010060/* for OSes which don't have it */
61#ifndef POLLRDHUP
62#define POLLRDHUP 0
63#endif
64
Willy Tarreau0c0c0a62017-03-14 14:36:26 +010065#ifndef MSG_MORE
66#define MSG_MORE 0
67#endif
Willy Tarreau84393aa2016-11-12 11:29:46 +010068
69struct err_msg {
70 int size;
71 int len;
72 char msg[0];
73};
74
75const int zero = 0;
76const int one = 1;
77const struct linger nolinger = { .l_onoff = 1, .l_linger = 0 };
78
79#define TRASH_SIZE 65536
80static char trash[TRASH_SIZE];
81
Willy Tarreau95a6b782016-11-12 13:25:53 +010082volatile int nbproc = 0;
Willy Tarreau869c7592016-11-12 17:50:57 +010083static struct timeval start_time;
84static int showtime;
85static int verbose;
Willy Tarreaud8a6a322022-11-25 17:05:05 +010086static int use_epoll;
Willy Tarreau869c7592016-11-12 17:50:57 +010087static int pid;
Willy Tarreau2d7cd3e2022-06-07 12:09:55 +020088static int sock_type = SOCK_STREAM;
89static int sock_proto = IPPROTO_TCP;
Willy Tarreau869c7592016-11-12 17:50:57 +010090
Willy Tarreau95a6b782016-11-12 13:25:53 +010091
Willy Tarreau84393aa2016-11-12 11:29:46 +010092/* display the message and exit with the code */
93__attribute__((noreturn)) void die(int code, const char *format, ...)
94{
95 va_list args;
96
Willy Tarreau1973e812016-11-12 18:45:42 +010097 if (format) {
98 va_start(args, format);
99 vfprintf(stderr, format, args);
100 va_end(args);
101 }
Willy Tarreau84393aa2016-11-12 11:29:46 +0100102 exit(code);
103}
104
105/* display the usage message and exit with the code */
106__attribute__((noreturn)) void usage(int code, const char *arg0)
107{
Willy Tarreau9557bac2016-11-12 17:53:16 +0100108 die(code,
109 "Usage : %s [options]* [<ip>:]port [<action>*]\n"
110 "\n"
111 "options :\n"
112 " -v : verbose\n"
Willy Tarreau2d7cd3e2022-06-07 12:09:55 +0200113 " -u : use UDP instead of TCP (limited)\n"
Willy Tarreaub714e112023-01-11 10:54:59 +0100114 " -U : use UNIX instead of TCP (limited, addr must have one '/')\n"
Willy Tarreau9557bac2016-11-12 17:53:16 +0100115 " -t|-tt|-ttt : show time (msec / relative / absolute)\n"
Willy Tarreaud8a6a322022-11-25 17:05:05 +0100116 " -e : use epoll instead of poll on Linux\n"
Willy Tarreau9557bac2016-11-12 17:53:16 +0100117 "actions :\n"
Willy Tarreau98028c82022-06-07 11:29:16 +0200118 " A[<count>] : Accepts <count> incoming sockets and closes count-1\n"
119 " Note: fd=accept(fd)\n"
Willy Tarreaud4933312022-06-07 12:03:48 +0200120 " B[[ip]:port] : Bind a new socket to ip:port or default one if unspecified.\n"
121 " Note: fd=socket,bind(fd)\n"
Willy Tarreau542bf0a2022-06-07 11:46:57 +0200122 " C[[ip]:port] : Connects to ip:port or default ones if unspecified.\n"
Willy Tarreau9557bac2016-11-12 17:53:16 +0100123 " Note: fd=socket,connect(fd)\n"
Willy Tarreau2e065cb2020-10-14 08:09:48 +0200124 " D : Disconnect (connect to AF_UNSPEC)\n"
Willy Tarreau98028c82022-06-07 11:29:16 +0200125 " E[<size>] : Echo this amount of bytes. 0=infinite. unset=any amount.\n"
126 " F : FIN : shutdown(SHUT_WR)\n"
127 " G : disable lingering\n"
128 " I : wait for Input data to be present (POLLIN)\n"
Willy Tarreau29cc11c2016-11-12 18:54:20 +0100129 " J : Jump back to oldest post-fork/post-accept action\n"
Willy Tarreaubcd817e2017-03-14 14:44:06 +0100130 " K : kill the connection and go on with next operation\n"
Willy Tarreau98028c82022-06-07 11:29:16 +0200131 " L[<backlog>] : Listens to ip:port and optionally sets backlog\n"
132 " Note: fd=socket,bind(fd),listen(fd)\n"
133 " N<max> : fork New process, limited to <max> concurrent (default 1)\n"
134 " O : wait for Output queue to be empty (POLLOUT + TIOCOUTQ)\n"
135 " P[<time>] : Pause for <time> ms (100 by default)\n"
Willy Tarreau9557bac2016-11-12 17:53:16 +0100136 " Q : disable TCP Quick-ack\n"
137 " R[<size>] : Read this amount of bytes. 0=infinite. unset=any amount.\n"
138 " S[<size>] : Send this amount of bytes. 0=infinite. unset=any amount.\n"
Willy Tarreau59623e02016-11-12 18:25:45 +0100139 " S:<string> : Send this exact string. \\r, \\n, \\t, \\\\ supported.\n"
Willy Tarreau98028c82022-06-07 11:29:16 +0200140 " T : set TCP_NODELAY\n"
Willy Tarreau9557bac2016-11-12 17:53:16 +0100141 " W[<time>] : Wait for any event on the socket, maximum <time> ms\n"
Willy Tarreaub7a6d0d2017-05-02 22:14:59 +0200142 " X[i|o|e]* ** : execvp() next args passing socket as stdin/stdout/stderr.\n"
143 " If i/o/e present, only stdin/out/err are mapped to socket.\n"
Willy Tarreau98028c82022-06-07 11:29:16 +0200144 " r : shutr : shutdown(SHUT_RD) (pauses a listener or ends recv)\n"
Willy Tarreau9557bac2016-11-12 17:53:16 +0100145 "\n"
146 "It's important to note that a single FD is used at once and that Accept\n"
147 "replaces the listening FD with the accepted one. Thus always do it after\n"
148 "a fork if other connections have to be accepted.\n"
149 "\n"
150 "After a fork, we loop back to the beginning and silently skip L/C if the\n"
151 "main socket already exists.\n"
152 "\n"
153 "Example dummy HTTP request drain server :\n"
154 " tcploop 8001 L W N20 A R S10 [ F K ]\n"
155 "\n"
156 "Example large bandwidth HTTP request drain server :\n"
157 " tcploop 8001 L W N20 A R S0 [ F K ]\n"
158 "\n"
159 "Example TCP client with pauses at each step :\n"
160 " tcploop 8001 C T W P100 S10 O P100 R S10 O R G K\n"
Willy Tarreaub7a6d0d2017-05-02 22:14:59 +0200161 "\n"
162 "Simple chargen server :\n"
163 " tcploop 8001 L A Xo cat /dev/zero\n"
164 "\n"
165 "Simple telnet server :\n"
166 " tcploop 8001 L W N A X /usr/sbin/in.telnetd\n"
Willy Tarreau9557bac2016-11-12 17:53:16 +0100167 "", arg0);
Willy Tarreau84393aa2016-11-12 11:29:46 +0100168}
169
Willy Tarreau869c7592016-11-12 17:50:57 +0100170void dolog(const char *format, ...)
171{
172 struct timeval date, tv;
173 int delay;
174 va_list args;
175
176 if (!verbose)
177 return;
178
179 if (showtime) {
180 gettimeofday(&date, NULL);
181 switch (showtime) {
182 case 1: // [msec] relative
183 delay = (date.tv_sec - start_time.tv_sec) * 1000000 + date.tv_usec - start_time.tv_usec;
184 fprintf(stderr, "[%d] ", delay / 1000);
185 break;
186 case 2: // [sec.usec] relative
187 tv.tv_usec = date.tv_usec - start_time.tv_usec;
188 tv.tv_sec = date.tv_sec - start_time.tv_sec;
189 if ((signed)tv.tv_sec > 0) {
190 if ((signed)tv.tv_usec < 0) {
191 tv.tv_usec += 1000000;
192 tv.tv_sec--;
193 }
194 } else if (tv.tv_sec == 0) {
195 if ((signed)tv.tv_usec < 0)
196 tv.tv_usec = 0;
197 } else {
198 tv.tv_sec = 0;
199 tv.tv_usec = 0;
200 }
Willy Tarreau752cc492017-03-14 14:37:13 +0100201 fprintf(stderr, "[%d.%06d] ", (int)tv.tv_sec, (int)tv.tv_usec);
Willy Tarreau869c7592016-11-12 17:50:57 +0100202 break;
203 default: // [sec.usec] absolute
Willy Tarreau752cc492017-03-14 14:37:13 +0100204 fprintf(stderr, "[%d.%06d] ", (int)date.tv_sec, (int)date.tv_usec);
Willy Tarreau869c7592016-11-12 17:50:57 +0100205 break;
206 }
207 }
208
209 fprintf(stderr, "%5d ", pid);
210
211 va_start(args, format);
212 vfprintf(stderr, format, args);
213 va_end(args);
214}
215
Willy Tarreau59623e02016-11-12 18:25:45 +0100216/* convert '\n', '\t', '\r', '\\' to their respective characters */
217int unescape(char *out, int size, const char *in)
218{
219 int len;
220
221 for (len = 0; len < size && *in; in++, out++, len++) {
222 if (*in == '\\') {
223 switch (in[1]) {
224 case 'n' : *out = '\n'; in++; continue;
225 case 't' : *out = '\t'; in++; continue;
226 case 'r' : *out = '\r'; in++; continue;
227 case '\\' : *out = '\\'; in++; continue;
228 default : break;
229 }
230 }
231 *out = *in;
232 }
233 return len;
234}
235
Willy Tarreau84393aa2016-11-12 11:29:46 +0100236struct err_msg *alloc_err_msg(int size)
237{
238 struct err_msg *err;
239
240 err = malloc(sizeof(*err) + size);
241 if (err) {
242 err->len = 0;
243 err->size = size;
244 }
245 return err;
246}
247
Willy Tarreau95a6b782016-11-12 13:25:53 +0100248void sig_handler(int sig)
249{
250 if (sig == SIGCHLD) {
251 while (waitpid(-1, NULL, WNOHANG) > 0)
252 __sync_sub_and_fetch(&nbproc, 1);
253 }
254}
Willy Tarreau84393aa2016-11-12 11:29:46 +0100255
256/* converts str in the form [[<ipv4>|<ipv6>|<hostname>]:]port to struct sockaddr_storage.
257 * Returns < 0 with err set in case of error.
258 */
Willy Tarreau542bf0a2022-06-07 11:46:57 +0200259int addr_to_ss(const char *str, struct sockaddr_storage *ss, struct err_msg *err)
Willy Tarreau84393aa2016-11-12 11:29:46 +0100260{
261 char *port_str;
262 int port;
263
264 memset(ss, 0, sizeof(*ss));
265
Willy Tarreaub714e112023-01-11 10:54:59 +0100266 /* if there's a slash it's a unix socket */
267 if (strchr(str, '/')) {
268 ((struct sockaddr_un *)ss)->sun_family = AF_UNIX;
269 strncpy(((struct sockaddr_un *)ss)->sun_path, str, sizeof(((struct sockaddr_un *)ss)->sun_path) - 1);
270 ((struct sockaddr_un *)ss)->sun_path[sizeof(((struct sockaddr_un *)ss)->sun_path)] = 0;
271 return 0;
272 }
273
Willy Tarreau84393aa2016-11-12 11:29:46 +0100274 /* look for the addr/port delimiter, it's the last colon. If there's no
275 * colon, it's 0:<port>.
276 */
277 if ((port_str = strrchr(str, ':')) == NULL) {
278 port = atoi(str);
Willy Tarreaucb284c72022-06-07 12:06:04 +0200279 if (port < 0 || port > 65535) {
Willy Tarreau84393aa2016-11-12 11:29:46 +0100280 err->len = snprintf(err->msg, err->size, "Missing/invalid port number: '%s'\n", str);
281 return -1;
282 }
283
284 ss->ss_family = AF_INET;
285 ((struct sockaddr_in *)ss)->sin_port = htons(port);
286 ((struct sockaddr_in *)ss)->sin_addr.s_addr = INADDR_ANY;
287 return 0;
288 }
289
290 *port_str++ = 0;
291
292 if (strrchr(str, ':') != NULL) {
293 /* IPv6 address contains ':' */
294 ss->ss_family = AF_INET6;
295 ((struct sockaddr_in6 *)ss)->sin6_port = htons(atoi(port_str));
296
297 if (!inet_pton(ss->ss_family, str, &((struct sockaddr_in6 *)ss)->sin6_addr)) {
298 err->len = snprintf(err->msg, err->size, "Invalid server address: '%s'\n", str);
299 return -1;
300 }
301 }
302 else {
303 ss->ss_family = AF_INET;
304 ((struct sockaddr_in *)ss)->sin_port = htons(atoi(port_str));
305
306 if (*str == '*' || *str == '\0') { /* INADDR_ANY */
307 ((struct sockaddr_in *)ss)->sin_addr.s_addr = INADDR_ANY;
308 return 0;
309 }
310
311 if (!inet_pton(ss->ss_family, str, &((struct sockaddr_in *)ss)->sin_addr)) {
312 struct hostent *he = gethostbyname(str);
313
314 if (he == NULL) {
315 err->len = snprintf(err->msg, err->size, "Invalid server name: '%s'\n", str);
316 return -1;
317 }
318 ((struct sockaddr_in *)ss)->sin_addr = *(struct in_addr *) *(he->h_addr_list);
319 }
320 }
321
322 return 0;
323}
324
Willy Tarreaue5f72b82022-11-25 16:15:20 +0100325/* waits up to <ms> milliseconds on fd <fd> for events <events> (POLLIN|POLLRDHUP|POLLOUT).
Willy Tarreaufd0974c2022-11-25 17:04:05 +0100326 * returns poll's status.
Willy Tarreau84393aa2016-11-12 11:29:46 +0100327 */
Willy Tarreau45c27792022-11-25 16:05:46 +0100328int wait_on_fd(int fd, int events, int ms)
Willy Tarreau84393aa2016-11-12 11:29:46 +0100329{
330 struct pollfd pollfd;
331 int ret;
332
Willy Tarreaud8a6a322022-11-25 17:05:05 +0100333#ifdef __linux__
334 while (use_epoll) {
335 struct epoll_event evt;
336 static int epoll_fd = -1;
337
338 if (epoll_fd == -1)
339 epoll_fd = epoll_create(1024);
340 if (epoll_fd == -1)
341 break;
342 evt.events = ((events & POLLIN) ? EPOLLIN : 0) |
343 ((events & POLLOUT) ? EPOLLOUT : 0) |
344 ((events & POLLRDHUP) ? EPOLLRDHUP : 0);
345 evt.data.fd = fd;
346 epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &evt);
347
348 do {
349 ret = epoll_wait(epoll_fd, &evt, 1, ms);
350 } while (ret == -1 && errno == EINTR);
351
352 evt.data.fd = fd;
353 epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &evt);
354 return ret;
355 }
356#endif
357
Willy Tarreau84393aa2016-11-12 11:29:46 +0100358 do {
359 pollfd.fd = fd;
360 pollfd.events = events;
Willy Tarreau45c27792022-11-25 16:05:46 +0100361 ret = poll(&pollfd, 1, ms);
Willy Tarreau84393aa2016-11-12 11:29:46 +0100362 } while (ret == -1 && errno == EINTR);
363
364 return ret;
365}
366
367int tcp_set_nodelay(int sock, const char *arg)
368{
Willy Tarreau4bfc6632021-03-31 08:45:47 +0200369 return setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
Willy Tarreau84393aa2016-11-12 11:29:46 +0100370}
371
372int tcp_set_nolinger(int sock, const char *arg)
373{
374 return setsockopt(sock, SOL_SOCKET, SO_LINGER, (struct linger *) &nolinger, sizeof(struct linger));
375}
376
377int tcp_set_noquickack(int sock, const char *arg)
378{
Willy Tarreau0c0c0a62017-03-14 14:36:26 +0100379#ifdef TCP_QUICKACK
Willy Tarreau84393aa2016-11-12 11:29:46 +0100380 /* warning: do not use during connect if nothing is to be sent! */
Willy Tarreau4bfc6632021-03-31 08:45:47 +0200381 return setsockopt(sock, IPPROTO_TCP, TCP_QUICKACK, &zero, sizeof(zero));
Willy Tarreau0c0c0a62017-03-14 14:36:26 +0100382#else
383 return 0;
384#endif
Willy Tarreau84393aa2016-11-12 11:29:46 +0100385}
386
Willy Tarreauff13dad2022-06-07 11:55:45 +0200387/* Create a new TCP socket for either listening or connecting */
Willy Tarreaub714e112023-01-11 10:54:59 +0100388int tcp_socket(sa_family_t fam)
Willy Tarreau84393aa2016-11-12 11:29:46 +0100389{
390 int sock;
Willy Tarreauff13dad2022-06-07 11:55:45 +0200391
Willy Tarreaub714e112023-01-11 10:54:59 +0100392 sock = socket(fam, sock_type, sock_proto);
Willy Tarreauff13dad2022-06-07 11:55:45 +0200393 if (sock < 0) {
394 perror("socket()");
395 return -1;
396 }
397
398 return sock;
399}
400
Willy Tarreaud4933312022-06-07 12:03:48 +0200401/* Try to bind to local address <sa>. Return the fd or -1 in case of error.
402 * Supports being passed NULL for arg if none has to be passed.
403 */
404int tcp_bind(int sock, const struct sockaddr_storage *sa, const char *arg)
Willy Tarreauff13dad2022-06-07 11:55:45 +0200405{
Willy Tarreaud4933312022-06-07 12:03:48 +0200406 struct sockaddr_storage conn_addr;
407
408 if (arg && arg[1]) {
409 struct err_msg err;
410
411 if (addr_to_ss(arg + 1, &conn_addr, &err) < 0)
412 die(1, "%s\n", err.msg);
413 sa = &conn_addr;
414 }
415
Willy Tarreau84393aa2016-11-12 11:29:46 +0100416
Willy Tarreauff13dad2022-06-07 11:55:45 +0200417 if (sock < 0) {
Willy Tarreaub714e112023-01-11 10:54:59 +0100418 sock = tcp_socket(sa->ss_family);
Willy Tarreauff13dad2022-06-07 11:55:45 +0200419 if (sock < 0)
420 return sock;
421 }
422
Willy Tarreau84393aa2016-11-12 11:29:46 +0100423 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) == -1) {
424 perror("setsockopt(SO_REUSEADDR)");
425 goto fail;
426 }
427
428#ifdef SO_REUSEPORT
429 if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (char *) &one, sizeof(one)) == -1) {
430 perror("setsockopt(SO_REUSEPORT)");
431 goto fail;
432 }
433#endif
434 if (bind(sock, (struct sockaddr *)sa, sa->ss_family == AF_INET6 ?
435 sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) == -1) {
436 perror("bind");
437 goto fail;
438 }
439
Willy Tarreaud4933312022-06-07 12:03:48 +0200440 return sock;
441 fail:
442 close(sock);
443 return -1;
444}
445
446/* Try to listen to address <sa>. Return the fd or -1 in case of error */
447int tcp_listen(int sock, const struct sockaddr_storage *sa, const char *arg)
448{
449 int backlog;
450
451 if (sock < 0) {
452 sock = tcp_bind(sock, sa, NULL);
453 if (sock < 0)
454 return sock;
455 }
456
457 if (arg[1])
458 backlog = atoi(arg + 1);
459 else
460 backlog = 1000;
461
462 if (backlog < 0 || backlog > 65535) {
463 fprintf(stderr, "backlog must be between 0 and 65535 inclusive (was %d)\n", backlog);
464 goto fail;
465 }
466
Willy Tarreau84393aa2016-11-12 11:29:46 +0100467 if (listen(sock, backlog) == -1) {
468 perror("listen");
469 goto fail;
470 }
471
472 return sock;
473 fail:
474 close(sock);
475 return -1;
476}
477
478/* accepts a socket from listening socket <sock>, and returns it (or -1 in case of error) */
479int tcp_accept(int sock, const char *arg)
480{
481 int count;
482 int newsock;
483
484 if (arg[1])
485 count = atoi(arg + 1);
486 else
487 count = 1;
488
489 if (count <= 0) {
490 fprintf(stderr, "accept count must be > 0 or unset (was %d)\n", count);
491 return -1;
492 }
493
494 do {
495 newsock = accept(sock, NULL, NULL);
496 if (newsock < 0) { // TODO: improve error handling
497 if (errno == EINTR || errno == EAGAIN || errno == ECONNABORTED)
498 continue;
499 perror("accept()");
500 break;
501 }
502
503 if (count > 1)
504 close(newsock);
505 count--;
506 } while (count > 0);
507
508 fcntl(newsock, F_SETFL, O_NONBLOCK);
509 return newsock;
510}
511
512/* Try to establish a new connection to <sa>. Return the fd or -1 in case of error */
Willy Tarreauff13dad2022-06-07 11:55:45 +0200513int tcp_connect(int sock, const struct sockaddr_storage *sa, const char *arg)
Willy Tarreau84393aa2016-11-12 11:29:46 +0100514{
Willy Tarreau542bf0a2022-06-07 11:46:57 +0200515 struct sockaddr_storage conn_addr;
Willy Tarreau84393aa2016-11-12 11:29:46 +0100516
Willy Tarreau542bf0a2022-06-07 11:46:57 +0200517 if (arg[1]) {
518 struct err_msg err;
519
520 if (addr_to_ss(arg + 1, &conn_addr, &err) < 0)
521 die(1, "%s\n", err.msg);
522 sa = &conn_addr;
523 }
524
Willy Tarreauff13dad2022-06-07 11:55:45 +0200525 if (sock < 0) {
Willy Tarreaub714e112023-01-11 10:54:59 +0100526 sock = tcp_socket(sa->ss_family);
Willy Tarreauff13dad2022-06-07 11:55:45 +0200527 if (sock < 0)
528 return sock;
529 }
Willy Tarreau84393aa2016-11-12 11:29:46 +0100530
531 if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1)
532 goto fail;
533
534 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) == -1)
535 goto fail;
536
Willy Tarreau24d41b92017-03-14 14:50:05 +0100537 if (connect(sock, (const struct sockaddr *)sa, sizeof(struct sockaddr_in)) < 0) {
Willy Tarreau84393aa2016-11-12 11:29:46 +0100538 if (errno != EINPROGRESS)
539 goto fail;
540 }
541
542 return sock;
543 fail:
544 close(sock);
545 return -1;
546}
547
Willy Tarreau2e065cb2020-10-14 08:09:48 +0200548/* Try to disconnect by connecting to AF_UNSPEC. Return >=0 on success, -1 in case of error */
549int tcp_disconnect(int sock)
550{
551 const struct sockaddr sa = { .sa_family = AF_UNSPEC };
552
553 return connect(sock, &sa, sizeof(sa));
554}
555
Willy Tarreau1973e812016-11-12 18:45:42 +0100556/* receives N bytes from the socket and returns 0 (or -1 in case of a recv
557 * error, or -2 in case of an argument error). When no arg is passed, receives
558 * anything and stops. Otherwise reads the requested amount of data. 0 means
559 * read as much as possible.
Willy Tarreau84393aa2016-11-12 11:29:46 +0100560 */
561int tcp_recv(int sock, const char *arg)
562{
563 int count = -1; // stop at first read
564 int ret;
Willy Tarreaua84a2db2017-03-14 14:50:52 +0100565 int max;
Willy Tarreau84393aa2016-11-12 11:29:46 +0100566
567 if (arg[1]) {
568 count = atoi(arg + 1);
569 if (count < 0) {
570 fprintf(stderr, "recv count must be >= 0 or unset (was %d)\n", count);
Willy Tarreau1973e812016-11-12 18:45:42 +0100571 return -2;
Willy Tarreau84393aa2016-11-12 11:29:46 +0100572 }
573 }
574
575 while (1) {
Willy Tarreaua84a2db2017-03-14 14:50:52 +0100576 max = (count > 0) ? count : INT_MAX;
577 if (max > sizeof(trash))
578 max = sizeof(trash);
579 ret = recv(sock, trash, max, MSG_NOSIGNAL | MSG_TRUNC);
Willy Tarreau84393aa2016-11-12 11:29:46 +0100580 if (ret < 0) {
581 if (errno == EINTR)
582 continue;
Willy Tarreau869c7592016-11-12 17:50:57 +0100583 if (errno != EAGAIN) {
584 dolog("recv %d\n", ret);
Willy Tarreau84393aa2016-11-12 11:29:46 +0100585 return -1;
Willy Tarreau869c7592016-11-12 17:50:57 +0100586 }
Willy Tarreaue5f72b82022-11-25 16:15:20 +0100587 while (!wait_on_fd(sock, POLLIN | POLLRDHUP, 1000));
Willy Tarreau84393aa2016-11-12 11:29:46 +0100588 continue;
589 }
Willy Tarreau869c7592016-11-12 17:50:57 +0100590 dolog("recv %d\n", ret);
Willy Tarreau84393aa2016-11-12 11:29:46 +0100591 if (!ret)
592 break;
593
594 if (!count)
595 continue;
596 else if (count > 0)
597 count -= ret;
598
599 if (count <= 0)
600 break;
601 }
602
603 return 0;
604}
605
Willy Tarreau1973e812016-11-12 18:45:42 +0100606/* Sends N bytes to the socket and returns 0 (or -1 in case of send error, -2
607 * in case of an argument error. If the byte count is not set, sends only one
608 * block. Sending zero means try to send forever. If the argument starts with
609 * ':' then whatever follows is interpreted as the payload to be sent as-is.
610 * Escaped characters '\r', '\n', '\t' and '\\' are detected and converted. In
Willy Tarreau59623e02016-11-12 18:25:45 +0100611 * this case, blocks must be small so that send() doesn't fragment them, as
612 * they will be put into the trash and expected to be sent at once.
Willy Tarreau84393aa2016-11-12 11:29:46 +0100613 */
614int tcp_send(int sock, const char *arg)
615{
616 int count = -1; // stop after first block
617 int ret;
618
Willy Tarreau59623e02016-11-12 18:25:45 +0100619 if (arg[1] == ':') {
620 count = unescape(trash, sizeof(trash), arg + 2);
621 } else if (arg[1]) {
Willy Tarreau84393aa2016-11-12 11:29:46 +0100622 count = atoi(arg + 1);
Willy Tarreau869c7592016-11-12 17:50:57 +0100623 if (count < 0) {
Willy Tarreau84393aa2016-11-12 11:29:46 +0100624 fprintf(stderr, "send count must be >= 0 or unset (was %d)\n", count);
Willy Tarreau1973e812016-11-12 18:45:42 +0100625 return -2;
Willy Tarreau84393aa2016-11-12 11:29:46 +0100626 }
627 }
628
629 while (1) {
630 ret = send(sock, trash,
631 (count > 0) && (count < sizeof(trash)) ? count : sizeof(trash),
632 MSG_NOSIGNAL | ((count > sizeof(trash)) ? MSG_MORE : 0));
633 if (ret < 0) {
634 if (errno == EINTR)
635 continue;
Willy Tarreau869c7592016-11-12 17:50:57 +0100636 if (errno != EAGAIN) {
637 dolog("send %d\n", ret);
Willy Tarreau84393aa2016-11-12 11:29:46 +0100638 return -1;
Willy Tarreau869c7592016-11-12 17:50:57 +0100639 }
Willy Tarreau45c27792022-11-25 16:05:46 +0100640 while (!wait_on_fd(sock, POLLOUT, 1000));
Willy Tarreau84393aa2016-11-12 11:29:46 +0100641 continue;
642 }
Willy Tarreau869c7592016-11-12 17:50:57 +0100643 dolog("send %d\n", ret);
Willy Tarreau84393aa2016-11-12 11:29:46 +0100644 if (!count)
645 continue;
646 else if (count > 0)
647 count -= ret;
648
649 if (count <= 0)
650 break;
651 }
652
653 return 0;
654}
655
656/* echoes N bytes to the socket and returns 0 (or -1 in case of error). If not
657 * set, echoes only the first block. Zero means forward forever.
658 */
659int tcp_echo(int sock, const char *arg)
660{
661 int count = -1; // echo forever
662 int ret;
663 int rcvd;
664
665 if (arg[1]) {
666 count = atoi(arg + 1);
667 if (count < 0) {
668 fprintf(stderr, "send count must be >= 0 or unset (was %d)\n", count);
669 return -1;
670 }
671 }
672
673 rcvd = 0;
674 while (1) {
675 if (rcvd <= 0) {
676 /* no data pending */
677 rcvd = recv(sock, trash, (count > 0) && (count < sizeof(trash)) ? count : sizeof(trash), MSG_NOSIGNAL);
678 if (rcvd < 0) {
679 if (errno == EINTR)
680 continue;
Willy Tarreau869c7592016-11-12 17:50:57 +0100681 if (errno != EAGAIN) {
682 dolog("recv %d\n", rcvd);
Willy Tarreau84393aa2016-11-12 11:29:46 +0100683 return -1;
Willy Tarreau869c7592016-11-12 17:50:57 +0100684 }
Willy Tarreaue5f72b82022-11-25 16:15:20 +0100685 while (!wait_on_fd(sock, POLLIN | POLLRDHUP, 1000));
Willy Tarreau84393aa2016-11-12 11:29:46 +0100686 continue;
687 }
Willy Tarreau869c7592016-11-12 17:50:57 +0100688 dolog("recv %d\n", rcvd);
Willy Tarreau84393aa2016-11-12 11:29:46 +0100689 if (!rcvd)
690 break;
691 }
692 else {
693 /* some data still pending */
694 ret = send(sock, trash, rcvd, MSG_NOSIGNAL | ((count > rcvd) ? MSG_MORE : 0));
695 if (ret < 0) {
696 if (errno == EINTR)
697 continue;
Willy Tarreau869c7592016-11-12 17:50:57 +0100698 if (errno != EAGAIN) {
699 dolog("send %d\n", ret);
Willy Tarreau84393aa2016-11-12 11:29:46 +0100700 return -1;
Willy Tarreau869c7592016-11-12 17:50:57 +0100701 }
Willy Tarreau45c27792022-11-25 16:05:46 +0100702 while (!wait_on_fd(sock, POLLOUT, 1000));
Willy Tarreau84393aa2016-11-12 11:29:46 +0100703 continue;
704 }
Willy Tarreau869c7592016-11-12 17:50:57 +0100705 dolog("send %d\n", ret);
Willy Tarreau84393aa2016-11-12 11:29:46 +0100706 rcvd -= ret;
707 if (rcvd)
708 continue;
709
710 if (!count)
711 continue;
712 else if (count > 0)
713 count -= ret;
714
715 if (count <= 0)
716 break;
717 }
718 }
719 return 0;
720}
721
722/* waits for an event on the socket, usually indicates an accept for a
723 * listening socket and a connect for an outgoing socket.
724 */
725int tcp_wait(int sock, const char *arg)
726{
Willy Tarreau84393aa2016-11-12 11:29:46 +0100727 int delay = -1; // wait forever
728 int ret;
729
730 if (arg[1]) {
731 delay = atoi(arg + 1);
732 if (delay < 0) {
733 fprintf(stderr, "wait time must be >= 0 or unset (was %d)\n", delay);
734 return -1;
735 }
736 }
737
738 /* FIXME: this doesn't take into account delivered signals */
Willy Tarreaue5f72b82022-11-25 16:15:20 +0100739 ret = wait_on_fd(sock, POLLIN | POLLRDHUP | POLLOUT, delay);
Willy Tarreau45c27792022-11-25 16:05:46 +0100740 if (ret < 0)
741 return ret;
Willy Tarreau869c7592016-11-12 17:50:57 +0100742
Willy Tarreau84393aa2016-11-12 11:29:46 +0100743 return 0;
744}
745
746/* waits for the input data to be present */
747int tcp_wait_in(int sock, const char *arg)
748{
Willy Tarreau84393aa2016-11-12 11:29:46 +0100749 int ret;
750
Willy Tarreaue5f72b82022-11-25 16:15:20 +0100751 ret = wait_on_fd(sock, POLLIN | POLLRDHUP, 1000);
Willy Tarreau45c27792022-11-25 16:05:46 +0100752 if (ret < 0)
753 return ret;
Willy Tarreau869c7592016-11-12 17:50:57 +0100754
Willy Tarreau84393aa2016-11-12 11:29:46 +0100755 return 0;
756}
757
758/* waits for the output queue to be empty */
759int tcp_wait_out(int sock, const char *arg)
760{
Willy Tarreau84393aa2016-11-12 11:29:46 +0100761 int ret;
762
Willy Tarreau45c27792022-11-25 16:05:46 +0100763 ret = wait_on_fd(sock, POLLOUT, 1000);
764 if (ret < 0)
765 return ret;
Willy Tarreau869c7592016-11-12 17:50:57 +0100766
Willy Tarreau84393aa2016-11-12 11:29:46 +0100767 /* Now wait for data to leave the socket */
768 do {
769 if (ioctl(sock, TIOCOUTQ, &ret) < 0)
770 return -1;
771 } while (ret > 0);
772 return 0;
773}
774
775/* delays processing for <time> milliseconds, 100 by default */
776int tcp_pause(int sock, const char *arg)
777{
Willy Tarreau84393aa2016-11-12 11:29:46 +0100778 int delay = 100;
Willy Tarreau84393aa2016-11-12 11:29:46 +0100779
780 if (arg[1]) {
781 delay = atoi(arg + 1);
782 if (delay < 0) {
783 fprintf(stderr, "wait time must be >= 0 or unset (was %d)\n", delay);
784 return -1;
785 }
786 }
787
788 usleep(delay * 1000);
789 return 0;
790}
791
Willy Tarreau95a6b782016-11-12 13:25:53 +0100792/* forks another process while respecting the limit imposed in argument (1 by
793 * default). Will wait for another process to exit before creating a new one.
794 * Returns the value of the fork() syscall, ie 0 for the child, non-zero for
795 * the parent, -1 for an error.
796 */
797int tcp_fork(int sock, const char *arg)
798{
799 int max = 1;
800 int ret;
801
802 if (arg[1]) {
803 max = atoi(arg + 1);
804 if (max <= 0) {
805 fprintf(stderr, "max process must be > 0 or unset (was %d)\n", max);
806 return -1;
807 }
808 }
809
810 while (nbproc >= max)
811 poll(NULL, 0, 1000);
812
813 ret = fork();
814 if (ret > 0)
815 __sync_add_and_fetch(&nbproc, 1);
816 return ret;
817}
818
Willy Tarreau84393aa2016-11-12 11:29:46 +0100819int main(int argc, char **argv)
820{
Willy Tarreau7184ca22022-06-07 11:36:20 +0200821 struct sockaddr_storage default_addr;
Willy Tarreau84393aa2016-11-12 11:29:46 +0100822 struct err_msg err;
823 const char *arg0;
Willy Tarreau29cc11c2016-11-12 18:54:20 +0100824 int loop_arg;
Willy Tarreau84393aa2016-11-12 11:29:46 +0100825 int arg;
Willy Tarreau95a6b782016-11-12 13:25:53 +0100826 int ret;
Willy Tarreau84393aa2016-11-12 11:29:46 +0100827 int sock;
Willy Tarreaub7a6d0d2017-05-02 22:14:59 +0200828 int errfd;
Willy Tarreau84393aa2016-11-12 11:29:46 +0100829
830 arg0 = argv[0];
Willy Tarreau869c7592016-11-12 17:50:57 +0100831
832 while (argc > 1 && argv[1][0] == '-') {
833 argc--; argv++;
834 if (strcmp(argv[0], "-t") == 0)
835 showtime++;
836 else if (strcmp(argv[0], "-tt") == 0)
837 showtime += 2;
838 else if (strcmp(argv[0], "-ttt") == 0)
839 showtime += 3;
Willy Tarreaud8a6a322022-11-25 17:05:05 +0100840 else if (strcmp(argv[0], "-e") == 0)
841 use_epoll = 1;
Willy Tarreau869c7592016-11-12 17:50:57 +0100842 else if (strcmp(argv[0], "-v") == 0)
843 verbose ++;
Willy Tarreau2d7cd3e2022-06-07 12:09:55 +0200844 else if (strcmp(argv[0], "-u") == 0) {
845 sock_type = SOCK_DGRAM;
846 sock_proto = IPPROTO_UDP;
847 }
Willy Tarreaub714e112023-01-11 10:54:59 +0100848 else if (strcmp(argv[0], "-U") == 0) {
849 sock_proto = 0;
850 }
Willy Tarreau869c7592016-11-12 17:50:57 +0100851 else if (strcmp(argv[0], "--") == 0)
852 break;
853 else
854 usage(1, arg0);
855 }
856
Willy Tarreau84393aa2016-11-12 11:29:46 +0100857 if (argc < 2)
858 usage(1, arg0);
859
Willy Tarreau869c7592016-11-12 17:50:57 +0100860 pid = getpid();
Willy Tarreau95a6b782016-11-12 13:25:53 +0100861 signal(SIGCHLD, sig_handler);
862
Willy Tarreau7184ca22022-06-07 11:36:20 +0200863 if (addr_to_ss(argv[1], &default_addr, &err) < 0)
Willy Tarreau84393aa2016-11-12 11:29:46 +0100864 die(1, "%s\n", err.msg);
865
Willy Tarreau869c7592016-11-12 17:50:57 +0100866 gettimeofday(&start_time, NULL);
867
Willy Tarreau84393aa2016-11-12 11:29:46 +0100868 sock = -1;
Willy Tarreau29cc11c2016-11-12 18:54:20 +0100869 loop_arg = 2;
870 for (arg = loop_arg; arg < argc; arg++) {
Willy Tarreau84393aa2016-11-12 11:29:46 +0100871 switch (argv[arg][0]) {
872 case 'L':
Willy Tarreaud4933312022-06-07 12:03:48 +0200873 sock = tcp_listen(sock, &default_addr, argv[arg]);
Willy Tarreau84393aa2016-11-12 11:29:46 +0100874 if (sock < 0)
875 die(1, "Fatal: tcp_listen() failed.\n");
876 break;
877
Willy Tarreaud4933312022-06-07 12:03:48 +0200878 case 'B':
Willy Tarreau84393aa2016-11-12 11:29:46 +0100879 /* silently ignore existing connections */
Willy Tarreaud4933312022-06-07 12:03:48 +0200880 sock = tcp_bind(sock, &default_addr, argv[arg]);
881 if (sock < 0)
882 die(1, "Fatal: tcp_connect() failed.\n");
883 dolog("connect\n");
884 break;
885
886 case 'C':
887 sock = tcp_connect(sock, &default_addr, argv[arg]);
Willy Tarreau84393aa2016-11-12 11:29:46 +0100888 if (sock < 0)
889 die(1, "Fatal: tcp_connect() failed.\n");
Willy Tarreau869c7592016-11-12 17:50:57 +0100890 dolog("connect\n");
Willy Tarreau84393aa2016-11-12 11:29:46 +0100891 break;
892
Willy Tarreau2e065cb2020-10-14 08:09:48 +0200893 case 'D':
894 /* silently ignore non-existing connections */
895 if (sock >= 0 && tcp_disconnect(sock) < 0)
896 die(1, "Fatal: tcp_connect() failed.\n");
897 dolog("disconnect\n");
898 break;
899
Willy Tarreau84393aa2016-11-12 11:29:46 +0100900 case 'A':
901 if (sock < 0)
902 die(1, "Fatal: tcp_accept() on non-socket.\n");
903 sock = tcp_accept(sock, argv[arg]);
904 if (sock < 0)
905 die(1, "Fatal: tcp_accept() failed.\n");
Willy Tarreau869c7592016-11-12 17:50:57 +0100906 dolog("accept\n");
Willy Tarreau29cc11c2016-11-12 18:54:20 +0100907 loop_arg = arg + 1; // cannot loop before accept()
Willy Tarreau84393aa2016-11-12 11:29:46 +0100908 break;
909
910 case 'T':
911 if (sock < 0)
912 die(1, "Fatal: tcp_set_nodelay() on non-socket.\n");
913 if (tcp_set_nodelay(sock, argv[arg]) < 0)
914 die(1, "Fatal: tcp_set_nodelay() failed.\n");
915 break;
916
917 case 'G':
918 if (sock < 0)
919 die(1, "Fatal: tcp_set_nolinger() on non-socket.\n");
920 if (tcp_set_nolinger(sock, argv[arg]) < 0)
921 die(1, "Fatal: tcp_set_nolinger() failed.\n");
922 break;
923
924 case 'Q':
925 if (sock < 0)
926 die(1, "Fatal: tcp_set_noquickack() on non-socket.\n");
927 if (tcp_set_noquickack(sock, argv[arg]) < 0)
928 die(1, "Fatal: tcp_set_noquickack() failed.\n");
929 break;
930
931 case 'R':
932 if (sock < 0)
933 die(1, "Fatal: tcp_recv() on non-socket.\n");
Willy Tarreau1973e812016-11-12 18:45:42 +0100934 ret = tcp_recv(sock, argv[arg]);
935 if (ret < 0) {
936 if (ret == -1) // usually ECONNRESET, silently exit
937 die(0, NULL);
Willy Tarreau84393aa2016-11-12 11:29:46 +0100938 die(1, "Fatal: tcp_recv() failed.\n");
Willy Tarreau1973e812016-11-12 18:45:42 +0100939 }
Willy Tarreau84393aa2016-11-12 11:29:46 +0100940 break;
941
942 case 'S':
943 if (sock < 0)
944 die(1, "Fatal: tcp_send() on non-socket.\n");
Willy Tarreau1973e812016-11-12 18:45:42 +0100945 ret = tcp_send(sock, argv[arg]);
946 if (ret < 0) {
947 if (ret == -1) // usually a broken pipe, silently exit
948 die(0, NULL);
Willy Tarreau84393aa2016-11-12 11:29:46 +0100949 die(1, "Fatal: tcp_send() failed.\n");
Willy Tarreau1973e812016-11-12 18:45:42 +0100950 }
Willy Tarreau84393aa2016-11-12 11:29:46 +0100951 break;
952
953 case 'E':
954 if (sock < 0)
955 die(1, "Fatal: tcp_echo() on non-socket.\n");
956 if (tcp_echo(sock, argv[arg]) < 0)
957 die(1, "Fatal: tcp_echo() failed.\n");
958 break;
959
960 case 'P':
961 if (tcp_pause(sock, argv[arg]) < 0)
962 die(1, "Fatal: tcp_pause() failed.\n");
963 break;
964
965 case 'W':
966 if (sock < 0)
967 die(1, "Fatal: tcp_wait() on non-socket.\n");
968 if (tcp_wait(sock, argv[arg]) < 0)
969 die(1, "Fatal: tcp_wait() failed.\n");
Willy Tarreau869c7592016-11-12 17:50:57 +0100970 dolog("ready_any\n");
Willy Tarreau84393aa2016-11-12 11:29:46 +0100971 break;
972
973 case 'I':
974 if (sock < 0)
975 die(1, "Fatal: tcp_wait_in() on non-socket.\n");
976 if (tcp_wait_in(sock, argv[arg]) < 0)
977 die(1, "Fatal: tcp_wait_in() failed.\n");
Willy Tarreau869c7592016-11-12 17:50:57 +0100978 dolog("ready_in\n");
Willy Tarreau84393aa2016-11-12 11:29:46 +0100979 break;
980
981 case 'O':
982 if (sock < 0)
983 die(1, "Fatal: tcp_wait_out() on non-socket.\n");
984 if (tcp_wait_out(sock, argv[arg]) < 0)
985 die(1, "Fatal: tcp_wait_out() failed.\n");
Willy Tarreau869c7592016-11-12 17:50:57 +0100986 dolog("ready_out\n");
Willy Tarreau84393aa2016-11-12 11:29:46 +0100987 break;
988
989 case 'K':
990 if (sock < 0 || close(sock) < 0)
991 die(1, "Fatal: close() on non-socket.\n");
Willy Tarreau869c7592016-11-12 17:50:57 +0100992 dolog("close\n");
Willy Tarreau84393aa2016-11-12 11:29:46 +0100993 sock = -1;
994 break;
995
996 case 'F':
997 /* ignore errors on shutdown() as they are common */
998 if (sock >= 0)
999 shutdown(sock, SHUT_WR);
Willy Tarreauc3890e52017-10-05 06:31:10 +02001000 dolog("shutdown(w)\n");
1001 break;
1002
1003 case 'r':
1004 /* ignore errors on shutdown() as they are common */
1005 if (sock >= 0)
1006 shutdown(sock, SHUT_RD);
1007 dolog("shutdown(r)\n");
Willy Tarreau84393aa2016-11-12 11:29:46 +01001008 break;
1009
Willy Tarreau95a6b782016-11-12 13:25:53 +01001010 case 'N':
1011 ret = tcp_fork(sock, argv[arg]);
1012 if (ret < 0)
1013 die(1, "Fatal: fork() failed.\n");
1014 if (ret > 0) {
1015 /* loop back to first arg */
Willy Tarreau29cc11c2016-11-12 18:54:20 +01001016 arg = loop_arg - 1;
Willy Tarreau95a6b782016-11-12 13:25:53 +01001017 continue;
1018 }
1019 /* OK we're in the child, let's continue */
Willy Tarreau869c7592016-11-12 17:50:57 +01001020 pid = getpid();
Willy Tarreau29cc11c2016-11-12 18:54:20 +01001021 loop_arg = arg + 1;
Willy Tarreau95a6b782016-11-12 13:25:53 +01001022 break;
Willy Tarreau29cc11c2016-11-12 18:54:20 +01001023
1024 case 'J': // jump back to oldest post-fork action
1025 arg = loop_arg - 1;
1026 continue;
1027
Willy Tarreaub7a6d0d2017-05-02 22:14:59 +02001028 case 'X': // execute command. Optionally supports redirecting only i/o/e
1029 if (arg + 1 >= argc)
1030 die(1, "Fatal: missing argument after %s\n", argv[arg]);
1031
1032 errfd = dup(2);
1033 fcntl(errfd, F_SETFD, fcntl(errfd, F_GETFD, FD_CLOEXEC) | FD_CLOEXEC);
1034 fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, O_NONBLOCK) & ~O_NONBLOCK);
1035 if (!argv[arg][1] || strchr(argv[arg], 'i'))
1036 dup2(sock, 0);
1037 if (!argv[arg][1] || strchr(argv[arg], 'o'))
1038 dup2(sock, 1);
1039 if (!argv[arg][1] || strchr(argv[arg], 'e'))
1040 dup2(sock, 2);
1041 argv += arg + 1;
1042 if (execvp(argv[0], argv) == -1) {
1043 int e = errno;
1044
1045 dup2(errfd, 2); // restore original stderr
1046 close(errfd);
1047 die(1, "Fatal: execvp(%s) failed : %s\n", argv[0], strerror(e));
1048 }
1049 break;
Willy Tarreau84393aa2016-11-12 11:29:46 +01001050 default:
1051 usage(1, arg0);
1052 }
1053 }
1054 return 0;
1055}