blob: 55f922aaaf1b2a1865c53fcc6c42e99f7580b0b2 [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;
35struct sockaddr_in saddr, caddr;
36socklen_t salen, calen;
37
38void usage(const char *arg0)
39{
40 printf("Usage: %s [ arg [<action>[,...]] ] ...\n"
41 "args:\n"
42 " -h display this help\n"
43 " -v verbose mode (shows ret values)\n"
44 " -c <actions> perform <action> on client side socket\n"
45 " -s <actions> perform <action> on server side socket\n"
46 " -l <actions> perform <action> on listening socket\n"
47 "\n"
48 "actions for -c/-s/-l (multiple may be delimited by commas) :\n"
49 " acc accept on listener, implicit before first -s\n"
50 " snd send a few bytes of data\n"
51 " mor send a few bytes of data with MSG_MORE\n"
52 " rcv receive a few bytes of data\n"
53 " drn drain: receive till zero\n"
54 " shr SHUT_RD : shutdown read side\n"
55 " shw SHUT_WR : shutdown write side\n"
56 " shb SHUT_RDWR : shutdown both sides\n"
57 " lin disable lingering on the socket\n"
58 " clo close the file descriptor\n"
59 " pol poll() for any event\n"
60 "\n", arg0);
61}
62
63void die(const char *msg)
64{
65 if (msg)
66 fprintf(stderr, "%s\n", msg);
67 exit(1);
68}
69
70const char *get_errno(int ret)
71{
72 static char errmsg[100];
73
74 if (ret >= 0)
75 return "";
76
77 snprintf(errmsg, sizeof(errmsg), " (%s)", strerror(errno));
78 return errmsg;
79}
80
81void do_acc(int fd)
82{
83 int ret;
84
85 calen = sizeof(caddr);
86 ret = accept(lfd, (struct sockaddr*)&caddr, &calen);
87 if (sfd < 0)
88 sfd = ret;
89 if (verbose)
90 printf("cmd #%d stp #%d: %s(%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__, fd, ret, get_errno(ret));
91}
92
93void do_snd(int fd)
94{
95 int ret;
96
97 ret = send(fd, "foo", 3, MSG_NOSIGNAL|MSG_DONTWAIT);
98 if (verbose)
99 printf("cmd #%d stp #%d: %s(%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__, fd, ret, get_errno(ret));
100}
101
102void do_mor(int fd)
103{
104 int ret;
105
106 ret = send(fd, "foo", 3, MSG_NOSIGNAL|MSG_DONTWAIT|MSG_MORE);
107 if (verbose)
108 printf("cmd #%d stp #%d: %s(%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__, fd, ret, get_errno(ret));
109}
110
111void do_rcv(int fd)
112{
113 char buf[10];
114 int ret;
115
116 ret = recv(fd, buf, sizeof(buf), MSG_DONTWAIT);
117 if (verbose)
118 printf("cmd #%d stp #%d: %s(%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__, fd, ret, get_errno(ret));
119}
120
121void do_drn(int fd)
122{
123 char buf[16384];
124 int total = -1;
125 int ret;
126
127 while (1) {
128 ret = recv(fd, buf, sizeof(buf), 0);
129 if (ret <= 0)
130 break;
131 if (total < 0)
132 total = 0;
133 total += ret;
134 }
135
136 if (verbose)
137 printf("cmd #%d stp #%d: %s(%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__, fd, total, get_errno(ret));
138}
139
140void do_shr(int fd)
141{
142 int ret;
143
144 ret = shutdown(fd, SHUT_RD);
145 if (verbose)
146 printf("cmd #%d stp #%d: %s(%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__, fd, ret, get_errno(ret));
147}
148
149void do_shw(int fd)
150{
151 int ret;
152
153 ret = shutdown(fd, SHUT_WR);
154 if (verbose)
155 printf("cmd #%d stp #%d: %s(%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__, fd, ret, get_errno(ret));
156}
157
158void do_shb(int fd)
159{
160 int ret;
161
162 ret = shutdown(fd, SHUT_RDWR);
163 if (verbose)
164 printf("cmd #%d stp #%d: %s(%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__, fd, ret, get_errno(ret));
165}
166
167void do_lin(int fd)
168{
169 struct linger nolinger = { .l_onoff = 1, .l_linger = 0 };
170 int ret;
171
172 ret = setsockopt(fd, SOL_SOCKET, SO_LINGER, &nolinger, sizeof(nolinger));
173 if (verbose)
174 printf("cmd #%d stp #%d: %s(%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__, fd, ret, get_errno(ret));
175}
176
177void do_clo(int fd)
178{
179 int ret;
180
181 ret = close(fd);
182 if (verbose)
183 printf("cmd #%d stp #%d: %s(%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__, fd, ret, get_errno(ret));
184}
185
186void do_pol(int fd)
187{
188 struct pollfd fds = { .fd = fd, .events = POLLIN|POLLOUT|POLLRDHUP, .revents=0 };
189 int ret;
190
191 ret = poll(&fds, 1, 0);
192 if (verbose) {
193 printf("cmd #%d stp #%d: %s(%d): ret=%d%s ev=%#x ", cmd, cmdstep, __FUNCTION__, fd, ret, get_errno(ret), ret > 0 ? fds.revents : 0);
194 if (ret > 0 && fds.revents) {
195 int flags, flag;
196 putchar('(');
197
198 for (flags = fds.revents; flags; flags ^= flag) {
199 flag = flags ^ (flags & (flags - 1)); // keep lowest bit only
200 switch (flag) {
201 case POLLIN: printf("IN"); break;
202 case POLLOUT: printf("OUT"); break;
203 case POLLPRI: printf("PRI"); break;
204 case POLLHUP: printf("HUP"); break;
205 case POLLERR: printf("ERR"); break;
206 case POLLNVAL: printf("NVAL"); break;
207#if POLLRDHUP
208 case POLLRDHUP: printf("RDHUP"); break;
209#endif
210 default: printf("???[%#x]", flag); break;
211 }
212 if (flags ^ flag)
213 putchar(' ');
214 }
215 putchar(')');
216 }
217 putchar('\n');
218 }
219}
220
221int main(int argc, char **argv)
222{
223 const char *arg0;
224 char *word, *next;
225 int fd;
226
227 /* listener */
228 lfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
229 if (lfd < 0)
230 die("socket(l)");
231
232 setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
233
234 memset(&saddr, 0, sizeof(saddr));
235 saddr.sin_family = AF_INET;
236 saddr.sin_port = htons(0);
237 salen = sizeof(saddr);
238
239 if (bind(lfd, (struct sockaddr *)&saddr, salen) < 0)
240 die("bind()");
241
242 if (listen(lfd, 1000) < 0)
243 die("listen()");
244
245 if (getsockname(lfd, (struct sockaddr *)&saddr, &salen) < 0)
246 die("getsockname()");
247
248
249 /* client */
250 cfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
251 if (cfd < 0)
252 die("socket(c)");
253
254 if (connect(cfd, (const struct sockaddr*)&saddr, salen) == -1)
255 die("connect()");
256
257 /* connection is pending in accept queue, accept() will either be
258 * explicit with "-l acc" below, or implicit on "-s <cmd>"
259 */
260
261 arg0 = argv[0];
262 if (argc < 2) {
263 usage(arg0);
264 exit(1);
265 }
266
267 write(1, "#### BEGIN ####\n", 16); // add a visible delimiter in the traces
268
269 while (argc > 1) {
270 argc--; argv++;
271 if (**argv != '-') {
272 usage(arg0);
273 exit(1);
274 }
275
276 fd = -1;
277 switch (argv[0][1]) {
278 case 'h' :
279 usage(arg0);
280 exit(0);
281 break;
282 case 'v' :
283 verbose++;
284 break;
285 case 'c' :
286 cmd++; cmdstep = 0;
287 fd = cfd;
288 break;
289 case 's' :
290 cmd++; cmdstep = 0;
291 if (sfd < 0)
292 do_acc(lfd);
293 if (sfd < 0)
294 die("accept()");
295 fd = sfd;
296 break;
297 case 'l' :
298 cmd++; cmdstep = 0;
299 fd = lfd;
300 break;
301 default : usage(arg0); exit(1); break;
302 }
303
304 if (fd >= 0) { /* an action is required */
305 if (argc < 2) {
306 usage(arg0);
307 exit(1);
308 }
309
310 for (word = argv[1]; word && *word; word = next) {
311 next = strchr(word, ',');
312 if (next)
313 *(next++) = 0;
314 cmdstep++;
315 if (strcmp(word, "acc") == 0) {
316 do_acc(fd);
317 }
318 else if (strcmp(word, "snd") == 0) {
319 do_snd(fd);
320 }
321 else if (strcmp(word, "mor") == 0) {
322 do_mor(fd);
323 }
324 else if (strcmp(word, "rcv") == 0) {
325 do_rcv(fd);
326 }
327 else if (strcmp(word, "drn") == 0) {
328 do_drn(fd);
329 }
330 else if (strcmp(word, "shb") == 0) {
331 do_shb(fd);
332 }
333 else if (strcmp(word, "shr") == 0) {
334 do_shr(fd);
335 }
336 else if (strcmp(word, "shw") == 0) {
337 do_shw(fd);
338 }
339 else if (strcmp(word, "lin") == 0) {
340 do_lin(fd);
341 }
342 else if (strcmp(word, "clo") == 0) {
343 do_clo(fd);
344 }
345 else if (strcmp(word, "pol") == 0) {
346 do_pol(fd);
347 }
348 else {
349 printf("Ignoring unknown action '%s' in step #%d of cmd #%d\n", word, cmdstep, cmd);
350 }
351 }
352 argc--; argv++;
353 }
354 }
355
356 write(1, "#### END ####\n", 14); // add a visible delimiter in the traces
357
358 if (!cmd) {
359 printf("No command was requested!\n");
360 usage(arg0);
361 exit(1);
362 }
363
364 return 0;
365}