]> git.ipfire.org Git - thirdparty/bash.git/blob - examples/loadables/accept.c
bash-5.2 distribution sources and documentation
[thirdparty/bash.git] / examples / loadables / accept.c
1 /* accept - listen for and accept a remote network connection on a given port */
2
3 /*
4 Copyright (C) 2020 Free Software Foundation, Inc.
5
6 This file is part of GNU Bash.
7 Bash is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 Bash is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Bash. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <config.h>
22
23 #if defined (HAVE_UNISTD_H)
24 # include <unistd.h>
25 #endif
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include "bashtypes.h"
31 #include <errno.h>
32 #include <time.h>
33 #include <limits.h>
34 #include "typemax.h"
35
36 #include <sys/socket.h>
37 #include <arpa/inet.h>
38 #include <netinet/in.h>
39
40 #include "loadables.h"
41
42 static int accept_bind_variable (char *, int);
43
44 int
45 accept_builtin (list)
46 WORD_LIST *list;
47 {
48 SHELL_VAR *v;
49 intmax_t iport;
50 int opt;
51 char *tmoutarg, *fdvar, *rhostvar, *rhost, *bindaddr;
52 unsigned short uport;
53 int servsock, clisock;
54 struct sockaddr_in server, client;
55 socklen_t clientlen;
56 struct timeval timeval;
57 struct linger linger = { 0, 0 };
58
59 rhostvar = tmoutarg = fdvar = rhost = bindaddr = (char *)NULL;
60
61 reset_internal_getopt ();
62 while ((opt = internal_getopt (list, "b:r:t:v:")) != -1)
63 {
64 switch (opt)
65 {
66 case 'b':
67 bindaddr = list_optarg;
68 break;
69 case 'r':
70 rhostvar = list_optarg;
71 break;
72 case 't':
73 tmoutarg = list_optarg;
74 break;
75 case 'v':
76 fdvar = list_optarg;
77 break;
78 CASE_HELPOPT;
79 default:
80 builtin_usage ();
81 return (EX_USAGE);
82 }
83 }
84
85 list = loptend;
86
87 /* Validate input and variables */
88 if (tmoutarg)
89 {
90 long ival, uval;
91 opt = uconvert (tmoutarg, &ival, &uval, (char **)0);
92 if (opt == 0 || ival < 0 || uval < 0)
93 {
94 builtin_error ("%s: invalid timeout specification", tmoutarg);
95 return (EXECUTION_FAILURE);
96 }
97 timeval.tv_sec = ival;
98 timeval.tv_usec = uval;
99 /* XXX - should we warn if ival == uval == 0 ? */
100 }
101
102 if (list == 0)
103 {
104 builtin_usage ();
105 return (EX_USAGE);
106 }
107
108 if (legal_number (list->word->word, &iport) == 0 || iport < 0 || iport > TYPE_MAXIMUM (unsigned short))
109 {
110 builtin_error ("%s: invalid port number", list->word->word);
111 return (EXECUTION_FAILURE);
112 }
113 uport = (unsigned short)iport;
114
115 if (fdvar == 0)
116 fdvar = "ACCEPT_FD";
117
118 unbind_variable (fdvar);
119 if (rhostvar)
120 unbind_variable (rhostvar);
121
122 if ((servsock = socket (AF_INET, SOCK_STREAM, IPPROTO_IP)) < 0)
123 {
124 builtin_error ("cannot create socket: %s", strerror (errno));
125 return (EXECUTION_FAILURE);
126 }
127
128 memset ((char *)&server, 0, sizeof (server));
129 server.sin_family = AF_INET;
130 server.sin_port = htons(uport);
131 server.sin_addr.s_addr = bindaddr ? inet_addr (bindaddr) : htonl(INADDR_ANY);
132
133 if (server.sin_addr.s_addr == INADDR_NONE)
134 {
135 builtin_error ("invalid address: %s", strerror (errno));
136 return (EXECUTION_FAILURE);
137 }
138
139 opt = 1;
140 setsockopt (servsock, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof (opt));
141 setsockopt (servsock, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof (linger));
142
143 if (bind (servsock, (struct sockaddr *)&server, sizeof (server)) < 0)
144 {
145 builtin_error ("socket bind failure: %s", strerror (errno));
146 close (servsock);
147 return (EXECUTION_FAILURE);
148 }
149
150 if (listen (servsock, 1) < 0)
151 {
152 builtin_error ("listen failure: %s", strerror (errno));
153 close (servsock);
154 return (EXECUTION_FAILURE);
155 }
156
157 if (tmoutarg)
158 {
159 fd_set iofds;
160
161 FD_ZERO(&iofds);
162 FD_SET(servsock, &iofds);
163
164 opt = select (servsock+1, &iofds, 0, 0, &timeval);
165 if (opt < 0)
166 builtin_error ("select failure: %s", strerror (errno));
167 if (opt <= 0)
168 {
169 close (servsock);
170 return (EXECUTION_FAILURE);
171 }
172 }
173
174 clientlen = sizeof (client);
175 if ((clisock = accept (servsock, (struct sockaddr *)&client, &clientlen)) < 0)
176 {
177 builtin_error ("client accept failure: %s", strerror (errno));
178 close (servsock);
179 return (EXECUTION_FAILURE);
180 }
181
182 close (servsock);
183
184 accept_bind_variable (fdvar, clisock);
185 if (rhostvar)
186 {
187 rhost = inet_ntoa (client.sin_addr);
188 v = builtin_bind_variable (rhostvar, rhost, 0);
189 if (v == 0 || readonly_p (v) || noassign_p (v))
190 builtin_error ("%s: cannot set variable", rhostvar);
191 }
192
193 return (EXECUTION_SUCCESS);
194 }
195
196 static int
197 accept_bind_variable (varname, intval)
198 char *varname;
199 int intval;
200 {
201 SHELL_VAR *v;
202 char ibuf[INT_STRLEN_BOUND (int) + 1], *p;
203
204 p = fmtulong (intval, 10, ibuf, sizeof (ibuf), 0);
205 v = builtin_bind_variable (varname, p, 0); /* XXX */
206 if (v == 0 || readonly_p (v) || noassign_p (v))
207 builtin_error ("%s: cannot set variable", varname);
208 return (v != 0);
209 }
210
211 char *accept_doc[] = {
212 "Accept a network connection on a specified port.",
213 ""
214 "This builtin allows a bash script to act as a TCP/IP server.",
215 "",
216 "Options, if supplied, have the following meanings:",
217 " -b address use ADDRESS as the IP address to listen on; the",
218 " default is INADDR_ANY",
219 " -t timeout wait TIMEOUT seconds for a connection. TIMEOUT may",
220 " be a decimal number including a fractional portion",
221 " -v varname store the numeric file descriptor of the connected",
222 " socket into VARNAME. The default VARNAME is ACCEPT_FD",
223 " -r rhost store the IP address of the remote host into the shell",
224 " variable RHOST, in dotted-decimal notation",
225 "",
226 "If successful, the shell variable ACCEPT_FD, or the variable named by the",
227 "-v option, will be set to the fd of the connected socket, suitable for",
228 "use as 'read -u$ACCEPT_FD'. RHOST, if supplied, will hold the IP address",
229 "of the remote client. The return status is 0.",
230 "",
231 "On failure, the return status is 1 and ACCEPT_FD (or VARNAME) and RHOST,",
232 "if supplied, will be unset.",
233 "",
234 "The server socket fd will be closed before accept returns.",
235 (char *) NULL
236 };
237
238 struct builtin accept_struct = {
239 "accept", /* builtin name */
240 accept_builtin, /* function implementing the builtin */
241 BUILTIN_ENABLED, /* initial flags for builtin */
242 accept_doc, /* array of long documentation strings. */
243 "accept [-b address] [-t timeout] [-v varname] [-r addrvar ] port", /* usage synopsis; becomes short_doc */
244 0 /* reserved for internal use */
245 };