summaryrefslogtreecommitdiffabout
path: root/tests/nt.c
Unidiff
Diffstat (limited to 'tests/nt.c') (more/less context) (ignore whitespace changes)
-rw-r--r--tests/nt.c339
1 files changed, 339 insertions, 0 deletions
diff --git a/tests/nt.c b/tests/nt.c
new file mode 100644
index 0000000..6ee18f8
--- a/dev/null
+++ b/tests/nt.c
@@ -0,0 +1,339 @@
1/* This file is part of GNU Pies.
2 Copyright (C) 2019 Sergey Poznyakoff
3
4 GNU Pies is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 GNU Pies is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Pies. If not, see <http://www.gnu.org/licenses/>. */
16
17#include <config.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <sys/types.h>
21#include <sys/stat.h>
22#include <fcntl.h>
23#include <unistd.h>
24#include <poll.h>
25#include <errno.h>
26#include <libpies.h>
27#include <iobuf.h>
28
29typedef struct netcat_server netcat_server_t;
30typedef struct netcat_stream netcat_stream_t;
31typedef ssize_t (*netcat_stream_io_t) (netcat_server_t *srv);
32typedef int (*netcat_stream_disconnect_t) (netcat_server_t *srv);
33
34enum { IN, OUT };
35
36struct netcat_server
37{
38 char const *id;
39 netcat_server_t *next, *prev;
40 struct pollfd *pollfd;
41 int state;
42 netcat_stream_disconnect_t disconnect;
43 netcat_server_t *peer;
44 struct iobuf buf[2];
45};
46
47static netcat_server_t *server_head, *server_tail;
48
49netcat_server_t *
50netcat_server_create (char const *id, struct pollfd *pfd, int state,
51 netcat_stream_disconnect_t dis, netcat_server_t *peer)
52{
53 netcat_server_t *srv = grecs_zalloc (sizeof (*srv));
54 srv->id = id;
55 srv->pollfd = pfd;
56 srv->state = state;
57 srv->pollfd->events |= srv->state;
58 srv->disconnect = dis;
59 srv->peer = peer;
60 if (peer)
61 srv->peer->peer = srv;
62 iobuf_reset (&srv->buf[IN]);
63 iobuf_reset (&srv->buf[OUT]);
64
65 srv->prev = server_tail;
66 srv->next = NULL;
67 if (server_tail)
68 server_tail->next = srv;
69 else
70 server_head = srv;
71 server_tail = srv;
72
73 return srv;
74}
75
76void
77netcat_server_remove (netcat_server_t *srv)
78{
79 netcat_server_t *p;
80 if ((p = srv->next) != NULL)
81 p->prev = srv->prev;
82 else
83 server_tail = srv->prev;
84 if ((p = srv->prev) != NULL)
85 p->next = srv->next;
86 else
87 server_head = srv->next;
88 if (srv->peer)
89 srv->peer->peer = NULL;
90 free (srv);
91}
92
93/*
94net_reader
951. If there is free space in the IN buffer, fill it with the data read
96from the fd and return
972. Otherwise, clear the POLLIN bit.
98
99stdout_writer (peer -> net_reader)
1001. If OUT buffer is empty
101 1.1 If the peer's IN buffer is not empty, transfer data from it
102 1.2 Else raise the peers POLLIN bit and return.
1032. Write as much as possible from OUT to fd
104
105stdin_reader (same as net_reader)
106
107net_writer (peer -> stdin_reader) same as stdout_writer
108*/
109
110ssize_t
111netcat_stream_read (netcat_server_t *srv)
112{
113 ssize_t n;
114
115 if (iobuf_avail_size (&srv->buf[IN]))
116 {
117 n = iobuf_fill (&srv->buf[IN], srv->pollfd->fd);
118 if (n == -1)
119 {
120 grecs_error (NULL, errno, "%s: read", srv->id);
121 srv->pollfd->events &= ~POLLIN;
122 return -1;
123 }
124 if (n == 0)
125 {
126 /* No more input is expected */
127 srv->disconnect (srv);
128 srv->state &= ~POLLIN;
129 srv->pollfd->events &= ~POLLIN;
130 if (srv->pollfd->events == 0)
131 srv->pollfd->fd = -1;
132 }
133 }
134 else
135 {
136 srv->pollfd->events &= ~POLLIN;
137 n = 0; /* catch timeout? */
138 }
139 return n;
140}
141
142static inline int
143peer_is_state (netcat_server_t *srv, int state)
144{
145 return srv->peer && (srv->peer->state & state);
146}
147
148ssize_t
149netcat_stream_write (netcat_server_t *srv)
150{
151 ssize_t n;
152
153 if (iobuf_data_size (&srv->buf[OUT]) == 0)
154 {
155 if (!peer_is_state (srv, POLLIN))
156 {
157 // shutdown write end
158 srv->disconnect (srv);
159 srv->state &= ~POLLOUT;
160 srv->pollfd->events &= ~POLLOUT;
161 if (srv->pollfd->events == 0)
162 srv->pollfd->fd = -1;
163 return -1;
164 }
165 if (iobuf_copy (&srv->buf[OUT], &srv->peer->buf[IN]) == 0)
166 {
167 srv->peer->pollfd->events |= POLLIN;
168 return 0;
169 }
170 }
171 n = iobuf_flush (&srv->buf[OUT], srv->pollfd->fd);
172 if (n == -1)
173 {
174 grecs_error (NULL, errno, "%s: write", srv->id);
175 return -1;
176 }
177 if (n == 0)
178 {
179 // FIXME: eof
180 return -1;
181 }
182 return 0;
183}
184
185int
186disconnect_in (netcat_server_t *srv)
187{
188 return shutdown (srv->pollfd->fd, SHUT_RD);
189}
190
191int
192disconnect_out (netcat_server_t *srv)
193{
194 return shutdown (srv->pollfd->fd, SHUT_WR);
195}
196
197int
198disconnect_stdin (netcat_server_t *srv)
199{
200 return close (srv->pollfd->fd);
201}
202
203int
204disconnect_stdout (netcat_server_t *srv)
205{
206 close (srv->pollfd->fd);
207 exit (0);
208}
209
210static int
211netcat (char const *urlstr)
212{
213 int fd;
214 struct pies_url *url;
215 struct pollfd pfd[3];
216 int nfd = sizeof (pfd) / sizeof (pfd[0]);
217 netcat_server_t *srvin, *srvout, *srv;
218
219 if (pies_url_create (&url, urlstr))
220 {
221 perror (urlstr);
222 return 64;
223 }
224
225 fd = url_connect (url, NULL);
226 if (fd == -1)
227 return 1;
228
229 pfd[0].fd = 0;
230 pfd[0].events = 0;
231 srvin = netcat_server_create ("stdin", &pfd[0], POLLIN, disconnect_stdin, NULL);
232
233 pfd[1].fd = 1;
234 pfd[1].events = 0;
235 srvout = netcat_server_create ("stdout", &pfd[1], POLLOUT, disconnect_stdout, NULL);
236
237 pfd[2].fd = fd;
238 pfd[2].events = 0;
239
240 netcat_server_create ("netread", &pfd[2], POLLIN, disconnect_in, srvout);
241 netcat_server_create ("netwrite", &pfd[2], POLLOUT, disconnect_out, srvin);
242
243 while (server_head)
244 {
245 ssize_t n = poll (pfd, nfd, -1);
246 if (n == -1)
247 {
248 if (errno != EINTR)
249 grecs_error (NULL, errno, "poll");
250 continue;
251 }
252 for (srv = server_head; srv; )
253 {
254 netcat_server_t *next = srv->next;
255 int events;
256
257 events = (srv->pollfd->events|POLLHUP)
258 & (srv->pollfd->revents & (srv->state|POLLHUP));
259 if (events)
260 {
261 if (events & POLLHUP)
262 {
263 //grecs_error (NULL, 0, "HUP on %s", srv->id);
264 srv->disconnect (srv);
265 srv->pollfd->events &= ~srv->state;
266 if (srv->pollfd->events == 0)
267 srv->pollfd->fd = -1;
268 srv->state = 0;
269 }
270 else if (events & POLLIN)
271 netcat_stream_read (srv);
272 else if (events & POLLOUT)
273 netcat_stream_write (srv);
274 }
275 if (srv->state == 0 || srv->pollfd->fd == -1)
276 netcat_server_remove (srv);
277 srv = next;
278 }
279 }
280 return 0;
281}
282
283static void
284redirect (int sfd, char const *name)
285{
286 int fd;
287
288 fd = open (name, sfd ? (O_WRONLY | O_TRUNC) : O_RDONLY);
289 if (!fd)
290 {
291 perror (name);
292 exit (1);
293 }
294 if (dup2 (fd, sfd))
295 {
296 perror ("dup2");
297 exit (1);
298 }
299}
300
301static void
302usage (FILE *fp)
303{
304 fprintf (fp, "usage: nt [-i FILE] [-o FILE] URL\n");
305}
306
307int
308main (int argc, char **argv)
309{
310 int c;
311
312 while ((c = getopt (argc, argv, "i:o:")) != EOF)
313 {
314 switch (c)
315 {
316 case 'i':
317 redirect (0, optarg);
318 break;
319
320 case 'o':
321 redirect (1, optarg);
322 break;
323
324 default:
325 exit (64);
326 }
327 }
328
329 argc -= optind;
330 argv += optind;
331
332 if (argc != 1)
333 {
334 usage (stderr);
335 exit (64);
336 }
337
338 return netcat (argv[0]);
339}

Return to:

Send suggestions and report system problems to the System administrator.