diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 15:38:56 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 15:38:56 +0000 |
commit | 6c20c8ed2cb9ab69a1a57ccb2b9b79969a808321 (patch) | |
tree | f63ce19d57fad3ac4a15bc26dbfbfa2b834111b5 /examples/loadables/accept.c | |
parent | Initial commit. (diff) | |
download | bash-6c20c8ed2cb9ab69a1a57ccb2b9b79969a808321.tar.xz bash-6c20c8ed2cb9ab69a1a57ccb2b9b79969a808321.zip |
Adding upstream version 5.2.15.upstream/5.2.15upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'examples/loadables/accept.c')
-rw-r--r-- | examples/loadables/accept.c | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/examples/loadables/accept.c b/examples/loadables/accept.c new file mode 100644 index 0000000..ff98423 --- /dev/null +++ b/examples/loadables/accept.c @@ -0,0 +1,245 @@ +/* accept - listen for and accept a remote network connection on a given port */ + +/* + Copyright (C) 2020 Free Software Foundation, Inc. + + This file is part of GNU Bash. + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include "bashtypes.h" +#include <errno.h> +#include <time.h> +#include <limits.h> +#include "typemax.h" + +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netinet/in.h> + +#include "loadables.h" + +static int accept_bind_variable (char *, int); + +int +accept_builtin (list) + WORD_LIST *list; +{ + SHELL_VAR *v; + intmax_t iport; + int opt; + char *tmoutarg, *fdvar, *rhostvar, *rhost, *bindaddr; + unsigned short uport; + int servsock, clisock; + struct sockaddr_in server, client; + socklen_t clientlen; + struct timeval timeval; + struct linger linger = { 0, 0 }; + + rhostvar = tmoutarg = fdvar = rhost = bindaddr = (char *)NULL; + + reset_internal_getopt (); + while ((opt = internal_getopt (list, "b:r:t:v:")) != -1) + { + switch (opt) + { + case 'b': + bindaddr = list_optarg; + break; + case 'r': + rhostvar = list_optarg; + break; + case 't': + tmoutarg = list_optarg; + break; + case 'v': + fdvar = list_optarg; + break; + CASE_HELPOPT; + default: + builtin_usage (); + return (EX_USAGE); + } + } + + list = loptend; + + /* Validate input and variables */ + if (tmoutarg) + { + long ival, uval; + opt = uconvert (tmoutarg, &ival, &uval, (char **)0); + if (opt == 0 || ival < 0 || uval < 0) + { + builtin_error ("%s: invalid timeout specification", tmoutarg); + return (EXECUTION_FAILURE); + } + timeval.tv_sec = ival; + timeval.tv_usec = uval; + /* XXX - should we warn if ival == uval == 0 ? */ + } + + if (list == 0) + { + builtin_usage (); + return (EX_USAGE); + } + + if (legal_number (list->word->word, &iport) == 0 || iport < 0 || iport > TYPE_MAXIMUM (unsigned short)) + { + builtin_error ("%s: invalid port number", list->word->word); + return (EXECUTION_FAILURE); + } + uport = (unsigned short)iport; + + if (fdvar == 0) + fdvar = "ACCEPT_FD"; + + unbind_variable (fdvar); + if (rhostvar) + unbind_variable (rhostvar); + + if ((servsock = socket (AF_INET, SOCK_STREAM, IPPROTO_IP)) < 0) + { + builtin_error ("cannot create socket: %s", strerror (errno)); + return (EXECUTION_FAILURE); + } + + memset ((char *)&server, 0, sizeof (server)); + server.sin_family = AF_INET; + server.sin_port = htons(uport); + server.sin_addr.s_addr = bindaddr ? inet_addr (bindaddr) : htonl(INADDR_ANY); + + if (server.sin_addr.s_addr == INADDR_NONE) + { + builtin_error ("invalid address: %s", strerror (errno)); + return (EXECUTION_FAILURE); + } + + opt = 1; + setsockopt (servsock, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof (opt)); + setsockopt (servsock, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof (linger)); + + if (bind (servsock, (struct sockaddr *)&server, sizeof (server)) < 0) + { + builtin_error ("socket bind failure: %s", strerror (errno)); + close (servsock); + return (EXECUTION_FAILURE); + } + + if (listen (servsock, 1) < 0) + { + builtin_error ("listen failure: %s", strerror (errno)); + close (servsock); + return (EXECUTION_FAILURE); + } + + if (tmoutarg) + { + fd_set iofds; + + FD_ZERO(&iofds); + FD_SET(servsock, &iofds); + + opt = select (servsock+1, &iofds, 0, 0, &timeval); + if (opt < 0) + builtin_error ("select failure: %s", strerror (errno)); + if (opt <= 0) + { + close (servsock); + return (EXECUTION_FAILURE); + } + } + + clientlen = sizeof (client); + if ((clisock = accept (servsock, (struct sockaddr *)&client, &clientlen)) < 0) + { + builtin_error ("client accept failure: %s", strerror (errno)); + close (servsock); + return (EXECUTION_FAILURE); + } + + close (servsock); + + accept_bind_variable (fdvar, clisock); + if (rhostvar) + { + rhost = inet_ntoa (client.sin_addr); + v = builtin_bind_variable (rhostvar, rhost, 0); + if (v == 0 || readonly_p (v) || noassign_p (v)) + builtin_error ("%s: cannot set variable", rhostvar); + } + + return (EXECUTION_SUCCESS); +} + +static int +accept_bind_variable (varname, intval) + char *varname; + int intval; +{ + SHELL_VAR *v; + char ibuf[INT_STRLEN_BOUND (int) + 1], *p; + + p = fmtulong (intval, 10, ibuf, sizeof (ibuf), 0); + v = builtin_bind_variable (varname, p, 0); /* XXX */ + if (v == 0 || readonly_p (v) || noassign_p (v)) + builtin_error ("%s: cannot set variable", varname); + return (v != 0); +} + +char *accept_doc[] = { + "Accept a network connection on a specified port.", + "" + "This builtin allows a bash script to act as a TCP/IP server.", + "", + "Options, if supplied, have the following meanings:", + " -b address use ADDRESS as the IP address to listen on; the", + " default is INADDR_ANY", + " -t timeout wait TIMEOUT seconds for a connection. TIMEOUT may", + " be a decimal number including a fractional portion", + " -v varname store the numeric file descriptor of the connected", + " socket into VARNAME. The default VARNAME is ACCEPT_FD", + " -r rhost store the IP address of the remote host into the shell", + " variable RHOST, in dotted-decimal notation", + "", + "If successful, the shell variable ACCEPT_FD, or the variable named by the", + "-v option, will be set to the fd of the connected socket, suitable for", + "use as 'read -u$ACCEPT_FD'. RHOST, if supplied, will hold the IP address", + "of the remote client. The return status is 0.", + "", + "On failure, the return status is 1 and ACCEPT_FD (or VARNAME) and RHOST,", + "if supplied, will be unset.", + "", + "The server socket fd will be closed before accept returns.", + (char *) NULL +}; + +struct builtin accept_struct = { + "accept", /* builtin name */ + accept_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + accept_doc, /* array of long documentation strings. */ + "accept [-b address] [-t timeout] [-v varname] [-r addrvar ] port", /* usage synopsis; becomes short_doc */ + 0 /* reserved for internal use */ +}; |