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