1 | // SPDX-License-Identifier: GPL-2.0 |
2 | |
3 | #include <stdio.h> |
4 | #include <stdlib.h> |
5 | #include <fcntl.h> |
6 | #include <string.h> |
7 | #include <unistd.h> |
8 | #include <signal.h> |
9 | |
10 | #include <arpa/inet.h> |
11 | #include <sys/socket.h> |
12 | |
13 | #define PORT 12345 |
14 | #define RUNTIME 10 |
15 | |
16 | static struct { |
17 | unsigned int timeout; |
18 | unsigned int port; |
19 | } opts = { |
20 | .timeout = RUNTIME, |
21 | .port = PORT, |
22 | }; |
23 | |
24 | static void handler(int sig) |
25 | { |
26 | _exit(sig == SIGALRM ? 0 : 1); |
27 | } |
28 | |
29 | static void set_timeout(void) |
30 | { |
31 | struct sigaction action = { |
32 | .sa_handler = handler, |
33 | }; |
34 | |
35 | sigaction(SIGALRM, &action, NULL); |
36 | |
37 | alarm(opts.timeout); |
38 | } |
39 | |
40 | static void do_connect(const struct sockaddr_in *dst) |
41 | { |
42 | int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); |
43 | |
44 | if (s >= 0) |
45 | fcntl(s, F_SETFL, O_NONBLOCK); |
46 | |
47 | connect(s, (struct sockaddr *)dst, sizeof(*dst)); |
48 | close(s); |
49 | } |
50 | |
51 | static void do_accept(const struct sockaddr_in *src) |
52 | { |
53 | int c, one = 1, s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); |
54 | |
55 | if (s < 0) |
56 | return; |
57 | |
58 | setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); |
59 | setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)); |
60 | |
61 | bind(s, (struct sockaddr *)src, sizeof(*src)); |
62 | |
63 | listen(s, 16); |
64 | |
65 | c = accept(s, NULL, NULL); |
66 | if (c >= 0) |
67 | close(c); |
68 | |
69 | close(s); |
70 | } |
71 | |
72 | static int accept_loop(void) |
73 | { |
74 | struct sockaddr_in src = { |
75 | .sin_family = AF_INET, |
76 | .sin_port = htons(opts.port), |
77 | }; |
78 | |
79 | inet_pton(AF_INET, "127.0.0.1" , &src.sin_addr); |
80 | |
81 | set_timeout(); |
82 | |
83 | for (;;) |
84 | do_accept(src: &src); |
85 | |
86 | return 1; |
87 | } |
88 | |
89 | static int connect_loop(void) |
90 | { |
91 | struct sockaddr_in dst = { |
92 | .sin_family = AF_INET, |
93 | .sin_port = htons(opts.port), |
94 | }; |
95 | |
96 | inet_pton(AF_INET, "127.0.0.1" , &dst.sin_addr); |
97 | |
98 | set_timeout(); |
99 | |
100 | for (;;) |
101 | do_connect(dst: &dst); |
102 | |
103 | return 1; |
104 | } |
105 | |
106 | static void parse_opts(int argc, char **argv) |
107 | { |
108 | int c; |
109 | |
110 | while ((c = getopt(argc, argv, "t:p:" )) != -1) { |
111 | switch (c) { |
112 | case 't': |
113 | opts.timeout = atoi(optarg); |
114 | break; |
115 | case 'p': |
116 | opts.port = atoi(optarg); |
117 | break; |
118 | } |
119 | } |
120 | } |
121 | |
122 | int main(int argc, char *argv[]) |
123 | { |
124 | pid_t p; |
125 | |
126 | parse_opts(argc, argv); |
127 | |
128 | p = fork(); |
129 | if (p < 0) |
130 | return 111; |
131 | |
132 | if (p > 0) |
133 | return accept_loop(); |
134 | |
135 | return connect_loop(); |
136 | } |
137 | |