Merge changes I6ba143f5,I3f2398d5,I13a06f60

* changes:
  Merge remote-tracking branch 'toybox/master' into HEAD
  netcat: Add UNIX domain socket support (-U)
  More toysh flow control plumbing.
This commit is contained in:
Elliott Hughes 2019-07-03 00:53:32 +00:00 committed by Gerrit Code Review
commit 654d5f24bf
5 changed files with 145 additions and 58 deletions

View File

@ -1892,12 +1892,13 @@
#undef FLAG_n
#endif
// netcat ^tlLw#<1W#<1p#<1>65535q#<1s:f:46u[!tlL][!Lw][!46] ^tlLw#<1W#<1p#<1>65535q#<1s:f:46u[!tlL][!Lw][!46]
// netcat ^tlLw#<1W#<1p#<1>65535q#<1s:f:46uU[!tlL][!Lw][!46U] ^tlLw#<1W#<1p#<1>65535q#<1s:f:46uU[!tlL][!Lw][!46U]
#undef OPTSTR_netcat
#define OPTSTR_netcat "^tlLw#<1W#<1p#<1>65535q#<1s:f:46u[!tlL][!Lw][!46]"
#define OPTSTR_netcat "^tlLw#<1W#<1p#<1>65535q#<1s:f:46uU[!tlL][!Lw][!46U]"
#ifdef CLEANUP_netcat
#undef CLEANUP_netcat
#undef FOR_netcat
#undef FLAG_U
#undef FLAG_u
#undef FLAG_6
#undef FLAG_4
@ -4922,18 +4923,19 @@
#ifndef TT
#define TT this.netcat
#endif
#define FLAG_u (1<<0)
#define FLAG_6 (1<<1)
#define FLAG_4 (1<<2)
#define FLAG_f (1<<3)
#define FLAG_s (1<<4)
#define FLAG_q (1<<5)
#define FLAG_p (1<<6)
#define FLAG_W (1<<7)
#define FLAG_w (1<<8)
#define FLAG_L (1<<9)
#define FLAG_l (1<<10)
#define FLAG_t (1<<11)
#define FLAG_U (1<<0)
#define FLAG_u (1<<1)
#define FLAG_6 (1<<2)
#define FLAG_4 (1<<3)
#define FLAG_f (1<<4)
#define FLAG_s (1<<5)
#define FLAG_q (1<<6)
#define FLAG_p (1<<7)
#define FLAG_W (1<<8)
#define FLAG_w (1<<9)
#define FLAG_L (1<<10)
#define FLAG_l (1<<11)
#define FLAG_t (1<<12)
#endif
#ifdef FOR_netstat

View File

@ -122,7 +122,7 @@
#define HELP_netstat "usage: netstat [-pWrxwutneal]\n\nDisplay networking information. Default is netstat -tuwx\n\n-r Routing table\n-a All sockets (not just connected)\n-l Listening server sockets\n-t TCP sockets\n-u UDP sockets\n-w Raw sockets\n-x Unix sockets\n-e Extended info\n-n Don't resolve names\n-W Wide display\n-p Show PID/program name of sockets"
#define HELP_netcat "usage: netcat [-46t] [-lL COMMAND...] [-u] [-wpq #] [-s addr] {IPADDR PORTNUM|-f FILENAME}\n\nForward stdin/stdout to a file or network connection.\n\n-4 Force IPv4\n-6 Force IPv6\n-L Listen for multiple incoming connections (server mode)\n-W SECONDS timeout for more data on an idle connection\n-f Use FILENAME (ala /dev/ttyS0) instead of network\n-l Listen for one incoming connection\n-p Local port number\n-q Quit SECONDS after EOF on stdin, even if stdout hasn't closed yet\n-s Local source address\n-t Allocate tty (must come before -l or -L)\n-u Use UDP\n-w SECONDS timeout to establish connection\n\nUse \"stty 115200 -F /dev/ttyS0 && stty raw -echo -ctlecho\" with\nnetcat -f to connect to a serial port.\n\nThe command line after -l or -L is executed (as a child process) to handle\neach incoming connection. If blank -l waits for a connection and forwards\nit to stdin/stdout. If no -p specified, -l prints port it bound to and\nbackgrounds itself (returning immediately).\n\nFor a quick-and-dirty server, try something like:\nnetcat -s 127.0.0.1 -p 1234 -tL /bin/bash -l"
#define HELP_netcat "usage: netcat [-46Ut] [-lL COMMAND...] [-u] [-wpq #] [-s addr] {IPADDR PORTNUM|-f FILENAME}\n\nForward stdin/stdout to a file or network connection.\n\n-4 Force IPv4\n-6 Force IPv6\n-L Listen for multiple incoming connections (server mode)\n-U Use a UNIX domain socket\n-W SECONDS timeout for more data on an idle connection\n-f Use FILENAME (ala /dev/ttyS0) instead of network\n-l Listen for one incoming connection\n-p Local port number\n-q Quit SECONDS after EOF on stdin, even if stdout hasn't closed yet\n-s Local source address\n-t Allocate tty (must come before -l or -L)\n-u Use UDP\n-w SECONDS timeout to establish connection\n\nUse \"stty 115200 -F /dev/ttyS0 && stty raw -echo -ctlecho\" with\nnetcat -f to connect to a serial port.\n\nThe command line after -l or -L is executed (as a child process) to handle\neach incoming connection. If blank -l waits for a connection and forwards\nit to stdin/stdout. If no -p specified, -l prints port it bound to and\nbackgrounds itself (returning immediately).\n\nFor a quick-and-dirty server, try something like:\nnetcat -s 127.0.0.1 -p 1234 -tL /bin/bash -l"
#define HELP_microcom "usage: microcom [-s SPEED] [-X] DEVICE\n\nSimple serial console.\n\n-s Set baud rate to SPEED\n-X Ignore ^@ (send break) and ^] (exit)"

