blob: 554fd63ca21d99f31be0873e061f0badac802b5e [file] [log] [blame]
Willy Tarreau616c1cf2019-09-05 09:18:47 +02001#define _GNU_SOURCE // for POLLRDHUP
2#include <sys/socket.h>
3#include <sys/stat.h>
4#include <sys/types.h>
5#include <netinet/in.h>
6#include <netinet/tcp.h>
7#include <errno.h>
8#include <fcntl.h>
9#include <poll.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <unistd.h>
14
15/* for OSes which don't have it */
16#ifndef POLLRDHUP
17#define POLLRDHUP 0
18#endif
19
Willy Tarreau79d2e7b2020-12-21 08:43:50 +010020#ifndef MSG_NOSIGNAL
21#define MSG_NOSIGNAL 0
22#endif
23#ifndef MSG_MORE
24#define MSG_MORE 0
25#endif
26
Willy Tarreau616c1cf2019-09-05 09:18:47 +020027int verbose = 0;
28int cmd = 0;
29int cmdstep = 0;
30int zero = 0;
31int one = 1;
32int lfd = -1;
33int cfd = -1;
34int sfd = -1;
Willy Tarreaue6106412022-11-17 07:44:27 +010035int connected = 0;
Willy Tarreau616c1cf2019-09-05 09:18:47 +020036struct sockaddr_in saddr, caddr;
37socklen_t salen, calen;
38
Willy Tarreau35402852022-11-17 07:59:49 +010039static inline const char *side(int fd)
40{
41 if (fd == lfd)
42 return "l";
43 if (fd == sfd)
44 return "s";
45 if (fd == cfd)
46 return "c";
47 return "?";
48}
49
Willy Tarreau616c1cf2019-09-05 09:18:47 +020050void usage(const char *arg0)
51{
52 printf("Usage: %s [ arg [<action>[,...]] ] ...\n"
53 "args:\n"
54 " -h display this help\n"
55 " -v verbose mode (shows ret values)\n"
56 " -c <actions> perform <action> on client side socket\n"
57 " -s <actions> perform <action> on server side socket\n"
58 " -l <actions> perform <action> on listening socket\n"
59 "\n"
60 "actions for -c/-s/-l (multiple may be delimited by commas) :\n"
Willy Tarreaue6106412022-11-17 07:44:27 +010061 " con connect to listener, implicit before first -c/-s\n"
Willy Tarreau616c1cf2019-09-05 09:18:47 +020062 " acc accept on listener, implicit before first -s\n"
63 " snd send a few bytes of data\n"
64 " mor send a few bytes of data with MSG_MORE\n"
65 " rcv receive a few bytes of data\n"
66 " drn drain: receive till zero\n"
67 " shr SHUT_RD : shutdown read side\n"
68 " shw SHUT_WR : shutdown write side\n"
69 " shb SHUT_RDWR : shutdown both sides\n"
70 " lin disable lingering on the socket\n"
71 " clo close the file descriptor\n"
72 " pol poll() for any event\n"
73 "\n", arg0);
74}
75
76void die(const char *msg)
77{
78 if (msg)
79 fprintf(stderr, "%s\n", msg);
80 exit(1);
81}
82
83const char *get_errno(int ret)
84{
85 static char errmsg[100];
86
87 if (ret >= 0)
88 return "";
89
90 snprintf(errmsg, sizeof(errmsg), " (%s)", strerror(errno));
91 return errmsg;
92}
93
94void do_acc(int fd)
95{
96 int ret;
97
98 calen = sizeof(caddr);
99 ret = accept(lfd, (struct sockaddr*)&caddr, &calen);
100 if (sfd < 0)
101 sfd = ret;
102 if (verbose)
Willy Tarreau35402852022-11-17 07:59:49 +0100103 printf("cmd #%d stp #%d: %s(%s=%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__ + 3, side(fd), fd, ret, get_errno(ret));
Willy Tarreau616c1cf2019-09-05 09:18:47 +0200104}
105
Willy Tarreaue6106412022-11-17 07:44:27 +0100106void do_con(int fd)
107{
108 int ret;
109
110 ret = connect(cfd, (const struct sockaddr*)&saddr, salen);
111 if (verbose)
Willy Tarreau35402852022-11-17 07:59:49 +0100112 printf("cmd #%d stp #%d: %s(%s=%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__ + 3, side(fd), fd, ret, get_errno(ret));
Willy Tarreaue6106412022-11-17 07:44:27 +0100113 connected = 1;
114}
115
Willy Tarreau616c1cf2019-09-05 09:18:47 +0200116void do_snd(int fd)
117{
118 int ret;
119
120 ret = send(fd, "foo", 3, MSG_NOSIGNAL|MSG_DONTWAIT);
121 if (verbose)
Willy Tarreau35402852022-11-17 07:59:49 +0100122 printf("cmd #%d stp #%d: %s(%s=%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__ + 3, side(fd), fd, ret, get_errno(ret));
Willy Tarreau616c1cf2019-09-05 09:18:47 +0200123}
124
125void do_mor(int fd)
126{
127 int ret;
128
129 ret = send(fd, "foo", 3, MSG_NOSIGNAL|MSG_DONTWAIT|MSG_MORE);
130 if (verbose)
Willy Tarreau35402852022-11-17 07:59:49 +0100131 printf("cmd #%d stp #%d: %s(%s=%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__ + 3, side(fd), fd, ret, get_errno(ret));
Willy Tarreau616c1cf2019-09-05 09:18:47 +0200132}
133
134void do_rcv(int fd)
135{
136 char buf[10];
137 int ret;
138
139 ret = recv(fd, buf, sizeof(buf), MSG_DONTWAIT);
140 if (verbose)
Willy Tarreau35402852022-11-17 07:59:49 +0100141 printf("cmd #%d stp #%d: %s(%s=%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__ + 3, side(fd), fd, ret, get_errno(ret));
Willy Tarreau616c1cf2019-09-05 09:18:47 +0200142}
143
144void do_drn(int fd)
145{
146 char buf[16384];
147 int total = -1;
148 int ret;
149
150 while (1) {
151 ret = recv(fd, buf, sizeof(buf), 0);
152 if (ret <= 0)
153 break;
154 if (total < 0)
155 total = 0;
156 total += ret;
157 }
158
159 if (verbose)
Willy Tarreau35402852022-11-17 07:59:49 +0100160 printf("cmd #%d stp #%d: %s(%s=%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__ + 3, side(fd), fd, total, get_errno(ret));
Willy Tarreau616c1cf2019-09-05 09:18:47 +0200161}
162
163void do_shr(int fd)
164{
165 int ret;
166
167 ret = shutdown(fd, SHUT_RD);
168 if (verbose)
Willy Tarreau35402852022-11-17 07:59:49 +0100169 printf("cmd #%d stp #%d: %s(%s=%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__ + 3, side(fd), fd, ret, get_errno(ret));
Willy Tarreau616c1cf2019-09-05 09:18:47 +0200170}
171
172void do_shw(int fd)
173{
174 int ret;
175
176 ret = shutdown(fd, SHUT_WR);
177 if (verbose)
Willy Tarreau35402852022-11-17 07:59:49 +0100178 printf("cmd #%d stp #%d: %s(%s=%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__ + 3, side(fd), fd, ret, get_errno(ret));
Willy Tarreau616c1cf2019-09-05 09:18:47 +0200179}
180
181void do_shb(int fd)
182{
183 int ret;
184
185 ret = shutdown(fd, SHUT_RDWR);
186 if (verbose)
Willy Tarreau35402852022-11-17 07:59:49 +0100187 printf("cmd #%d stp #%d: %s(%s=%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__ + 3, side(fd), fd, ret, get_errno(ret));
Willy Tarreau616c1cf2019-09-05 09:18:47 +0200188}
189
190void do_lin(int fd)
191{
192 struct linger nolinger = { .l_onoff = 1, .l_linger = 0 };
193 int ret;
194
195 ret = setsockopt(fd, SOL_SOCKET, SO_LINGER, &nolinger, sizeof(nolinger));
196 if (verbose)
Willy Tarreau35402852022-11-17 07:59:49 +0100197 printf("cmd #%d stp #%d: %s(%s=%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__ + 3, side(fd), fd, ret, get_errno(ret));
Willy Tarreau616c1cf2019-09-05 09:18:47 +0200198}
199
200void do_clo(int fd)
201{
202 int ret;
203
204 ret = close(fd);
205 if (verbose)
Willy Tarreau35402852022-11-17 07:59:49 +0100206 printf("cmd #%d stp #%d: %s(%s=%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__ + 3, side(fd), fd, ret, get_errno(ret));
Willy Tarreau616c1cf2019-09-05 09:18:47 +0200207}
208
209void do_pol(int fd)
210{
211 struct pollfd fds = { .fd = fd, .events = POLLIN|POLLOUT|POLLRDHUP, .revents=0 };
212 int ret;
213
214 ret = poll(&fds, 1, 0);
215 if (verbose) {
Willy Tarreau35402852022-11-17 07:59:49 +0100216 printf("cmd #%d stp #%d: %s(%s=%d): ret=%d%s ev=%#x ", cmd, cmdstep, __FUNCTION__ + 3, side(fd), fd, ret, get_errno(ret), ret > 0 ? fds.revents : 0);
Willy Tarreau616c1cf2019-09-05 09:18:47 +0200217 if (ret > 0 && fds.revents) {
218 int flags, flag;
219 putchar('(');
220
221 for (flags = fds.revents; flags; flags ^= flag) {
222 flag = flags ^ (flags & (flags - 1)); // keep lowest bit only
223 switch (flag) {
224 case POLLIN: printf("IN"); break;
225 case POLLOUT: printf("OUT"); break;
226 case POLLPRI: printf("PRI"); break;
227 case POLLHUP: printf("HUP"); break;
228 case POLLERR: printf("ERR"); break;
229 case POLLNVAL: printf("NVAL"); break;
230#if POLLRDHUP
231 case POLLRDHUP: printf("RDHUP"); break;
232#endif
233 default: printf("???[%#x]", flag); break;
234 }
235 if (flags ^ flag)
236 putchar(' ');
237 }
238 putchar(')');
239 }
240 putchar('\n');
241 }
242}
243
244int main(int argc, char **argv)
245{
246 const char *arg0;
247 char *word, *next;
248 int fd;
249
250 /* listener */
251 lfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
252 if (lfd < 0)
253 die("socket(l)");
254
255 setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
256
257 memset(&saddr, 0, sizeof(saddr));
258 saddr.sin_family = AF_INET;
259 saddr.sin_port = htons(0);
260 salen = sizeof(saddr);
261
262 if (bind(lfd, (struct sockaddr *)&saddr, salen) < 0)
263 die("bind()");
264
265 if (listen(lfd, 1000) < 0)
266 die("listen()");
267
268 if (getsockname(lfd, (struct sockaddr *)&saddr, &salen) < 0)
269 die("getsockname()");
270
271
272 /* client */
273 cfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
274 if (cfd < 0)
275 die("socket(c)");
276
Willy Tarreau616c1cf2019-09-05 09:18:47 +0200277 arg0 = argv[0];
278 if (argc < 2) {
279 usage(arg0);
280 exit(1);
281 }
282
283 write(1, "#### BEGIN ####\n", 16); // add a visible delimiter in the traces
284
285 while (argc > 1) {
286 argc--; argv++;
287 if (**argv != '-') {
288 usage(arg0);
289 exit(1);
290 }
291
292 fd = -1;
293 switch (argv[0][1]) {
294 case 'h' :
295 usage(arg0);
296 exit(0);
297 break;
298 case 'v' :
299 verbose++;
300 break;
301 case 'c' :
302 cmd++; cmdstep = 0;
Willy Tarreaue6106412022-11-17 07:44:27 +0100303 if (!connected) {
304 do_con(cfd);
305 /* connection is pending in accept queue, accept() will either be
306 * explicit with "-l acc" below, or implicit on "-s <cmd>"
307 */
308 }
Willy Tarreau616c1cf2019-09-05 09:18:47 +0200309 fd = cfd;
310 break;
311 case 's' :
312 cmd++; cmdstep = 0;
Willy Tarreaue6106412022-11-17 07:44:27 +0100313 if (!connected)
314 do_con(cfd);
Willy Tarreau616c1cf2019-09-05 09:18:47 +0200315 if (sfd < 0)
316 do_acc(lfd);
317 if (sfd < 0)
318 die("accept()");
319 fd = sfd;
320 break;
321 case 'l' :
322 cmd++; cmdstep = 0;
323 fd = lfd;
324 break;
325 default : usage(arg0); exit(1); break;
326 }
327
328 if (fd >= 0) { /* an action is required */
329 if (argc < 2) {
330 usage(arg0);
331 exit(1);
332 }
333
334 for (word = argv[1]; word && *word; word = next) {
335 next = strchr(word, ',');
336 if (next)
337 *(next++) = 0;
338 cmdstep++;
339 if (strcmp(word, "acc") == 0) {
340 do_acc(fd);
341 }
Willy Tarreaue6106412022-11-17 07:44:27 +0100342 else if (strcmp(word, "con") == 0) {
343 do_con(fd);
344 }
Willy Tarreau616c1cf2019-09-05 09:18:47 +0200345 else if (strcmp(word, "snd") == 0) {
346 do_snd(fd);
347 }
348 else if (strcmp(word, "mor") == 0) {
349 do_mor(fd);
350 }
351 else if (strcmp(word, "rcv") == 0) {
352 do_rcv(fd);
353 }
354 else if (strcmp(word, "drn") == 0) {
355 do_drn(fd);
356 }
357 else if (strcmp(word, "shb") == 0) {
358 do_shb(fd);
359 }
360 else if (strcmp(word, "shr") == 0) {
361 do_shr(fd);
362 }
363 else if (strcmp(word, "shw") == 0) {
364 do_shw(fd);
365 }
366 else if (strcmp(word, "lin") == 0) {
367 do_lin(fd);
368 }
369 else if (strcmp(word, "clo") == 0) {
370 do_clo(fd);
371 }
372 else if (strcmp(word, "pol") == 0) {
373 do_pol(fd);
374 }
375 else {
376 printf("Ignoring unknown action '%s' in step #%d of cmd #%d\n", word, cmdstep, cmd);
377 }
378 }
379 argc--; argv++;
380 }
381 }
382
383 write(1, "#### END ####\n", 14); // add a visible delimiter in the traces
384
385 if (!cmd) {
386 printf("No command was requested!\n");
387 usage(arg0);
388 exit(1);
389 }
390
391 return 0;
392}