1/*
2 * netopen.c -- functions to make tcp/udp connections
3 *
4 * Chet Ramey
5 * chet@ins.CWRU.Edu
6 */
7
8/* Copyright (C) 1987-2002 Free Software Foundation, Inc.
9
10   This file is part of GNU Bash, the Bourne Again SHell.
11
12   Bash is free software; you can redistribute it and/or modify it
13   under the terms of the GNU General Public License as published by
14   the Free Software Foundation; either version 2, or (at your option)
15   any later version.
16
17   Bash is distributed in the hope that it will be useful, but WITHOUT
18   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
20   License for more details.
21
22   You should have received a copy of the GNU General Public License
23   along with Bash; see the file COPYING.  If not, write to the Free
24   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
25
26#include <config.h>
27
28#if defined (HAVE_NETWORK)
29
30#if defined (HAVE_UNISTD_H)
31#  include <unistd.h>
32#endif
33
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>
54#include <bashintl.h>
55
56#include <errno.h>
57
58#include <shell.h>
59#include <xmalloc.h>
60
61#ifndef errno
62extern int errno;
63#endif
64
65#if !defined (HAVE_INET_ATON)
66extern int inet_aton __P((const char *, struct in_addr *));
67#endif
68
69#ifndef HAVE_GETADDRINFO
70static int _getaddr __P((char *, struct in_addr *));
71static int _getserv __P((char *, int, unsigned short *));
72static int _netopen4 __P((char *, char *, int));
73#else /* HAVE_GETADDRINFO */
74static int _netopen6 __P((char *, char *, int));
75#endif
76
77static int _netopen __P((char *, char *, int));
78
79#ifndef HAVE_GETADDRINFO
80/* Stuff the internet address corresponding to HOST into AP, in network
81   byte order.  Return 1 on success, 0 on failure. */
82
83static 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;
92  if (host[0] >= '0' && host[0] <= '9')
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. */
116static int
117_getserv (serv, proto, pp)
118     char *serv;
119     int proto;
120     unsigned short *pp;
121{
122  intmax_t l;
123  unsigned short s;
124
125  if (legal_number (serv, &l))
126    {
127      s = (unsigned short)(l & 0xFFFF);
128      if (s != l)
129	return (0);
130      s = htons (s);
131      if (pp)
132	*pp = s;
133      return 1;
134    }
135  else
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 */
148    return 0;
149#endif /* !HAVE_GETSERVBYNAME */
150}
151
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 */
156static int
157_netopen4(host, serv, typ)
158     char *host, *serv;
159     int typ;
160{
161  struct in_addr ina;
162  struct sockaddr_in sin;
163  unsigned short p;
164  int s, e;
165
166  if (_getaddr(host, &ina) == 0)
167    {
168      internal_error (_("%s: host unknown"), host);
169      errno = EINVAL;
170      return -1;
171    }
172
173  if (_getserv(serv, typ, &p) == 0)
174    {
175      internal_error(_("%s: invalid service"), serv);
176      errno = EINVAL;
177      return -1;
178    }
179
180  memset ((char *)&sin, 0, sizeof(sin));
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    {
194      e = errno;
195      sys_error("connect");
196      close(s);
197      errno = e;
198      return (-1);
199    }
200
201  return(s);
202}
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 */
211static 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
220  memset ((char *)&hints, 0, sizeof (hints));
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 */
276static 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}
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 */
292int
293netopen (path)
294     char *path;
295{
296  char *np, *s, *t;
297  int fd;
298
299  np = (char *)xmalloc (strlen (path) + 1);
300  strcpy (np, path);
301
302  s = np + 9;
303  t = strchr (s, '/');
304  if (t == 0)
305    {
306      internal_error (_("%s: bad network path specification"), path);
307      return -1;
308    }
309  *t++ = '\0';
310  fd = _netopen (s, t, path[5]);
311  free (np);
312
313  return fd;
314}
315
316#if 0
317/*
318 * Open a TCP connection to host `host' on the port defined for service
319 * `serv' and return the connected socket.
320 */
321int
322tcpopen (host, serv)
323     char *host, *serv;
324{
325  return (_netopen (host, serv, 't'));
326}
327
328/*
329 * Open a UDP connection to host `host' on the port defined for service
330 * `serv' and return the connected socket.
331 */
332int
333udpopen (host, serv)
334     char *host, *serv;
335{
336  return _netopen (host, serv, 'u');
337}
338#endif
339
340#else /* !HAVE_NETWORK */
341
342int
343netopen (path)
344     char *path;
345{
346  internal_error (_("network operations not supported"));
347  return -1;
348}
349
350#endif /* !HAVE_NETWORK */
351