View File

@ -169,7 +169,7 @@ USE_MV(NEWTOY(mv, "<2vnF(remove-destination)fi[-ni]", TOYFLAG_BIN))
USE_NBD_CLIENT(OLDTOY(nbd-client, nbd_client, TOYFLAG_USR|TOYFLAG_BIN))
USE_NBD_CLIENT(NEWTOY(nbd_client, "<3>3ns", 0))
USE_NETCAT(OLDTOY(nc, netcat, TOYFLAG_USR|TOYFLAG_BIN))
USE_NETCAT(NEWTOY(netcat, USE_NETCAT_LISTEN("^tlL")"w#<1W#<1p#<1>65535q#<1s:f:46u"USE_NETCAT_LISTEN("[!tlL][!Lw]")"[!46]", TOYFLAG_BIN))
USE_NETCAT(NEWTOY(netcat, USE_NETCAT_LISTEN("^tlL")"w#<1W#<1p#<1>65535q#<1s:f:46uU"USE_NETCAT_LISTEN("[!tlL][!Lw]")"[!46U]", TOYFLAG_BIN))
USE_NETSTAT(NEWTOY(netstat, "pWrxwutneal", TOYFLAG_BIN))
USE_NICE(NEWTOY(nice, "^<1n#", TOYFLAG_BIN))
USE_NL(NEWTOY(nl, "v#<1=1l#w#<0=6Eb:n:s:", TOYFLAG_USR|TOYFLAG_BIN))

View File

