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