1 /* $Id: socket.C,v 1.28 2009/05/11 22:09:50 dm Exp $ */
2 
3 /*
4  *
5  * Copyright (C) 1998 David Mazieres (dm@uun.org)
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2, or (at
10  * your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20  * USA
21  *
22  */
23 
24 #if __linux
25 /* Needed for struct ucred */
26 # define _GNU_SOURCE 1
27 #endif /* __linux */
28 
29 #include "amisc.h"
30 #include "init.h"
31 extern "C" {
32 #ifdef HAVE_BINDRESVPORT
33 # ifdef HAVE_RPC_RPC_H
34 #  include <rpc/rpc.h>
35 # endif /* HAVE_RPC_RPC_H */
36 # ifdef NEED_BINDRESVPORT_DECL
37 int bindresvport (int, struct sockaddr_in *);
38 # endif /* NEED_BINDRESVPORT_DECL */
39 #endif /* HAVE_BINDRESVPORT */
40 #include <netinet/in_systm.h>
41 #include <netinet/tcp.h>
42 #include <netinet/ip.h>
43 }
44 
45 #ifdef SFS_ALLOW_LARGE_BUFFER
46 enum { maxsobufsize = 0x11000 }; /* 64K + header */
47 #else /* !SFS_ALLOW_LARGE_BUFFER */
48 enum { maxsobufsize = 0x05000 }; /* 16K + header */
49 #endif /* !SFS_ALLOW_LARGE_BUFFER */
50 int sndbufsize = maxsobufsize;
51 int rcvbufsize = maxsobufsize;
52 in_addr inet_bindaddr;
53 INITFN(init_env);
54 static void
init_env()55 init_env ()
56 {
57   if (char *p = safegetenv ("SNDBUFSIZE"))
58     sndbufsize = atoi (p);
59   if (char *p = safegetenv ("RCVBUFSIZE"))
60     rcvbufsize = atoi (p);
61 
62   char *p = safegetenv ("BINDADDR");
63   if (!p || inet_aton (p, &inet_bindaddr) <= 0)
64     inet_bindaddr.s_addr = htonl (INADDR_ANY);
65 }
66 
67 
68 u_int16_t inetsocket_lastport;
69 
70 int
inetsocket_resvport(int type,u_int32_t addr)71 inetsocket_resvport (int type, u_int32_t addr)
72 {
73 #ifndef NORESVPORTS
74   int s;
75   struct sockaddr_in sin;
76 
77   bzero (&sin, sizeof (sin));
78   sin.sin_family = AF_INET;
79   sin.sin_port = htons (0);
80   if (addr == INADDR_ANY)
81     sin.sin_addr = inet_bindaddr;
82   else
83     sin.sin_addr.s_addr = htonl (addr);
84   if ((s = socket (AF_INET, type, 0)) < 0)
85     return (-1);
86 
87   /* Don't bother if we aren't root */
88   if (geteuid ()) {
89   again0:
90     if (bind (s, (struct sockaddr *) &sin, sizeof (sin)) >= 0)
91       return s;
92     if (errno == EADDRNOTAVAIL && sin.sin_addr.s_addr != htonl (addr)) {
93       sin.sin_addr.s_addr = htonl (addr);
94       goto again0;
95     }
96     close (s);
97     return -1;
98   }
99 #ifdef HAVE_BINDRESVPORT
100  again1:
101   if (bindresvport (s, &sin) >= 0) {
102     inetsocket_lastport = ntohs (sin.sin_port);
103     return s;
104   }
105   if (errno == EADDRNOTAVAIL && sin.sin_addr.s_addr != htonl (addr)) {
106     sin.sin_addr.s_addr = htonl (addr);
107     goto again1;
108   }
109 #else /* !HAVE_BINDRESVPORT */
110   for (inetsocket_lastport = IPPORT_RESERVED - 1;
111        inetsocket_lastport > IPPORT_RESERVED/2;
112        inetsocket_lastport--) {
113   again2:
114     sin.sin_port = htons (inetsocket_lastport);
115     if (bind (s, (struct sockaddr *) &sin, sizeof (sin)) >= 0)
116       return (s);
117     if (errno == EADDRNOTAVAIL && sin.sin_addr.s_addr != htonl (addr)) {
118       sin.sin_addr.s_addr = htonl (addr);
119       goto again2;
120     }
121     if (errno != EADDRINUSE)
122       break;
123   }
124 #endif /* !HAVE_BINDRESVPORT */
125   close (s);
126   return -1;
127 #else /* NORESVPORTS */
128   return inetsocket (type, addr, 0);
129 #endif /* NORESVPORTS */
130 }
131 
132 int
inetsocket(int type,u_int16_t port,u_int32_t addr)133 inetsocket (int type, u_int16_t port, u_int32_t addr)
134 {
135   int s;
136   int n;
137   socklen_t sn;
138   struct sockaddr_in sin;
139 
140   bzero (&sin, sizeof (sin));
141   sin.sin_family = AF_INET;
142   sin.sin_port = htons (port);
143   if (addr == INADDR_ANY)
144     sin.sin_addr = inet_bindaddr;
145   else
146     sin.sin_addr.s_addr = htonl (addr);
147   if ((s = socket (AF_INET, type, 0)) < 0)
148     return -1;
149 
150   sn = sizeof (n);
151   n = 1;
152   /* Avoid those annoying TIME_WAITs for TCP */
153   if (port && type == SOCK_STREAM
154       && setsockopt (s, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sn) < 0)
155     fatal ("inetsocket: SO_REUSEADDR: %s\n", strerror (errno));
156 #ifdef IP_FREEBIND
157   n = 1;
158   setsockopt (s, IPPROTO_IP, IP_FREEBIND, &n, sizeof (n));
159 #endif /* IP_FREEBIND */
160  again:
161   if (bind (s, (struct sockaddr *) &sin, sizeof (sin)) >= 0) {
162 #if 0
163     if (type == SOCK_STREAM)
164       warn ("TCP (fd %d) bound: %s\n", s, __backtrace (__FL__));
165 #endif
166     return s;
167   }
168   if (errno == EADDRNOTAVAIL && sin.sin_addr.s_addr != htonl (addr)) {
169     sin.sin_addr.s_addr = htonl (addr);
170     goto again;
171   }
172   close (s);
173   return -1;
174 }
175 
176 int
unixsocket(const char * path)177 unixsocket (const char *path)
178 {
179   sockaddr_un sun;
180 
181   if (strlen (path) >= sizeof (sun.sun_path)) {
182 #ifdef ENAMETOOLONG
183     errno = ENAMETOOLONG;
184 #else /* !ENAMETOOLONG */
185     errno = E2BIG;
186 #endif /* !ENAMETOOLONG */
187     return -1;
188   }
189 
190   bzero (&sun, sizeof (sun));
191   sun.sun_family = AF_UNIX;
192   strcpy (sun.sun_path, path);
193 
194   int fd = socket (AF_UNIX, SOCK_STREAM, 0);
195   if (fd < 0)
196     return -1;
197   if (bind (fd, (sockaddr *) &sun, sizeof (sun)) < 0) {
198     close (fd);
199     return -1;
200   }
201   return fd;
202 }
203 
204 int
unixsocket_connect(const char * path)205 unixsocket_connect (const char *path)
206 {
207   sockaddr_un sun;
208 
209   if (strlen (path) >= sizeof (sun.sun_path)) {
210 #ifdef ENAMETOOLONG
211     errno = ENAMETOOLONG;
212 #else /* !ENAMETOOLONG */
213     errno = E2BIG;
214 #endif /* !ENAMETOOLONG */
215     return -1;
216   }
217 
218   bzero (&sun, sizeof (sun));
219   sun.sun_family = AF_UNIX;
220   strcpy (sun.sun_path, path);
221 
222   int fd = socket (AF_UNIX, SOCK_STREAM, 0);
223   if (fd < 0)
224     return -1;
225   if (connect (fd, (sockaddr *) &sun, sizeof (sun)) < 0) {
226     close (fd);
227     return -1;
228   }
229   return fd;
230 }
231 
232 bool
isunixsocket(int fd)233 isunixsocket (int fd)
234 {
235   sockaddr_un sun;
236   socklen_t sunlen = sizeof (sun);
237   bzero (&sun, sizeof (sun));
238   sun.sun_family = AF_UNIX;
239   if (getsockname (fd, (sockaddr *) &sun, &sunlen) < 0
240       || sun.sun_family != AF_UNIX)
241     return false;
242   return true;
243 }
244 
245 void
close_on_exec(int s,bool set)246 close_on_exec (int s, bool set)
247 {
248   if (fcntl (s, F_SETFD, int (set)) < 0)
249     fatal ("F_SETFD: %s\n", strerror (errno));
250 }
251 
252 int
_make_async(int s)253 _make_async (int s)
254 {
255   int n;
256   if ((n = fcntl (s, F_GETFL)) < 0
257       || fcntl (s, F_SETFL, n | O_NONBLOCK) < 0)
258     return -1;
259   return 0;
260 }
261 
262 void
make_async(int s)263 make_async (int s)
264 {
265   int n;
266   int type;
267   socklen_t sn;
268   if (_make_async (s) < 0)
269     fatal ("O_NONBLOCK: %s\n", strerror (errno));
270   type = 0;
271   sn = sizeof (type);
272   if (getsockopt (s, SOL_SOCKET, SO_TYPE, (char *)&type, &sn) < 0)
273     return;
274 #if defined (SO_RCVBUF) && defined (SO_SNDBUF)
275   if (type == SOCK_STREAM)
276     n = rcvbufsize;
277   else
278     n = maxsobufsize;
279   if (setsockopt (s, SOL_SOCKET, SO_RCVBUF, (char *) &n, sizeof (n)) < 0)
280     warn ("SO_RCVBUF: %s\n", strerror (errno));
281 
282   if (type == SOCK_STREAM)
283     n = sndbufsize;
284   else
285     n = maxsobufsize;
286   if (setsockopt (s, SOL_SOCKET, SO_SNDBUF, (char *) &n, sizeof (n)) < 0)
287     warn ("SO_SNDBUF: %s\n", strerror (errno));
288 #endif /* SO_RCVBUF && SO_SNDBUF */
289   /* Enable keepalives to avoid buffering write data for infinite time */
290   n = 1;
291   if (type == SOCK_STREAM
292       && setsockopt (s, SOL_SOCKET, SO_KEEPALIVE,
293 		     (char *) &n, sizeof (n)) < 0)
294     warn ("SO_KEEPALIVE: %s\n", strerror (errno));
295 }
296 
297 void
make_sync(int s)298 make_sync (int s)
299 {
300   int n;
301   if ((n = fcntl (s, F_GETFL)) >= 0)
302     fcntl (s, F_SETFL, n & ~O_NONBLOCK);
303 }
304 
305 void
tcp_nodelay(int s)306 tcp_nodelay (int s)
307 {
308 #if defined (TCP_NODELAY) || defined (IPTOS_LOWDELAY)
309   int n = 1;
310 #endif /* TCP_NODELAY || IPTOS_LOWDELAY */
311 #ifdef TCP_NODELAY
312   if (setsockopt (s, IPPROTO_TCP, TCP_NODELAY, (char *) &n, sizeof (n)) < 0)
313     warn ("TCP_NODELAY: %m\n");
314 #endif /* TCP_NODELAY */
315 #ifdef IPTOS_LOWDELAY
316   setsockopt (s, IPPROTO_IP, IP_TOS, (char *) &n, sizeof (n));
317 #endif /* IPTOS_LOWDELAY */
318 }
319 
320 void
tcp_abort(int fd)321 tcp_abort (int fd)
322 {
323   struct linger l;
324   l.l_onoff = 1;
325   l.l_linger = 0;
326   setsockopt (fd, SOL_SOCKET, SO_LINGER, (char *) &l, sizeof (l));
327   close (fd);
328 }
329 
330 bool
addreq(const sockaddr * a,const sockaddr * b,socklen_t size)331 addreq (const sockaddr *a, const sockaddr *b, socklen_t size)
332 {
333   if (a->sa_family != b->sa_family)
334     return false;
335   switch (a->sa_family) {
336   case AF_INET:
337     if (implicit_cast<size_t> (size) >= sizeof (sockaddr_in)) {
338       const sockaddr_in *aa = reinterpret_cast<const sockaddr_in *> (a);
339       const sockaddr_in *bb = reinterpret_cast<const sockaddr_in *> (b);
340       return (aa->sin_addr.s_addr == bb->sin_addr.s_addr
341 	      && aa->sin_port == bb->sin_port);
342     }
343     warn ("addreq: %d bytes is too small for AF_INET sockaddrs\n", size);
344     return false;
345   default:
346     warn ("addreq: bad sa_family %d\n", a->sa_family);
347     return false;
348   }
349 }
350 
351 #if NEED_GETPEEREID
352 int
getpeereid(int fd,uid_t * u,gid_t * g)353 getpeereid (int fd, uid_t *u, gid_t *g)
354 {
355   struct ucred cred;
356   socklen_t credlen = sizeof (cred);
357   int r = getsockopt (fd, SOL_SOCKET, SO_PEERCRED, &cred, &credlen);
358   if (r >= 0) {
359     if (u)
360       *u = cred.uid;
361     if (g)
362       *g = cred.gid;
363   }
364   return r;
365 }
366 #endif /* !NEED_GETPEEREID */
367