blob: 022c0396f4fc551078e6c2640a8d008166a6f1c5 [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>
Willy Tarreau2afa4252022-11-25 16:34:11 +01005
6#ifdef __linux__
7#include <sys/epoll.h>
8#endif
9
Willy Tarreau616c1cf2019-09-05 09:18:47 +020010#include <netinet/in.h>
11#include <netinet/tcp.h>
12#include <errno.h>
13#include <fcntl.h>
14#include <poll.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <unistd.h>
19
20/* for OSes which don't have it */
21#ifndef POLLRDHUP
22#define POLLRDHUP 0
23#endif
24
Willy Tarreau79d2e7b2020-12-21 08:43:50 +010025#ifndef MSG_NOSIGNAL
26#define MSG_NOSIGNAL 0
27#endif
28#ifndef MSG_MORE
29#define MSG_MORE 0
30#endif
31
Willy Tarreau616c1cf2019-09-05 09:18:47 +020032int verbose = 0;
33int cmd = 0;
34int cmdstep = 0;
35int zero = 0;
36int one = 1;
37int lfd = -1;
38int cfd = -1;
39int sfd = -1;
Willy Tarreaue6106412022-11-17 07:44:27 +010040int connected = 0;
Willy Tarreau2afa4252022-11-25 16:34:11 +010041int use_epoll = 0;
Willy Tarreau616c1cf2019-09-05 09:18:47 +020042struct sockaddr_in saddr, caddr;
43socklen_t salen, calen;
44
Willy Tarreau35402852022-11-17 07:59:49 +010045static inline const char *side(int fd)
46{
47 if (fd == lfd)
48 return "l";
49 if (fd == sfd)
50 return "s";
51 if (fd == cfd)
52 return "c";
53 return "?";
54}
55
Willy Tarreau616c1cf2019-09-05 09:18:47 +020056void usage(const char *arg0)
57{
58 printf("Usage: %s [ arg [<action>[,...]] ] ...\n"
59 "args:\n"
60 " -h display this help\n"
61 " -v verbose mode (shows ret values)\n"
Willy Tarreau2afa4252022-11-25 16:34:11 +010062 " -e use epoll instead of poll\n"
Willy Tarreau616c1cf2019-09-05 09:18:47 +020063 " -c <actions> perform <action> on client side socket\n"
64 " -s <actions> perform <action> on server side socket\n"
65 " -l <actions> perform <action> on listening socket\n"
66 "\n"
67 "actions for -c/-s/-l (multiple may be delimited by commas) :\n"
Willy Tarreaue6106412022-11-17 07:44:27 +010068 " con connect to listener, implicit before first -c/-s\n"
Willy Tarreau616c1cf2019-09-05 09:18:47 +020069 " acc accept on listener, implicit before first -s\n"
70 " snd send a few bytes of data\n"
71 " mor send a few bytes of data with MSG_MORE\n"
72 " rcv receive a few bytes of data\n"
73 " drn drain: receive till zero\n"
74 " shr SHUT_RD : shutdown read side\n"
75 " shw SHUT_WR : shutdown write side\n"
76 " shb SHUT_RDWR : shutdown both sides\n"
77 " lin disable lingering on the socket\n"
78 " clo close the file descriptor\n"
79 " pol poll() for any event\n"
80 "\n", arg0);
81}
82
83void die(const char *msg)
84{
85 if (msg)
86 fprintf(stderr, "%s\n", msg);
87 exit(1);
88}
89
90const char *get_errno(int ret)
91{
92 static char errmsg[100];
93
94 if (ret >= 0)
95 return "";
96
97 snprintf(errmsg, sizeof(errmsg), " (%s)", strerror(errno));
98 return errmsg;
99}
100
101void do_acc(int fd)
102{
103 int ret;
104
105 calen = sizeof(caddr);
106 ret = accept(lfd, (struct sockaddr*)&caddr, &calen);
107 if (sfd < 0)
108 sfd = ret;
109 if (verbose)
Willy Tarreau35402852022-11-17 07:59:49 +0100110 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 +0200111}
112
Willy Tarreaue6106412022-11-17 07:44:27 +0100113void do_con(int fd)
114{
115 int ret;
116
117 ret = connect(cfd, (const struct sockaddr*)&saddr, salen);
118 if (verbose)
Willy Tarreau35402852022-11-17 07:59:49 +0100119 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 +0100120 connected = 1;
121}
122
Willy Tarreau616c1cf2019-09-05 09:18:47 +0200123void do_snd(int fd)
124{
125 int ret;
126
127 ret = send(fd, "foo", 3, MSG_NOSIGNAL|MSG_DONTWAIT);
128 if (verbose)
Willy Tarreau35402852022-11-17 07:59:49 +0100129 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 +0200130}
131
132void do_mor(int fd)
133{
134 int ret;
135
136 ret = send(fd, "foo", 3, MSG_NOSIGNAL|MSG_DONTWAIT|MSG_MORE);
137 if (verbose)
Willy Tarreau35402852022-11-17 07:59:49 +0100138 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 +0200139}
140
141void do_rcv(int fd)
142{
143 char buf[10];
144 int ret;
145
146 ret = recv(fd, buf, sizeof(buf), MSG_DONTWAIT);
147 if (verbose)
Willy Tarreau35402852022-11-17 07:59:49 +0100148 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 +0200149}
150
151void do_drn(int fd)
152{
153 char buf[16384];
154 int total = -1;
155 int ret;
156
157 while (1) {
158 ret = recv(fd, buf, sizeof(buf), 0);
159 if (ret <= 0)
160 break;
161 if (total < 0)
162 total = 0;
163 total += ret;
164 }
165
166 if (verbose)
Willy Tarreau35402852022-11-17 07:59:49 +0100167 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 +0200168}
169
170void do_shr(int fd)
171{
172 int ret;
173
174 ret = shutdown(fd, SHUT_RD);
175 if (verbose)
Willy Tarreau35402852022-11-17 07:59:49 +0100176 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 +0200177}
178
179void do_shw(int fd)
180{
181 int ret;
182
183 ret = shutdown(fd, SHUT_WR);
184 if (verbose)
Willy Tarreau35402852022-11-17 07:59:49 +0100185 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 +0200186}
187
188void do_shb(int fd)
189{
190 int ret;
191
192 ret = shutdown(fd, SHUT_RDWR);
193 if (verbose)
Willy Tarreau35402852022-11-17 07:59:49 +0100194 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 +0200195}
196
197void do_lin(int fd)
198{
199 struct linger nolinger = { .l_onoff = 1, .l_linger = 0 };
200 int ret;
201
202 ret = setsockopt(fd, SOL_SOCKET, SO_LINGER, &nolinger, sizeof(nolinger));
203 if (verbose)
Willy Tarreau35402852022-11-17 07:59:49 +0100204 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 +0200205}
206
207void do_clo(int fd)
208{
209 int ret;
210
211 ret = close(fd);
212 if (verbose)
Willy Tarreau35402852022-11-17 07:59:49 +0100213 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 +0200214}
215
216void do_pol(int fd)
217{
218 struct pollfd fds = { .fd = fd, .events = POLLIN|POLLOUT|POLLRDHUP, .revents=0 };
Willy Tarreau2afa4252022-11-25 16:34:11 +0100219 int flags, flag;
Willy Tarreau616c1cf2019-09-05 09:18:47 +0200220 int ret;
221
Willy Tarreau2afa4252022-11-25 16:34:11 +0100222#ifdef __linux__
223 while (use_epoll) {
224 struct epoll_event evt;
225 static int epoll_fd = -1;
226
227 if (epoll_fd == -1)
228 epoll_fd = epoll_create(1024);
229 if (epoll_fd == -1)
230 break;
231 evt.events = EPOLLIN | EPOLLOUT | EPOLLRDHUP;
232 evt.data.fd = fd;
233 epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &evt);
234 ret = epoll_wait(epoll_fd, &evt, 1, 0);
235
236 if (verbose) {
237 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 ? evt.events : 0);
238 if (ret > 0 && evt.events) {
239 putchar('(');
240
241 for (flags = evt.events; flags; flags ^= flag) {
242 flag = flags ^ (flags & (flags - 1)); // keep lowest bit only
243 switch (flag) {
244 case EPOLLIN: printf("IN"); break;
245 case EPOLLOUT: printf("OUT"); break;
246 case EPOLLPRI: printf("PRI"); break;
247 case EPOLLHUP: printf("HUP"); break;
248 case EPOLLERR: printf("ERR"); break;
249 case EPOLLRDHUP: printf("RDHUP"); break;
250 default: printf("???[%#x]", flag); break;
251 }
252 if (flags ^ flag)
253 putchar(' ');
254 }
255 putchar(')');
256 }
257 putchar('\n');
258 }
259
260 evt.data.fd = fd;
261 epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &evt);
262 return;
263 }
264#endif
Willy Tarreau616c1cf2019-09-05 09:18:47 +0200265 ret = poll(&fds, 1, 0);
266 if (verbose) {
Willy Tarreau35402852022-11-17 07:59:49 +0100267 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 +0200268 if (ret > 0 && fds.revents) {
Willy Tarreau616c1cf2019-09-05 09:18:47 +0200269 putchar('(');
270
271 for (flags = fds.revents; flags; flags ^= flag) {
272 flag = flags ^ (flags & (flags - 1)); // keep lowest bit only
273 switch (flag) {
274 case POLLIN: printf("IN"); break;
275 case POLLOUT: printf("OUT"); break;
276 case POLLPRI: printf("PRI"); break;
277 case POLLHUP: printf("HUP"); break;
278 case POLLERR: printf("ERR"); break;
279 case POLLNVAL: printf("NVAL"); break;
280#if POLLRDHUP
281 case POLLRDHUP: printf("RDHUP"); break;
282#endif
283 default: printf("???[%#x]", flag); break;
284 }
285 if (flags ^ flag)
286 putchar(' ');
287 }
288 putchar(')');
289 }
290 putchar('\n');
291 }
292}
293
294int main(int argc, char **argv)
295{
296 const char *arg0;
297 char *word, *next;
298 int fd;
299
300 /* listener */
301 lfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
302 if (lfd < 0)
303 die("socket(l)");
304
305 setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
306
307 memset(&saddr, 0, sizeof(saddr));
308 saddr.sin_family = AF_INET;
309 saddr.sin_port = htons(0);
310 salen = sizeof(saddr);
311
312 if (bind(lfd, (struct sockaddr *)&saddr, salen) < 0)
313 die("bind()");
314
315 if (listen(lfd, 1000) < 0)
316 die("listen()");
317
318 if (getsockname(lfd, (struct sockaddr *)&saddr, &salen) < 0)
319 die("getsockname()");
320
321
322 /* client */
323 cfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
324 if (cfd < 0)
325 die("socket(c)");
326
Willy Tarreau616c1cf2019-09-05 09:18:47 +0200327 arg0 = argv[0];
328 if (argc < 2) {
329 usage(arg0);
330 exit(1);
331 }
332
333 write(1, "#### BEGIN ####\n", 16); // add a visible delimiter in the traces
334
335 while (argc > 1) {
336 argc--; argv++;
337 if (**argv != '-') {
338 usage(arg0);
339 exit(1);
340 }
341
342 fd = -1;
343 switch (argv[0][1]) {
344 case 'h' :
345 usage(arg0);
346 exit(0);
347 break;
348 case 'v' :
349 verbose++;
350 break;
Willy Tarreau2afa4252022-11-25 16:34:11 +0100351 case 'e' :
352 use_epoll = 1;
353 break;
Willy Tarreau616c1cf2019-09-05 09:18:47 +0200354 case 'c' :
355 cmd++; cmdstep = 0;
Willy Tarreaue6106412022-11-17 07:44:27 +0100356 if (!connected) {
357 do_con(cfd);
358 /* connection is pending in accept queue, accept() will either be
359 * explicit with "-l acc" below, or implicit on "-s <cmd>"
360 */
361 }
Willy Tarreau616c1cf2019-09-05 09:18:47 +0200362 fd = cfd;
363 break;
364 case 's' :
365 cmd++; cmdstep = 0;
Willy Tarreaue6106412022-11-17 07:44:27 +0100366 if (!connected)
367 do_con(cfd);
Willy Tarreau616c1cf2019-09-05 09:18:47 +0200368 if (sfd < 0)
369 do_acc(lfd);
370 if (sfd < 0)
371 die("accept()");
372 fd = sfd;
373 break;
374 case 'l' :
375 cmd++; cmdstep = 0;
376 fd = lfd;
377 break;
378 default : usage(arg0); exit(1); break;
379 }
380
381 if (fd >= 0) { /* an action is required */
382 if (argc < 2) {
383 usage(arg0);
384 exit(1);
385 }
386
387 for (word = argv[1]; word && *word; word = next) {
388 next = strchr(word, ',');
389 if (next)
390 *(next++) = 0;
391 cmdstep++;
392 if (strcmp(word, "acc") == 0) {
393 do_acc(fd);
394 }
Willy Tarreaue6106412022-11-17 07:44:27 +0100395 else if (strcmp(word, "con") == 0) {
396 do_con(fd);
397 }
Willy Tarreau616c1cf2019-09-05 09:18:47 +0200398 else if (strcmp(word, "snd") == 0) {
399 do_snd(fd);
400 }
401 else if (strcmp(word, "mor") == 0) {
402 do_mor(fd);
403 }
404 else if (strcmp(word, "rcv") == 0) {
405 do_rcv(fd);
406 }
407 else if (strcmp(word, "drn") == 0) {
408 do_drn(fd);
409 }
410 else if (strcmp(word, "shb") == 0) {
411 do_shb(fd);
412 }
413 else if (strcmp(word, "shr") == 0) {
414 do_shr(fd);
415 }
416 else if (strcmp(word, "shw") == 0) {
417 do_shw(fd);
418 }
419 else if (strcmp(word, "lin") == 0) {
420 do_lin(fd);
421 }
422 else if (strcmp(word, "clo") == 0) {
423 do_clo(fd);
424 }
425 else if (strcmp(word, "pol") == 0) {
426 do_pol(fd);
427 }
428 else {
429 printf("Ignoring unknown action '%s' in step #%d of cmd #%d\n", word, cmdstep, cmd);
430 }
431 }
432 argc--; argv++;
433 }
434 }
435
436 write(1, "#### END ####\n", 14); // add a visible delimiter in the traces
437
438 if (!cmd) {
439 printf("No command was requested!\n");
440 usage(arg0);
441 exit(1);
442 }
443
444 return 0;
445}