@ -7,13 +7,13 @@
* netcat -L zombies
USE_NETCAT(OLDTOY(nc, netcat, TOYFLAG_USR|TOYFLAG_BIN))
USE_NETCAT(NEWTOY(netcat, USE_NETCAT_LISTEN("^tlL")"w#<1W#<1p#<1>65535q#<1s:f:46u"USE_NETCAT_LISTEN("[!tlL][!Lw]")"[!46]", TOYFLAG_BIN))
USE_NETCAT(NEWTOY(netcat, USE_NETCAT_LISTEN("^tlL")"w#<1W#<1p#<1>65535q#<1s:f:46uU"USE_NETCAT_LISTEN("[!tlL][!Lw]")"[!46U]", TOYFLAG_BIN))
config NETCAT
bool "netcat"
default y
help
usage: netcat [-46] [-u] [-wpq #] [-s addr] {IPADDR PORTNUM|-f FILENAME}
usage: netcat [-46U] [-u] [-wpq #] [-s addr] {IPADDR PORTNUM|-f FILENAME}
Forward stdin/stdout to a file or network connection.
@ -24,6 +24,7 @@ config NETCAT
-q Quit SECONDS after EOF on stdin, even if stdout hasn't closed yet
-s Local source address
-u Use UDP
-U Use a UNIX domain socket
-w SECONDS timeout to establish connection
-W SECONDS timeout for more data on an idle connection
@ -85,11 +86,13 @@ void netcat_main(void)
// The argument parsing logic can't make "<2" conditional on other
// arguments like -f and -l, so do it by hand here.
if ((toys.optflags&FLAG_f) ? toys.optc :
(!(toys.optflags&(FLAG_l|FLAG_L)) && toys.optc!=2))
(!(toys.optflags&(FLAG_l|FLAG_L)) &&
toys.optc!=(toys.optflags&FLAG_U?1:2)))
help_exit("bad argument count");
if (toys.optflags&FLAG_4) family = AF_INET;
else if (toys.optflags&FLAG_6) family = AF_INET6;
else if (toys.optflags&FLAG_U) family = AF_UNIX;
if (toys.optflags&FLAG_u) type = SOCK_DGRAM;
@ -97,9 +100,28 @@ void netcat_main(void)
else {
// Setup socket
if (!(toys.optflags&(FLAG_L|FLAG_l))) {
struct addrinfo *addr = xgetaddrinfo(toys.optargs[0], toys.optargs[1],
family, type, 0, 0);
sockfd = xconnect(addr);
if (toys.optflags&FLAG_U) {
struct sockaddr_un sockaddr;
memset(&sockaddr, 0, sizeof(struct sockaddr_un));
if (strlen(toys.optargs[0]) + 1 > sizeof(sockaddr.sun_path))
error_exit("socket path too long %s", toys.optargs[0]);
strcpy(sockaddr.sun_path, toys.optargs[0]);
sockaddr.sun_family = AF_UNIX;
sockfd = xsocket(AF_UNIX, type | SOCK_CLOEXEC, 0);
if (connect(sockfd, (struct sockaddr*)&sockaddr,
sizeof(sockaddr)) != 0) {
perror_exit("could not bind to unix domain socket");
}
} else {
struct addrinfo *addr = xgetaddrinfo(toys.optargs[0], toys.optargs[1],
family, type, 0, 0);
sockfd = xconnect(addr);
}
// We have a connection. Disarm timeout.
set_alarm(0);
@ -109,14 +131,33 @@ void netcat_main(void)
pollinate(in1, in2, out1, out2, TT.W, TT.q);
} else {
// Listen for incoming connections
struct sockaddr* address = (void*)toybuf;
socklen_t len = sizeof(struct sockaddr_storage);
if (toys.optflags&FLAG_U) {
struct sockaddr_un sockaddr;
sprintf(toybuf, "%ld", TT.p);
sockfd = xbind(xgetaddrinfo(TT.s, toybuf, family, type, 0, 0));
memset(&sockaddr, 0, sizeof(struct sockaddr_un));
if (!(toys.optflags&FLAG_s))
error_exit("-s must be provided if using -U with -L/-l");
if (strlen(TT.s) + 1 > sizeof(sockaddr.sun_path))
error_exit("socket path too long %s", TT.s);
strcpy(sockaddr.sun_path, TT.s);
sockaddr.sun_family = AF_UNIX;
sockfd = xsocket(AF_UNIX, type | SOCK_CLOEXEC, 0);
if (bind(sockfd, (struct sockaddr*)&sockaddr,
sizeof(struct sockaddr_un)) != 0) {
perror_exit("unable to bind to UNIX domain socket");
}
} else {
sprintf(toybuf, "%ld", TT.p);
sockfd = xbind(xgetaddrinfo(TT.s, toybuf, family, type, 0, 0));
}
if (listen(sockfd, 5)) error_exit("listen");
if (!TT.p) {
if (!TT.p && !(toys.optflags&FLAG_U)) {
struct sockaddr* address = (void*)toybuf;
socklen_t len = sizeof(struct sockaddr_storage);
short port_be;
getsockname(sockfd, address, &len);
@ -136,7 +177,7 @@ void netcat_main(void)
do {
child = 0;
in1 = out2 = accept(sockfd, (struct sockaddr *)address, &len);
in1 = out2 = accept(sockfd, NULL, NULL);
if (in1<0) perror_exit("accept");
// We have a connection. Disarm timeout.

View File

@ -184,7 +184,7 @@ static void run_command(struct sh_process *pp)
pipe[0] = 0;
pipe[1] = 1;
if (-1 == (pp->pid = xpopen_both(pp->arg.v, pipe)))
perror_msg("%s: not found", *pp->arg.v);
perror_msg("%s: vfork", *pp->arg.v);
else pp->exit = xpclose_both(pp->pid, 0);
}
@ -290,7 +290,7 @@ static char *parse_word(char *start)
// pointer to start of unused part of line if it needs another line of input.
static char *parse_line(char *line, struct double_list **pipeline)
{
char *start = line, *end, *s;
char *start = line, *end, *s, *ex, *add;
struct sh_arg *arg = 0;
struct double_list *pl, *expect = 0;
unsigned i, paren = 0;
@ -350,40 +350,81 @@ static char *parse_line(char *line, struct double_list **pipeline)
for (pl = *pipeline; pl ; pl = (pl->next == *pipeline) ? 0 : pl->next) {
arg = (void *)pl->data;
if (!arg->c) continue;
add = 0;
// parse flow control statements in this command line
for (i = 0; i<arg->c; i++) {
char *ex = expect ? expect->prev->data : 0;
for (i = 0; ; i++) {
ex = expect ? expect->prev->data : 0;
s = arg->v[i];
if (!strcmp(s, "if")) ex = "then";
else if (!strcmp(s, "for") || !strcmp(s, "select")
|| !strcmp(s, "while") || !strcmp(s, "until")) ex = "do";
else if (!strcmp(s, "case")) ex = "esac";
else if (!strcmp(s, "{")) ex = "}";
else if (!strcmp(s, "[[")) ex = "]]";
// If we expect a non-flow-control command, eat rest of line
else if (expect && !ex) {
// push word to expect to end this block, and expect a command first
if (add) {
dlist_add(&expect, add);
dlist_add(&expect, add = 0);
}
// end of statement?
if (i == arg->c) break;
// When waiting for { it must be next symbol, but can be on a new line.
if (ex && !strcmp(ex, "{") && (strcmp(s, "{") || (!i && end))) {
syntax_err("need {");
goto flush;
}
if (!strcmp(s, "if")) add = "then";
else if (!strcmp(s, "for") || !strcmp(s, "select")
|| !strcmp(s, "while") || !strcmp(s, "until")) add = "do";
else if (!strcmp(s, "case")) add = "esac";
else if (!strcmp(s, "{")) add = "}";
else if (!strcmp(s, "[[")) add = "]]";
// function NAME () [nl] { [nl] body ; }
// Why can you to declare functions inside other functions?
else if (arg->c>i+1 && !strcmp(arg->v[i+1], "(")) goto funky;
else if (!strcmp(s, "function")) {
i++;
funky:
// At this point we can only have a function: barf if it's invalid
if (arg->c<i+3 || !strcmp(arg->v[i+1], "(")
|| !strcmp(arg->v[i+2], ")"))
{
syntax_err("bad function ()");
goto flush;
}
dlist_add(&expect, "}");
dlist_add(&expect, 0);
dlist_add(&expect, "{");
// Expecting NULL will take any otherwise unrecognized word
} else if (expect && !ex) {
free(dlist_pop(&expect));
continue;
// Did we find a specific word we were waiting for?
} else if (ex && !strcmp(arg->v[i], ex)) {
// If we expect nothing and didn't just start a new flow control block,
// rest of statement is a command and arguments, so stop now
} else if (!ex) break;
if (add) continue;
// If we got here we expect a word to end this block: is this it?
if (!strcmp(arg->v[i], ex)) {
free(dlist_pop(&expect));
// can't "if | then" or "while && do", only ; or newline works
if (end && !strcmp(end, ";")) {
// can't if | then or while && do, only ; or newline counts
syntax_err("bad %s", end);
goto flush;
}
if (!strcmp(s, "do")) dlist_add(&expect, "done");
else if (!strcmp(s, "then")) dlist_add(&expect, "fi\0A");
break;
// if it's a multipart block, what comes next?
if (!strcmp(s, "do")) ex = "done";
else if (!strcmp(s, "then")) add = "fi\0A";
// fi could have elif, which queues a then.
} else if (ex && !strcmp(ex, "fi")) {
} else if (!strcmp(ex, "fi")) {
if (!strcmp(s, "elif")) {
free(dlist_pop(&expect));
dlist_add(&expect, "then");
add = "then";
// catch duplicate else while we're here
} else if (!strcmp(s, "else")) {
if (ex[3] != 'A') {
@ -391,23 +432,21 @@ static char *parse_line(char *line, struct double_list **pipeline)
goto flush;
}
free(dlist_pop(&expect));
dlist_add(&expect, "fi\0B");
add = "fi\0B";
}
} else break;
dlist_add(&expect, ex);
}
}
// Record how the previous stanza ended
// Record how the previous stanza ended: ; | & ;; || && ;& ;;& |& NULL
end = arg->v[arg->c];
}
// If we need more lines to finish flow control...
// TODO: functions
if (expect) {
// Do we need more lines to finish a flow control statement?
if (expect || paren) {
llist_traverse(expect, free);
return start;
}
// iterate through the commands running each one
for (pl = *pipeline; pl ; pl = (pl->next == *pipeline) ? 0 : pl->next) {
struct sh_process *pp = xzalloc(sizeof(struct sh_process));
@ -445,7 +484,12 @@ void sh_main(void)
size_t linelen = 0;
// Prompt and read line
if (!f) do_prompt(getenv(command ? "PS2" : "PS1"));
if (!f) {
char *s = getenv(command ? "PS2" : "PS1");
if (!s) s = command ? "> " : (getpid() ? "\\$ " : "# ");
do_prompt(s);
}
if (1 > getline(&new, &linelen, f ? f : stdin)) break;
if (f) TT.lineno++;