1 /*
2 
3     $Id: common.c,v 1.20 1998/08/13 14:53:30 thoth Exp $, part of
4     faucet and hose: network pipe utilities
5     Copyright (C) 1992-98 Robert Forsman
6 
7     This program 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 2 of the License, or
10     (at your option) any later version.
11 
12     This program 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 this program; if not, write to the Free Software
19     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21     */
22 
23 #include	<stdio.h>
24 #include	<stdlib.h>
25 #include	<string.h>
26 #include	<fcntl.h>
27 #include	<errno.h>
28 #include	<unistd.h>
29 #include	<sys/socket.h>
30 #ifndef NOUNIXSOCKETS
31 #include	<sys/un.h>
32 #endif /*NOUNIXSOCKETS*/
33 #include	<sys/time.h>
34 #include	<netdb.h>
35 #include	<netinet/in.h>
36 #include	<arpa/inet.h>
37 #include "common.h"
38 
39 #define	EXITCODE_CONNECTION	127
40 #define	EXITCODE_ARGS	126
41 
42 /**********************************************************************/
43 
44 int	*fds=0;
45 int	nfds;
46 int	fdsize=0;
47 
48 int how_shutdown = -2;
49 
add_fd(fd)50 void add_fd(fd)
51     int	fd;
52 {
53     if (fds==0) {
54 	fds = (int*)malloc(sizeof(*fds)*(fdsize=4));
55     } else if (nfds >= fdsize) {
56 	fds = (int*)realloc(fds, sizeof(*fds)*(fdsize*=2));
57 	if (fds==0) {
58 	    fprintf(stderr, "%s: Out of memory\n", progname);
59 	    exit(1);
60 	}
61     }
62     fds[nfds++] = fd;
63     if (fd>2)
64 	/* We should reserve this spot in the file descriptor table.
65 	 If we don't it could get allocated by the socket(2) call and
66 	 we would have an awful mess on our hands. */
67 	dup2(0, fd);
68 }
69 
reserve_fds(placeholder)70 void reserve_fds(placeholder)
71      int	placeholder;	/* I usually pass in 0, assuming that
72 				   the process will have a stdin, even
73 				   if it's attached to /dev/null */
74 {
75     int	i;
76     for (i=0; i<nfds; i++) {
77 	if (!valid_descriptor(fds[i]))
78 	    dup2(0, fds[i]);
79     }
80 }
81 
dup_n(socket)82 void dup_n(socket)
83     int	socket;
84 {
85     int	i;
86 #if 0
87     printf("I will redirect fds");
88     for (i=0; i<nfds; i++) {
89 	printf(" %d", fds[i]);
90     }
91     printf(" and shutdown with %d\n", how_shutdown);
92 #endif
93     if (how_shutdown>=0)
94 	shutdown(socket, how_shutdown!=0);
95     for (i=0; i<nfds; i++) {
96 	dup2(socket, fds[i]);
97     }
98 }
99 
100 /**********************************************************************/
101 
name_to_inet_port(portname)102 int name_to_inet_port(portname)
103 char *portname;
104 /* This procedure converts a character string to a port number.  It looks
105    up the service by name and if there is none, then it converts the string
106    to a number with sscanf */
107 {
108   struct servent	*p;
109 
110   if (portname==NULL)
111     return 0;
112 
113   p = getservbyname(portname,"tcp");
114   if (p!=NULL)
115     {
116       return p->s_port;
117     }
118   else
119     {
120       int	port;
121       if (sscanf(portname,"%i",&port)!=1)
122 	{
123 	  return 0;
124 	}
125       else
126 	return htons(port);
127     }
128 }
129 
130 struct in_addr ** /* addr_array */
convert_hostname(name,count_ret)131 convert_hostname(name, count_ret)
132     char	*name;
133     int		*count_ret;
134 {
135   struct hostent	*hp;
136   struct in_addr	**rval;
137 
138   hp = gethostbyname(name);
139   if (hp != NULL) {
140     int	i;
141     if (hp->h_length != sizeof(struct in_addr)) {
142 	fprintf(stderr, "%s: Funky: (hp->h_length = %d) != (sizeof(struct in_addr) = %ld)\n", progname, hp->h_length, (long) sizeof(struct in_addr));
143     }
144     for (i = 0; hp->h_addr_list[i]; i++)
145 	{ }
146     *count_ret = i;
147     rval = (struct in_addr **)malloc(sizeof(*rval) * (i+1));
148     for (i=0; i<*count_ret; i++) {
149 	rval[i] = (struct in_addr*)malloc(hp->h_length);
150 	memcpy((char*)rval[i], hp->h_addr_list[i], hp->h_length);
151     }
152     rval[*count_ret] = 0;
153     return rval;
154   } else {
155 #ifndef HAVE_INET_ATON
156       int	count, len;
157       unsigned int	a1,a2,a3,a4;
158 #endif
159       rval = (struct in_addr**)malloc(2*sizeof(*rval));
160       rval[0] = (struct in_addr*)malloc(sizeof(struct in_addr));
161 #ifdef HAVE_INET_ATON
162       if (0==inet_aton(name, rval[0])) {
163 	  *count_ret = 0;
164 	  free(rval[0]);
165 	  free(rval);
166 	  return 0;
167       }
168 #else
169       count = sscanf(name,"%i.%i.%i.%i%n", &a1, &a2, &a3, &a4, &len);
170       if (4!=count || 0!=name[len] )
171 	  return 0;
172       rval[0]->s_addr = (((((a1 << 8) | a2) << 8) | a3) << 8) | a4;
173 #endif
174       *count_ret = 1;
175       rval[1] = 0;
176 
177       return rval;
178   }
179 }
180 
181 
182 /* print an internet host address prettily */
printhost(fp,addr)183 void printhost(fp, addr)
184      FILE	*fp;
185      struct in_addr	*addr;
186 {
187   struct hostent	*h;
188   char	*s,**p;
189 
190   h = gethostbyaddr((char*)addr, sizeof(*addr),AF_INET);
191   s = (h==NULL) ? NULL : (char*)/*gratuitous cast away const*/h->h_name;
192 
193   fputs(inet_ntoa(*addr), fp);
194 
195   fprintf(fp, "(%s",s?s:"name unknown");
196   if (s)
197     for (p=h->h_aliases; *p; p++)
198       fprintf(fp, ",%s",*p);
199   fprintf(fp, ")");
200 }
201 
202 #ifdef NO_STRERROR
203 /* Added for those systems without */
204 extern char *sys_errlist[];
205 extern int sys_nerr;
206 
207 char *
strerror(num)208 strerror(num)
209      int num;
210 {
211   static char ebuf[40];		/* overflow this, baby */
212 
213   if (num < sys_nerr)
214     return sys_errlist[num];
215   else
216     sprintf(ebuf, "Unknown error: %i\n", num);
217   return ebuf;
218 }
219 #endif
220 
221 /* bind to a port on the local machine. */
222 int
bindlocal(fd,name,addrname,domain,reuseaddr)223 bindlocal(fd, name, addrname, domain, reuseaddr)
224      int	fd, domain;
225      char	*name, *addrname;
226      int	reuseaddr;
227 {
228   struct sockaddr	*laddr;
229   int	addrlen;
230   int	countdown;
231   int	rval;
232 
233   if (reuseaddr && domain == AF_INET) {
234 #ifdef SO_REUSEADDR
235       /* The below fix is based on articles that came from comp.sys.hp.hpux
236 	 with the problem of having FIN_WAIT_2 statuses on sockets.  But even
237 	 on Solaris the sockets with TIME_WAIT block the bind() call, so I
238 	 thought it would be a good idea to try the setsockopt() call.
239 	 1998/01/18 Thomas Endo <tendo@netcom.com> */
240       int	enable = 1;
241       if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&enable,
242 		     sizeof(enable)) < 0)
243 	  {
244 	      fprintf(stderr,"%s: error in setsockopt (%s)\n",progname,
245 		      strerror(errno));
246 	      exit(EXITCODE_CONNECTION);
247 	  }
248 #else
249       fprintf(stderr, "%s: Warning. SO_REUSEADDR is not available\n",
250 	      progname);
251 #endif
252   }
253 
254   if (domain==AF_INET)
255     {
256       static struct sockaddr_in	srv;
257       static int	initted=0;
258 
259       laddr = (struct sockaddr*)&srv;
260       addrlen = sizeof(srv);
261 
262       if (!initted) {
263 	srv.sin_family = AF_INET;
264 
265 	if (addrname) {
266 	    int	count;
267 	    struct in_addr **addresses;
268 	    addresses = convert_hostname(addrname, &count);
269 	    if (addresses == 0) {
270 		fprintf(stderr, "%s: Unable to convert %s to an internet address\n", progname, addrname);
271 		errno=0;
272 		return 0;
273 	    }
274 	    srv.sin_addr = *(addresses[0]);
275 	} else {
276 	    srv.sin_addr.s_addr = INADDR_ANY;
277 	}
278 
279 	srv.sin_port = name_to_inet_port(name);
280 
281 	if (srv.sin_port==0)
282 	  {
283 	    fprintf(stderr, "%s: port %s unknown\n", progname, name);
284 	    errno = 0;
285 	    return 0;
286 	  }
287       }
288       initted = 1;		/* bindlocal is only called once in
289 				   each netpipes program */
290     }
291 #ifndef NOUNIXSOCKETS
292   else if (domain == AF_UNIX)
293     {
294       static struct sockaddr_un	srv;
295       laddr = (struct sockaddr*)&srv;
296       addrlen = sizeof(srv);
297 
298       srv.sun_family = AF_UNIX;
299       strncpy(srv.sun_path, name, sizeof(srv.sun_path));
300       srv.sun_path[sizeof(srv.sun_path) -1] = 0; /* NUL terminate that string*/
301     }
302 #endif
303   else
304     {
305       fprintf(stderr, "%s: unknown address family %d in bindlocal()\n",
306 	      progname, domain);
307       exit(EXITCODE_ARGS);
308     }
309 
310   countdown= (domain!=AF_INET || reuseaddr)?1:10;
311   do {
312     rval = bind(fd, laddr, addrlen);
313     if (rval != 0)
314       {
315 	if (errno==EADDRINUSE && --countdown>0)
316 	  {
317 	    fprintf(stderr,"%s: Address %s in use, sleeping 10.\n",
318 		    progname, name);
319 	    sleep (10);
320 	    fprintf(stderr,"%s: Trying again . . .\n", progname);
321 	  }
322 	else
323 	  return 0;
324       }
325   } while (rval);
326 
327   return 1;
328 }
329 
330 
331 /* check to see if the descriptor is assigned to a pipe/file/socket (valid)
332    or is unused (INvalid) */
valid_descriptor(int fd)333 int valid_descriptor(int fd)
334 {
335 	int	rval;
336 	fd_set	fds;
337 	struct timeval	tv;
338 
339 	tv.tv_sec = 0; tv.tv_usec = 0; /* POLL */
340 
341 	FD_ZERO(&fds);
342 	FD_SET(fd, &fds);
343 
344 	rval = select(fd+1, &fds, (fd_set *)0, (fd_set *)0, &tv);
345 
346 	if (rval<0 && errno == EBADF) {
347 #ifdef DEBUG
348 	    fprintf(stderr, "%s: descriptor %d not in use\n",
349 		    progname, fd);
350 #endif
351 	    return 0;
352 	} else {
353 #ifdef DEBUG
354 	    fprintf(stderr, "%s: descriptor %d already in use\n",
355 		    progname, fd);
356 #endif
357 	    return 1;
358 	}
359 }
360