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