diff options
-rw-r--r-- | lib/Makefile.am | 1 | ||||
-rw-r--r-- | lib/libpies.h | 3 | ||||
-rw-r--r-- | lib/urlconn.c | 157 | ||||
-rw-r--r-- | src/piesctl.c | 122 | ||||
-rw-r--r-- | tests/.gitignore | 1 | ||||
-rw-r--r-- | tests/Makefile.am | 9 | ||||
-rwxr-xr-x | tests/aux/in.test | 22 | ||||
-rw-r--r-- | tests/inet.at | 38 | ||||
-rw-r--r-- | tests/iobuf.h | 141 | ||||
-rw-r--r-- | tests/nt.c | 339 | ||||
-rw-r--r-- | tests/testsuite.at | 1 | ||||
-rw-r--r-- | tests/to.c | 50 |
12 files changed, 761 insertions, 123 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index 7b4eb42..4eb6041 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am | |||
@@ -33,6 +33,7 @@ libpies_a_SOURCES=\ | |||
33 | split3.c\ | 33 | split3.c\ |
34 | strtotok.c\ | 34 | strtotok.c\ |
35 | url.c\ | 35 | url.c\ |
36 | urlconn.c\ | ||
36 | wildmatch.c | 37 | wildmatch.c |
37 | 38 | ||
38 | libpies_a_LIBADD=\ | 39 | libpies_a_LIBADD=\ |
diff --git a/lib/libpies.h b/lib/libpies.h index 76b70fe..40364a6 100644 --- a/lib/libpies.h +++ b/lib/libpies.h | |||
@@ -102,5 +102,8 @@ void sockaddr_to_str (const struct sockaddr *sa, int salen, | |||
102 | size_t *plen); | 102 | size_t *plen); |
103 | char *sockaddr_to_astr (const struct sockaddr *sa, int salen); | 103 | char *sockaddr_to_astr (const struct sockaddr *sa, int salen); |
104 | 104 | ||
105 | /* urlconn.c */ | ||
106 | int url_connect (struct pies_url *url, struct grecs_sockaddr *source_addr); | ||
107 | |||
105 | 108 | ||
106 | 109 | ||
diff --git a/lib/urlconn.c b/lib/urlconn.c new file mode 100644 index 0000000..5994626 --- /dev/null +++ b/lib/urlconn.c | |||
@@ -0,0 +1,157 @@ | |||
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 <sys/types.h> | ||
19 | #include <sys/socket.h> | ||
20 | #include <sys/un.h> | ||
21 | #include <sys/stat.h> | ||
22 | #include <netinet/in.h> | ||
23 | #include <netdb.h> | ||
24 | #include <errno.h> | ||
25 | #include <unistd.h> | ||
26 | #include <fcntl.h> | ||
27 | #include <libpies.h> | ||
28 | #include <grecs.h> | ||
29 | |||
30 | int | ||
31 | url_connect (struct pies_url *url, struct grecs_sockaddr *source_addr) | ||
32 | { | ||
33 | int fd; | ||
34 | union pies_sockaddr_storage addr; | ||
35 | socklen_t socklen; | ||
36 | int flags; | ||
37 | |||
38 | if (strcmp (url->scheme, "unix") == 0 | ||
39 | || strcmp (url->scheme, "file") == 0 | ||
40 | || strcmp (url->scheme, "socket") == 0) | ||
41 | { | ||
42 | struct stat st; | ||
43 | |||
44 | if (url->port) | ||
45 | { | ||
46 | grecs_error (NULL, 0, _("%s: invalid connection type: " | ||
47 | "port is meaningless for UNIX sockets"), | ||
48 | url->string); | ||
49 | errno = EINVAL; | ||
50 | return -1; | ||
51 | } | ||
52 | |||
53 | if (strlen (url->path) > sizeof addr.s_un.sun_path) | ||
54 | { | ||
55 | grecs_error (NULL, 0, | ||
56 | _("%s: UNIX socket name too long"), | ||
57 | url->path); | ||
58 | errno = EINVAL; | ||
59 | return -1; | ||
60 | } | ||
61 | |||
62 | addr.s.sa_family = PF_UNIX; | ||
63 | socklen = sizeof (addr.s_un); | ||
64 | strcpy (addr.s_un.sun_path, url->path); | ||
65 | |||
66 | if (stat (url->path, &st)) | ||
67 | { | ||
68 | if (errno != ENOENT) | ||
69 | { | ||
70 | grecs_error (NULL, errno, _("%s: %s failed"), url->path, "stat"); | ||
71 | return -1; | ||
72 | } | ||
73 | } | ||
74 | else | ||
75 | { | ||
76 | if (!S_ISSOCK (st.st_mode)) | ||
77 | { | ||
78 | grecs_error (NULL, 0, _("%s: not a socket"), url->path); | ||
79 | errno = EINVAL; | ||
80 | return -1; | ||
81 | } | ||
82 | } | ||
83 | } | ||
84 | else if (strcmp (url->scheme, "inet") == 0) | ||
85 | { | ||
86 | struct addrinfo hints; | ||
87 | struct addrinfo *res; | ||
88 | int rc; | ||
89 | |||
90 | memset (&hints, 0, sizeof (hints)); | ||
91 | hints.ai_socktype = SOCK_STREAM; | ||
92 | hints.ai_flags = 0; | ||
93 | hints.ai_family = AF_INET; | ||
94 | |||
95 | rc = getaddrinfo (url->host, url->port_s, &hints, &res); | ||
96 | switch (rc) | ||
97 | { | ||
98 | case 0: | ||
99 | break; | ||
100 | |||
101 | case EAI_SYSTEM: | ||
102 | grecs_error (NULL, errno, "%s", _("cannot parse address")); | ||
103 | return -1; | ||
104 | |||
105 | case EAI_BADFLAGS: | ||
106 | case EAI_SOCKTYPE: | ||
107 | grecs_error (NULL, 0, _("%s:%d: internal error converting address"), | ||
108 | __FILE__,__LINE__); | ||
109 | return -1; | ||
110 | |||
111 | case EAI_MEMORY: | ||
112 | grecs_alloc_die (); | ||
113 | |||
114 | default: | ||
115 | grecs_error (NULL, 0, "getaddrinfo: %s", gai_strerror(rc)); | ||
116 | return -1; | ||
117 | } | ||
118 | |||
119 | socklen = sizeof (addr.s_in); | ||
120 | memcpy (&addr.s_in, res->ai_addr, socklen); | ||
121 | freeaddrinfo (res); | ||
122 | } | ||
123 | |||
124 | fd = socket (addr.s.sa_family, SOCK_STREAM, 0); | ||
125 | if (fd == -1) | ||
126 | { | ||
127 | grecs_error (NULL, errno, _("%s: %s failed"), url->string, "socket"); | ||
128 | return -1; | ||
129 | } | ||
130 | |||
131 | if ((flags = fcntl (fd, F_GETFD, 0)) == -1 | ||
132 | || fcntl (fd, F_SETFD, flags | FD_CLOEXEC) == -1) | ||
133 | grecs_error (NULL, 0, _("%s: cannot set close-on-exec: %s"), | ||
134 | url->string, strerror (errno)); | ||
135 | |||
136 | if (source_addr) | ||
137 | { | ||
138 | if (source_addr->sa->sa_family != addr.s.sa_family) | ||
139 | grecs_error (NULL, 0, | ||
140 | _("source and destination address family differ")); | ||
141 | else if (bind (fd, source_addr->sa, source_addr->len) < 0) | ||
142 | { | ||
143 | grecs_error (NULL, errno, _("%s: %s failed"), url->string, "bind"); | ||
144 | close (fd); | ||
145 | return -1; | ||
146 | } | ||
147 | } | ||
148 | |||
149 | if (connect (fd, &addr.s, socklen)) | ||
150 | { | ||
151 | grecs_error (NULL, errno, _("%s: %s failed"), url->string, "connect"); | ||
152 | close (fd); | ||
153 | return -1; | ||
154 | } | ||
155 | |||
156 | return fd; | ||
157 | } | ||
diff --git a/src/piesctl.c b/src/piesctl.c index 2e35aea..aa983ee 100644 --- a/src/piesctl.c +++ b/src/piesctl.c | |||
@@ -415,123 +415,22 @@ static struct shttp_connection * | |||
415 | shttp_connect (struct pies_url *url, struct grecs_sockaddr *source_addr) | 415 | shttp_connect (struct pies_url *url, struct grecs_sockaddr *source_addr) |
416 | { | 416 | { |
417 | int fd; | 417 | int fd; |
418 | union pies_sockaddr_storage addr; | 418 | struct pies_url *conn_url; |
419 | socklen_t socklen; | ||
420 | int flags; | ||
421 | struct shttp_connection *conn; | 419 | struct shttp_connection *conn; |
422 | FILE *fp; | 420 | FILE *fp; |
423 | |||
424 | if (strcmp (url->scheme, "unix") == 0 | ||
425 | || strcmp (url->scheme, "file") == 0 | ||
426 | || strcmp (url->scheme, "socket") == 0) | ||
427 | { | ||
428 | struct stat st; | ||
429 | |||
430 | if (url->port) | ||
431 | { | ||
432 | grecs_error (NULL, 0, _("%s: invalid connection type: " | ||
433 | "port is meaningless for UNIX sockets"), | ||
434 | url->string); | ||
435 | return NULL; | ||
436 | } | ||
437 | |||
438 | if (strlen (url->path) > sizeof addr.s_un.sun_path) | ||
439 | { | ||
440 | errno = EINVAL; | ||
441 | grecs_error (NULL, 0, | ||
442 | _("%s: UNIX socket name too long"), | ||
443 | url->path); | ||
444 | return NULL; | ||
445 | } | ||
446 | |||
447 | addr.s.sa_family = PF_UNIX; | ||
448 | socklen = sizeof (addr.s_un); | ||
449 | strcpy (addr.s_un.sun_path, url->path); | ||
450 | |||
451 | if (stat (url->path, &st)) | ||
452 | { | ||
453 | if (errno != ENOENT) | ||
454 | { | ||
455 | grecs_error (NULL, errno, _("%s: %s failed"), url->path, "stat"); | ||
456 | return NULL; | ||
457 | } | ||
458 | } | ||
459 | else | ||
460 | { | ||
461 | if (!S_ISSOCK (st.st_mode)) | ||
462 | { | ||
463 | grecs_error (NULL, 0, _("%s: not a socket"), url->path); | ||
464 | return NULL; | ||
465 | } | ||
466 | } | ||
467 | } | ||
468 | else if (strcmp (url->scheme, "inet") == 0) | ||
469 | { | ||
470 | short pnum; | ||
471 | struct hostent *hp; | ||
472 | |||
473 | addr.s_in.sin_family = PF_INET; | ||
474 | socklen = sizeof (addr.s_in); | ||
475 | |||
476 | pnum = url->port ? url->port : 8080; | ||
477 | |||
478 | hp = gethostbyname (url->host); | ||
479 | if (!hp) | ||
480 | { | ||
481 | grecs_error (NULL, 0, _("%s: unknown host name"), url->string); | ||
482 | return NULL; | ||
483 | } | ||
484 | |||
485 | addr.s_in.sin_family = hp->h_addrtype; | ||
486 | switch (hp->h_addrtype) | ||
487 | { | ||
488 | case AF_INET: | ||
489 | memmove (&addr.s_in.sin_addr, hp->h_addr, 4); | ||
490 | addr.s_in.sin_port = htons (pnum); | ||
491 | break; | ||
492 | |||
493 | default: | ||
494 | grecs_error (NULL, 0, | ||
495 | _("%s: invalid connection type: " | ||
496 | "unsupported address family"), | ||
497 | url->string); | ||
498 | return NULL; | ||
499 | } | ||
500 | } | ||
501 | else | ||
502 | { | ||
503 | grecs_error (NULL, 0, _("%s: unsupported protocol"), url->string); | ||
504 | return NULL; | ||
505 | } | ||
506 | 421 | ||
507 | fd = socket (addr.s.sa_family, SOCK_STREAM, 0); | 422 | if (pies_url_copy (&conn_url, url)) |
508 | if (fd == -1) | 423 | grecs_alloc_die (); |
509 | { | ||
510 | grecs_error (NULL, errno, _("%s: %s failed"), url->string, "socket"); | ||
511 | return NULL; | ||
512 | } | ||
513 | 424 | ||
514 | if ((flags = fcntl (fd, F_GETFD, 0)) == -1 | 425 | if (conn_url->port == 0 && strcmp (conn_url->scheme, "inet") == 0) |
515 | || fcntl (fd, F_SETFD, flags | FD_CLOEXEC) == -1) | ||
516 | grecs_error (NULL, 0, _("%s: cannot set close-on-exec: %s"), | ||
517 | url->string, strerror (errno)); | ||
518 | |||
519 | if (source_addr) | ||
520 | { | 426 | { |
521 | if (source_addr->sa->sa_family != addr.s.sa_family) | 427 | conn_url->port_s = grecs_strdup ("8080"); |
522 | grecs_error (NULL, 0, | 428 | conn_url->port = 8080; |
523 | _("source and destination address family differ")); | ||
524 | else if (bind (fd, source_addr->sa, source_addr->len) < 0) | ||
525 | { | ||
526 | grecs_error (NULL, errno, _("%s: %s failed"), url->string, "bind"); | ||
527 | exit (EX_UNAVAILABLE); | ||
528 | } | ||
529 | } | 429 | } |
530 | 430 | fd = url_connect (conn_url, source_addr); | |
531 | if (connect (fd, &addr.s, socklen)) | 431 | if (fd == -1) |
532 | { | 432 | { |
533 | grecs_error (NULL, errno, _("%s: %s failed"), url->string, "connect"); | 433 | pies_url_destroy (&conn_url); |
534 | close (fd); | ||
535 | return NULL; | 434 | return NULL; |
536 | } | 435 | } |
537 | 436 | ||
@@ -545,8 +444,7 @@ shttp_connect (struct pies_url *url, struct grecs_sockaddr *source_addr) | |||
545 | 444 | ||
546 | conn = grecs_zalloc (sizeof (*conn)); | 445 | conn = grecs_zalloc (sizeof (*conn)); |
547 | conn->fp = fp; | 446 | conn->fp = fp; |
548 | if (pies_url_copy (&conn->url, url)) | 447 | conn->url = conn_url; |
549 | grecs_alloc_die (); | ||
550 | 448 | ||
551 | if (!no_netrc_option) | 449 | if (!no_netrc_option) |
552 | netrc_scan (conn->url); | 450 | netrc_scan (conn->url); |
diff --git a/tests/.gitignore b/tests/.gitignore index 459fa44..68159e3 100644 --- a/tests/.gitignore +++ b/tests/.gitignore | |||
@@ -7,3 +7,4 @@ testsuite | |||
7 | testsuite.dir | 7 | testsuite.dir |
8 | testsuite.log | 8 | testsuite.log |
9 | to | 9 | to |
10 | nt | ||
diff --git a/tests/Makefile.am b/tests/Makefile.am index 14f5b4f..0290f61 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am | |||
@@ -20,7 +20,8 @@ AUXTOOLS = \ | |||
20 | aux/mailer\ | 20 | aux/mailer\ |
21 | aux/touchfile\ | 21 | aux/touchfile\ |
22 | aux/sleepexit\ | 22 | aux/sleepexit\ |
23 | aux/startcheck | 23 | aux/startcheck\ |
24 | aux/in.test | ||
24 | 25 | ||
25 | EXTRA_DIST = $(TESTSUITE_AT) testsuite package.m4 $(AUXTOOLS) | 26 | EXTRA_DIST = $(TESTSUITE_AT) testsuite package.m4 $(AUXTOOLS) |
26 | DISTCLEANFILES = atconfig $(check_SCRIPTS) | 27 | DISTCLEANFILES = atconfig $(check_SCRIPTS) |
@@ -52,6 +53,7 @@ TESTSUITE_AT = \ | |||
52 | cyclic.at\ | 53 | cyclic.at\ |
53 | env.at\ | 54 | env.at\ |
54 | envop.at\ | 55 | envop.at\ |
56 | inet.at\ | ||
55 | respawn.at\ | 57 | respawn.at\ |
56 | redirect.at\ | 58 | redirect.at\ |
57 | ret-exec.at\ | 59 | ret-exec.at\ |
@@ -64,9 +66,12 @@ TESTSUITE_AT = \ | |||
64 | TESTSUITE = $(srcdir)/testsuite | 66 | TESTSUITE = $(srcdir)/testsuite |
65 | M4=m4 | 67 | M4=m4 |
66 | 68 | ||
67 | noinst_PROGRAMS = envtest to lines | 69 | noinst_PROGRAMS = envtest to lines nt |
70 | nt_SOURCES = nt.c iobuf.h | ||
68 | AM_CPPFLAGS = \ | 71 | AM_CPPFLAGS = \ |
69 | -I$(top_srcdir)/lib\ | 72 | -I$(top_srcdir)/lib\ |
73 | -I$(top_srcdir)/gnu\ | ||
74 | -I$(top_builddir)/gnu\ | ||
70 | @GRECS_INCLUDES@ | 75 | @GRECS_INCLUDES@ |
71 | 76 | ||
72 | LDADD = \ | 77 | LDADD = \ |
diff --git a/tests/aux/in.test b/tests/aux/in.test new file mode 100755 index 0000000..4a98763 --- /dev/null +++ b/tests/aux/in.test | |||
@@ -0,0 +1,22 @@ | |||
1 | #! /bin/sh | ||
2 | |||
3 | FILE=${1:?} | ||
4 | IFS=' | ||
5 | ' | ||
6 | while read COMMAND ARG | ||
7 | do | ||
8 | echo $COMMAND $ARG >> $FILE | ||
9 | case $COMMAND in | ||
10 | stop) if [ -n "$STOPCMD" ]; then | ||
11 | echo "STOP" | ||
12 | $STOPCMD | ||
13 | else | ||
14 | echo "OK $COMMAND $ARG" | ||
15 | fi | ||
16 | ;; | ||
17 | quit) exit $ARG;; | ||
18 | *) echo "OK $COMMAND $ARG" | ||
19 | ;; | ||
20 | esac | ||
21 | done | ||
22 | |||
diff --git a/tests/inet.at b/tests/inet.at new file mode 100644 index 0000000..41cae6a --- /dev/null +++ b/tests/inet.at | |||
@@ -0,0 +1,38 @@ | |||
1 | AT_SETUP([inet component]) | ||
2 | AT_CHECK([ | ||
3 | PIES_XFAIL_CHECK | ||
4 | PIES_CONTROL_INIT | ||
5 | AT_DATA([input], | ||
6 | [line 1 | ||
7 | line 2 | ||
8 | stop | ||
9 | ]) | ||
10 | : ${PIES_TEST_INET_SOCKET:=unix://$PWD/in.sock} | ||
11 | cat > pies.conf <<_EOT | ||
12 | component in { | ||
13 | command "$auxdir/in.test $PWD/inlog"; | ||
14 | env { | ||
15 | set "STOPCMD=piesctl --url unix:///$PWD/pies.ctl --no-netrc shutdown"; | ||
16 | } | ||
17 | mode inetd; | ||
18 | socket "$PIES_TEST_INET_SOCKET"; | ||
19 | stderr file "$PWD/log.err"; | ||
20 | } | ||
21 | component controller { | ||
22 | command "nt $PIES_TEST_INET_SOCKET -i input"; | ||
23 | } | ||
24 | _EOT | ||
25 | set -e | ||
26 | to 10 \ | ||
27 | pies --foreground --stderr \ | ||
28 | --config-file control.conf --config-file pies.conf --debug 1 2>errlog | ||
29 | |||
30 | cat inlog | ||
31 | cat log.err >&2 | ||
32 | ], | ||
33 | [0], | ||
34 | [line 1 | ||
35 | line 2 | ||
36 | stop | ||
37 | ]) | ||
38 | AT_CLEANUP | ||
diff --git a/tests/iobuf.h b/tests/iobuf.h new file mode 100644 index 0000000..4e43338 --- /dev/null +++ b/tests/iobuf.h | |||
@@ -0,0 +1,141 @@ | |||
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 | #define IO_BUF_SIZE 512 | ||
18 | |||
19 | struct iobuf | ||
20 | { | ||
21 | char buf[IO_BUF_SIZE]; /* Initial line buffer */ | ||
22 | size_t start; /* Start of data */ | ||
23 | size_t avail; /* Start of available space */ | ||
24 | }; | ||
25 | |||
26 | static inline void | ||
27 | iobuf_reset (struct iobuf *bp) | ||
28 | { | ||
29 | bp->start = bp->avail = 0; | ||
30 | } | ||
31 | |||
32 | /* Return the count of data bytes in the buffer */ | ||
33 | static inline size_t | ||
34 | iobuf_data_size (struct iobuf *bp) | ||
35 | { | ||
36 | return bp->avail - bp->start; | ||
37 | } | ||
38 | |||
39 | static inline char * | ||
40 | iobuf_data_ptr (struct iobuf *bp) | ||
41 | { | ||
42 | return bp->buf + bp->start; | ||
43 | } | ||
44 | |||
45 | static inline void | ||
46 | iobuf_data_advance (struct iobuf *bp, size_t n) | ||
47 | { | ||
48 | bp->start += n; | ||
49 | if (bp->start == bp->avail) | ||
50 | iobuf_reset (bp); | ||
51 | } | ||
52 | |||
53 | static inline int | ||
54 | iobuf_putback (struct iobuf *bp) | ||
55 | { | ||
56 | return bp->buf[--bp->start-1]; | ||
57 | } | ||
58 | |||
59 | static inline int | ||
60 | iobuf_getc (struct iobuf *bp) | ||
61 | { | ||
62 | int c = -1; | ||
63 | if (iobuf_data_size (bp) > 0) | ||
64 | { | ||
65 | c = *iobuf_data_ptr (bp); | ||
66 | iobuf_data_advance(bp, 1); | ||
67 | } | ||
68 | return c; | ||
69 | } | ||
70 | |||
71 | /* Return the count of available bytes in the buffer */ | ||
72 | static inline size_t | ||
73 | iobuf_avail_size (struct iobuf *bp) | ||
74 | { | ||
75 | return sizeof (bp->buf) - bp->avail; | ||
76 | } | ||
77 | |||
78 | static inline char * | ||
79 | iobuf_avail_ptr (struct iobuf *bp) | ||
80 | { | ||
81 | return bp->buf + bp->avail; | ||
82 | } | ||
83 | |||
84 | static inline void | ||
85 | iobuf_avail_advance (struct iobuf *bp, size_t n) | ||
86 | { | ||
87 | bp->avail += n; | ||
88 | if (iobuf_avail_size (bp) == 0 && iobuf_data_size (bp) == 0) | ||
89 | iobuf_reset (bp); | ||
90 | } | ||
91 | |||
92 | static inline int | ||
93 | iobuf_putc (struct iobuf *bp, int c) | ||
94 | { | ||
95 | if (iobuf_avail_size (bp) > 0) | ||
96 | { | ||
97 | *iobuf_avail_ptr (bp) = c; | ||
98 | iobuf_avail_advance (bp, 1); | ||
99 | return c; | ||
100 | } | ||
101 | return -1; | ||
102 | } | ||
103 | |||
104 | static inline ssize_t | ||
105 | iobuf_fill (struct iobuf *bp, int fd) | ||
106 | { | ||
107 | ssize_t n = read (fd, iobuf_avail_ptr (bp), iobuf_avail_size (bp)); | ||
108 | if (n >= 0) | ||
109 | iobuf_avail_advance (bp, n); | ||
110 | return n; | ||
111 | } | ||
112 | |||
113 | static inline ssize_t | ||
114 | iobuf_flush (struct iobuf *bp, int fd) | ||
115 | { | ||
116 | ssize_t n = write (fd, iobuf_data_ptr (bp), iobuf_data_size (bp)); | ||
117 | if (n >= 0) | ||
118 | iobuf_data_advance (bp, n); | ||
119 | return n; | ||
120 | } | ||
121 | |||
122 | static inline ssize_t | ||
123 | iobuf_copy (struct iobuf *dst, struct iobuf *src) | ||
124 | { | ||
125 | ssize_t n = iobuf_data_size (src); | ||
126 | if (n > 0) | ||
127 | { | ||
128 | ssize_t avail = iobuf_avail_size (dst); | ||
129 | if (avail < n) | ||
130 | n = avail; | ||
131 | if (n) | ||
132 | { | ||
133 | memcpy (iobuf_avail_ptr (dst), iobuf_data_ptr (src), n); | ||
134 | iobuf_avail_advance (dst, n); | ||
135 | iobuf_data_advance (src, n); | ||
136 | } | ||
137 | } | ||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | |||
diff --git a/tests/nt.c b/tests/nt.c new file mode 100644 index 0000000..6ee18f8 --- /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 | |||
29 | typedef struct netcat_server netcat_server_t; | ||
30 | typedef struct netcat_stream netcat_stream_t; | ||
31 | typedef ssize_t (*netcat_stream_io_t) (netcat_server_t *srv); | ||
32 | typedef int (*netcat_stream_disconnect_t) (netcat_server_t *srv); | ||
33 | |||
34 | enum { IN, OUT }; | ||
35 | |||
36 | struct 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 | |||
47 | static netcat_server_t *server_head, *server_tail; | ||
48 | |||
49 | netcat_server_t * | ||
50 | netcat_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 | |||
76 | void | ||
77 | netcat_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 | /* | ||
94 | net_reader | ||
95 | 1. If there is free space in the IN buffer, fill it with the data read | ||
96 | from the fd and return | ||
97 | 2. Otherwise, clear the POLLIN bit. | ||
98 | |||
99 | stdout_writer (peer -> net_reader) | ||
100 | 1. 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. | ||
103 | 2. Write as much as possible from OUT to fd | ||
104 | |||
105 | stdin_reader (same as net_reader) | ||
106 | |||
107 | net_writer (peer -> stdin_reader) same as stdout_writer | ||
108 | */ | ||
109 | |||
110 | ssize_t | ||
111 | netcat_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 | |||
142 | static inline int | ||
143 | peer_is_state (netcat_server_t *srv, int state) | ||
144 | { | ||
145 | return srv->peer && (srv->peer->state & state); | ||
146 | } | ||
147 | |||
148 | ssize_t | ||
149 | netcat_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 | |||
185 | int | ||
186 | disconnect_in (netcat_server_t *srv) | ||
187 | { | ||
188 | return shutdown (srv->pollfd->fd, SHUT_RD); | ||
189 | } | ||
190 | |||
191 | int | ||
192 | disconnect_out (netcat_server_t *srv) | ||
193 | { | ||
194 | return shutdown (srv->pollfd->fd, SHUT_WR); | ||
195 | } | ||
196 | |||
197 | int | ||
198 | disconnect_stdin (netcat_server_t *srv) | ||
199 | { | ||
200 | return close (srv->pollfd->fd); | ||
201 | } | ||
202 | |||
203 | int | ||
204 | disconnect_stdout (netcat_server_t *srv) | ||
205 | { | ||
206 | close (srv->pollfd->fd); | ||
207 | exit (0); | ||
208 | } | ||
209 | |||
210 | static int | ||
211 | netcat (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 | |||
283 | static void | ||
284 | redirect (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 | |||
301 | static void | ||
302 | usage (FILE *fp) | ||
303 | { | ||
304 | fprintf (fp, "usage: nt [-i FILE] [-o FILE] URL\n"); | ||
305 | } | ||
306 | |||
307 | int | ||
308 | main (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 | } | ||
diff --git a/tests/testsuite.at b/tests/testsuite.at index e340a2b..822ac7b 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at | |||
@@ -68,6 +68,7 @@ m4_include([ret-notify.at]) | |||
68 | m4_include([startup.at]) | 68 | m4_include([startup.at]) |
69 | m4_include([shutdown.at]) | 69 | m4_include([shutdown.at]) |
70 | m4_include([shell.at]) | 70 | m4_include([shell.at]) |
71 | m4_include([inet.at]) | ||
71 | 72 | ||
72 | m4_include([envop.at]) | 73 | m4_include([envop.at]) |
73 | m4_include([env.at]) \ No newline at end of file | 74 | m4_include([env.at]) \ No newline at end of file |
@@ -1,9 +1,28 @@ | |||
1 | #include <config.h> | ||
1 | #include <stdlib.h> | 2 | #include <stdlib.h> |
2 | #include <stdio.h> | 3 | #include <stdio.h> |
3 | #include <errno.h> | 4 | #include <errno.h> |
4 | #include <unistd.h> | 5 | #include <unistd.h> |
5 | #include <sys/types.h> | 6 | #include <sys/types.h> |
6 | #include <sys/wait.h> | 7 | #include <sys/wait.h> |
8 | #include <signal.h> | ||
9 | |||
10 | int volatile got_sigchld, got_sigalrm; | ||
11 | |||
12 | void | ||
13 | sighan (int sig) | ||
14 | { | ||
15 | switch (sig) | ||
16 | { | ||
17 | case SIGCHLD: | ||
18 | got_sigchld = 1; | ||
19 | break; | ||
20 | |||
21 | case SIGALRM: | ||
22 | got_sigalrm = 1; | ||
23 | break; | ||
24 | } | ||
25 | } | ||
7 | 26 | ||
8 | int | 27 | int |
9 | main (int argc, char **argv) | 28 | main (int argc, char **argv) |
@@ -13,7 +32,7 @@ main (int argc, char **argv) | |||
13 | char *p; | 32 | char *p; |
14 | pid_t pid, ret; | 33 | pid_t pid, ret; |
15 | int status; | 34 | int status; |
16 | 35 | ||
17 | if (argc < 3) | 36 | if (argc < 3) |
18 | { | 37 | { |
19 | fprintf (stderr, "usage: %s TIMEOUT COMMAND ...\n", progname); | 38 | fprintf (stderr, "usage: %s TIMEOUT COMMAND ...\n", progname); |
@@ -30,13 +49,16 @@ main (int argc, char **argv) | |||
30 | argc -= 2; | 49 | argc -= 2; |
31 | argv += 2; | 50 | argv += 2; |
32 | 51 | ||
52 | signal (SIGALRM, sighan); | ||
53 | signal (SIGCHLD, sighan); | ||
54 | |||
33 | pid = fork (); | 55 | pid = fork (); |
34 | if (pid == -1) | 56 | if (pid == -1) |
35 | { | 57 | { |
36 | perror ("fork"); | 58 | perror ("fork"); |
37 | exit (127); | 59 | exit (127); |
38 | } | 60 | } |
39 | 61 | ||
40 | if (pid == 0) | 62 | if (pid == 0) |
41 | { | 63 | { |
42 | execvp (argv[0], argv); | 64 | execvp (argv[0], argv); |
@@ -45,8 +67,23 @@ main (int argc, char **argv) | |||
45 | } | 67 | } |
46 | 68 | ||
47 | alarm (n); | 69 | alarm (n); |
70 | while (1) | ||
71 | { | ||
72 | pause (); | ||
73 | if (got_sigchld) | ||
74 | { | ||
75 | alarm (0); | ||
76 | break; | ||
77 | } | ||
78 | if (got_sigalrm) | ||
79 | { | ||
80 | fprintf (stderr, "%s: timed out\n", progname); | ||
81 | kill (pid, SIGKILL); | ||
82 | exit (127); | ||
83 | } | ||
84 | } | ||
85 | |||
48 | ret = wait (&status); | 86 | ret = wait (&status); |
49 | alarm (0); | ||
50 | 87 | ||
51 | if (ret != pid) | 88 | if (ret != pid) |
52 | { | 89 | { |
@@ -56,7 +93,7 @@ main (int argc, char **argv) | |||
56 | 93 | ||
57 | if (WIFEXITED (status)) | 94 | if (WIFEXITED (status)) |
58 | return WEXITSTATUS (status); | 95 | return WEXITSTATUS (status); |
59 | 96 | ||
60 | if (WIFSIGNALED (status)) | 97 | if (WIFSIGNALED (status)) |
61 | fprintf (stderr, "%s: %s terminated on signal %d\n", progname, argv[0], | 98 | fprintf (stderr, "%s: %s terminated on signal %d\n", progname, argv[0], |
62 | WTERMSIG (status)); | 99 | WTERMSIG (status)); |
@@ -67,8 +104,3 @@ main (int argc, char **argv) | |||
67 | progname, argv[0], status); | 104 | progname, argv[0], status); |
68 | return 127; | 105 | return 127; |
69 | } | 106 | } |
70 | |||
71 | |||
72 | |||
73 | |||
74 | |||