]>
Commit | Line | Data |
---|---|---|
bb70624e JA |
1 | /* |
2 | * netopen.c -- functions to make tcp/udp connections | |
3 | * | |
4 | * Chet Ramey | |
5 | * chet@ins.CWRU.Edu | |
6 | */ | |
7 | ||
a0c0a00f | 8 | /* Copyright (C) 1987-2016 Free Software Foundation, Inc. |
bb70624e JA |
9 | |
10 | This file is part of GNU Bash, the Bourne Again SHell. | |
11 | ||
3185942a JA |
12 | Bash is free software: you can redistribute it and/or modify |
13 | it under the terms of the GNU General Public License as published by | |
14 | the Free Software Foundation, either version 3 of the License, or | |
15 | (at your option) any later version. | |
bb70624e | 16 | |
3185942a JA |
17 | Bash is distributed in the hope that it will be useful, |
18 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | GNU General Public License for more details. | |
bb70624e JA |
21 | |
22 | You should have received a copy of the GNU General Public License | |
3185942a JA |
23 | along with Bash. If not, see <http://www.gnu.org/licenses/>. |
24 | */ | |
bb70624e JA |
25 | |
26 | #include <config.h> | |
27 | ||
28 | #if defined (HAVE_NETWORK) | |
29 | ||
f73dda09 JA |
30 | #if defined (HAVE_UNISTD_H) |
31 | # include <unistd.h> | |
32 | #endif | |
33 | ||
bb70624e JA |
34 | #include <stdio.h> |
35 | #include <sys/types.h> | |
36 | ||
37 | #if defined (HAVE_SYS_SOCKET_H) | |
38 | # include <sys/socket.h> | |
39 | #endif | |
40 | ||
41 | #if defined (HAVE_NETINET_IN_H) | |
42 | # include <netinet/in.h> | |
43 | #endif | |
44 | ||
45 | #if defined (HAVE_NETDB_H) | |
46 | # include <netdb.h> | |
47 | #endif | |
48 | ||
49 | #if defined (HAVE_ARPA_INET_H) | |
50 | # include <arpa/inet.h> | |
51 | #endif | |
52 | ||
53 | #include <bashansi.h> | |
b80f6443 JA |
54 | #include <bashintl.h> |
55 | ||
bb70624e JA |
56 | #include <errno.h> |
57 | ||
f73dda09 JA |
58 | #include <shell.h> |
59 | #include <xmalloc.h> | |
60 | ||
bb70624e JA |
61 | #ifndef errno |
62 | extern int errno; | |
63 | #endif | |
64 | ||
65 | #if !defined (HAVE_INET_ATON) | |
f73dda09 | 66 | extern int inet_aton __P((const char *, struct in_addr *)); |
bb70624e JA |
67 | #endif |
68 | ||
0628567a JA |
69 | #ifndef HAVE_GETADDRINFO |
70 | static int _getaddr __P((char *, struct in_addr *)); | |
71 | static int _getserv __P((char *, int, unsigned short *)); | |
72 | static int _netopen4 __P((char *, char *, int)); | |
73 | #else /* HAVE_GETADDRINFO */ | |
74 | static int _netopen6 __P((char *, char *, int)); | |
75 | #endif | |
76 | ||
77 | static int _netopen __P((char *, char *, int)); | |
78 | ||
f73dda09 | 79 | #ifndef HAVE_GETADDRINFO |
bb70624e JA |
80 | /* Stuff the internet address corresponding to HOST into AP, in network |
81 | byte order. Return 1 on success, 0 on failure. */ | |
82 | ||
83 | static int | |
84 | _getaddr (host, ap) | |
85 | char *host; | |
86 | struct in_addr *ap; | |
87 | { | |
88 | struct hostent *h; | |
89 | int r; | |
90 | ||
91 | r = 0; | |
f73dda09 | 92 | if (host[0] >= '0' && host[0] <= '9') |
bb70624e JA |
93 | { |
94 | /* If the first character is a digit, guess that it's an | |
95 | Internet address and return immediately if inet_aton succeeds. */ | |
96 | r = inet_aton (host, ap); | |
97 | if (r) | |
98 | return r; | |
99 | } | |
100 | #if !defined (HAVE_GETHOSTBYNAME) | |
101 | return 0; | |
102 | #else | |
103 | h = gethostbyname (host); | |
104 | if (h && h->h_addr) | |
105 | { | |
106 | bcopy(h->h_addr, (char *)ap, h->h_length); | |
107 | return 1; | |
108 | } | |
109 | #endif | |
110 | return 0; | |
111 | ||
112 | } | |
113 | ||
114 | /* Return 1 if SERV is a valid port number and stuff the converted value into | |
115 | PP in network byte order. */ | |
116 | static int | |
28ef6c31 | 117 | _getserv (serv, proto, pp) |
bb70624e | 118 | char *serv; |
28ef6c31 | 119 | int proto; |
bb70624e JA |
120 | unsigned short *pp; |
121 | { | |
7117c2d2 | 122 | intmax_t l; |
bb70624e JA |
123 | unsigned short s; |
124 | ||
125 | if (legal_number (serv, &l)) | |
126 | { | |
bb70624e | 127 | s = (unsigned short)(l & 0xFFFF); |
f73dda09 JA |
128 | if (s != l) |
129 | return (0); | |
bb70624e JA |
130 | s = htons (s); |
131 | if (pp) | |
132 | *pp = s; | |
133 | return 1; | |
134 | } | |
135 | else | |
28ef6c31 JA |
136 | #if defined (HAVE_GETSERVBYNAME) |
137 | { | |
138 | struct servent *se; | |
139 | ||
140 | se = getservbyname (serv, (proto == 't') ? "tcp" : "udp"); | |
141 | if (se == 0) | |
142 | return 0; | |
143 | if (pp) | |
144 | *pp = se->s_port; /* ports returned in network byte order */ | |
145 | return 1; | |
146 | } | |
147 | #else /* !HAVE_GETSERVBYNAME */ | |
bb70624e | 148 | return 0; |
28ef6c31 | 149 | #endif /* !HAVE_GETSERVBYNAME */ |
bb70624e JA |
150 | } |
151 | ||
f73dda09 JA |
152 | /* |
153 | * Open a TCP or UDP connection to HOST on port SERV. Uses the | |
154 | * traditional BSD mechanisms. Returns the connected socket or -1 on error. | |
155 | */ | |
bb70624e | 156 | static int |
f73dda09 | 157 | _netopen4(host, serv, typ) |
bb70624e JA |
158 | char *host, *serv; |
159 | int typ; | |
160 | { | |
161 | struct in_addr ina; | |
162 | struct sockaddr_in sin; | |
163 | unsigned short p; | |
28ef6c31 | 164 | int s, e; |
bb70624e JA |
165 | |
166 | if (_getaddr(host, &ina) == 0) | |
167 | { | |
b80f6443 | 168 | internal_error (_("%s: host unknown"), host); |
28ef6c31 | 169 | errno = EINVAL; |
bb70624e JA |
170 | return -1; |
171 | } | |
172 | ||
28ef6c31 | 173 | if (_getserv(serv, typ, &p) == 0) |
bb70624e | 174 | { |
b80f6443 | 175 | internal_error(_("%s: invalid service"), serv); |
28ef6c31 | 176 | errno = EINVAL; |
bb70624e JA |
177 | return -1; |
178 | } | |
179 | ||
7117c2d2 | 180 | memset ((char *)&sin, 0, sizeof(sin)); |
bb70624e JA |
181 | sin.sin_family = AF_INET; |
182 | sin.sin_port = p; | |
183 | sin.sin_addr = ina; | |
184 | ||
185 | s = socket(AF_INET, (typ == 't') ? SOCK_STREAM : SOCK_DGRAM, 0); | |
186 | if (s < 0) | |
187 | { | |
188 | sys_error ("socket"); | |
189 | return (-1); | |
190 | } | |
191 | ||
192 | if (connect (s, (struct sockaddr *)&sin, sizeof (sin)) < 0) | |
193 | { | |
28ef6c31 | 194 | e = errno; |
bb70624e JA |
195 | sys_error("connect"); |
196 | close(s); | |
28ef6c31 | 197 | errno = e; |
bb70624e JA |
198 | return (-1); |
199 | } | |
200 | ||
201 | return(s); | |
202 | } | |
f73dda09 JA |
203 | #endif /* ! HAVE_GETADDRINFO */ |
204 | ||
205 | #ifdef HAVE_GETADDRINFO | |
206 | /* | |
207 | * Open a TCP or UDP connection to HOST on port SERV. Uses getaddrinfo(3) | |
208 | * which provides support for IPv6. Returns the connected socket or -1 | |
209 | * on error. | |
210 | */ | |
211 | static int | |
212 | _netopen6 (host, serv, typ) | |
213 | char *host, *serv; | |
214 | int typ; | |
215 | { | |
216 | int s, e; | |
217 | struct addrinfo hints, *res, *res0; | |
218 | int gerr; | |
219 | ||
7117c2d2 | 220 | memset ((char *)&hints, 0, sizeof (hints)); |
f73dda09 JA |
221 | /* XXX -- if problems with IPv6, set to PF_INET for IPv4 only */ |
222 | #ifdef DEBUG /* PF_INET is the one that works for me */ | |
223 | hints.ai_family = PF_INET; | |
224 | #else | |
225 | hints.ai_family = PF_UNSPEC; | |
226 | #endif | |
227 | hints.ai_socktype = (typ == 't') ? SOCK_STREAM : SOCK_DGRAM; | |
228 | ||
229 | gerr = getaddrinfo (host, serv, &hints, &res0); | |
230 | if (gerr) | |
231 | { | |
232 | if (gerr == EAI_SERVICE) | |
233 | internal_error ("%s: %s", serv, gai_strerror (gerr)); | |
234 | else | |
235 | internal_error ("%s: %s", host, gai_strerror (gerr)); | |
236 | errno = EINVAL; | |
237 | return -1; | |
238 | } | |
239 | ||
240 | for (res = res0; res; res = res->ai_next) | |
241 | { | |
242 | if ((s = socket (res->ai_family, res->ai_socktype, res->ai_protocol)) < 0) | |
243 | { | |
244 | if (res->ai_next) | |
245 | continue; | |
246 | sys_error ("socket"); | |
247 | freeaddrinfo (res0); | |
248 | return -1; | |
249 | } | |
250 | if (connect (s, res->ai_addr, res->ai_addrlen) < 0) | |
251 | { | |
252 | if (res->ai_next) | |
253 | { | |
254 | close (s); | |
255 | continue; | |
256 | } | |
257 | e = errno; | |
258 | sys_error ("connect"); | |
259 | close (s); | |
260 | freeaddrinfo (res0); | |
261 | errno = e; | |
262 | return -1; | |
263 | } | |
264 | freeaddrinfo (res0); | |
265 | break; | |
266 | } | |
267 | return s; | |
268 | } | |
269 | #endif /* HAVE_GETADDRINFO */ | |
270 | ||
271 | /* | |
272 | * Open a TCP or UDP connection to HOST on port SERV. Uses getaddrinfo(3) | |
273 | * if available, falling back to the traditional BSD mechanisms otherwise. | |
274 | * Returns the connected socket or -1 on error. | |
275 | */ | |
276 | static int | |
277 | _netopen(host, serv, typ) | |
278 | char *host, *serv; | |
279 | int typ; | |
280 | { | |
281 | #ifdef HAVE_GETADDRINFO | |
282 | return (_netopen6 (host, serv, typ)); | |
283 | #else | |
284 | return (_netopen4 (host, serv, typ)); | |
285 | #endif | |
286 | } | |
bb70624e JA |
287 | |
288 | /* | |
289 | * Open a TCP or UDP connection given a path like `/dev/tcp/host/port' to | |
290 | * host `host' on port `port' and return the connected socket. | |
291 | */ | |
292 | int | |
293 | netopen (path) | |
294 | char *path; | |
295 | { | |
296 | char *np, *s, *t; | |
297 | int fd; | |
298 | ||
f73dda09 | 299 | np = (char *)xmalloc (strlen (path) + 1); |
bb70624e JA |
300 | strcpy (np, path); |
301 | ||
302 | s = np + 9; | |
303 | t = strchr (s, '/'); | |
304 | if (t == 0) | |
305 | { | |
b80f6443 | 306 | internal_error (_("%s: bad network path specification"), path); |
a0c0a00f | 307 | free (np); |
bb70624e JA |
308 | return -1; |
309 | } | |
310 | *t++ = '\0'; | |
311 | fd = _netopen (s, t, path[5]); | |
312 | free (np); | |
313 | ||
314 | return fd; | |
315 | } | |
316 | ||
317 | #if 0 | |
318 | /* | |
319 | * Open a TCP connection to host `host' on the port defined for service | |
320 | * `serv' and return the connected socket. | |
321 | */ | |
322 | int | |
323 | tcpopen (host, serv) | |
324 | char *host, *serv; | |
325 | { | |
326 | return (_netopen (host, serv, 't')); | |
327 | } | |
328 | ||
329 | /* | |
330 | * Open a UDP connection to host `host' on the port defined for service | |
331 | * `serv' and return the connected socket. | |
332 | */ | |
333 | int | |
334 | udpopen (host, serv) | |
335 | char *host, *serv; | |
336 | { | |
337 | return _netopen (host, serv, 'u'); | |
338 | } | |
339 | #endif | |
340 | ||
341 | #else /* !HAVE_NETWORK */ | |
342 | ||
343 | int | |
344 | netopen (path) | |
345 | char *path; | |
346 | { | |
b80f6443 | 347 | internal_error (_("network operations not supported")); |
bb70624e JA |
348 | return -1; |
349 | } | |
350 | ||
351 | #endif /* !HAVE_NETWORK */ |