split up constants.h some
[trinity.git] / fd-sockets.c
blob01fd6997b032e28ec3329088d439555ae480490b
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <fcntl.h>
6 #include <errno.h>
7 #include <sys/types.h>
8 #include <sys/socket.h>
9 #include <sys/stat.h>
11 #include "constants.h"
12 #include "log.h"
13 #include "net.h"
14 #include "maps.h"
15 #include "params.h" // verbose, do_specific_proto
16 #include "protocols.h"
17 #include "random.h"
18 #include "shm.h"
19 #include "trinity.h"
20 #include "uid.h"
21 #include "utils.h"
23 unsigned int nr_sockets = 0;
25 static const char *cachefilename="trinity.socketcache";
27 #define MAX_PER_DOMAIN 5
28 #define MAX_TRIES_PER_DOMAIN 10
30 static int open_socket(unsigned int domain, unsigned int type, unsigned int protocol)
32 int fd;
33 __unused__ int ret;
34 struct sockaddr *sa = NULL;
35 socklen_t salen;
36 struct sockopt so = { 0, 0, 0, 0 };
38 fd = socket(domain, type, protocol);
39 if (fd == -1)
40 return fd;
42 shm->sockets[nr_sockets].fd = fd;
43 shm->sockets[nr_sockets].triplet.family = domain;
44 shm->sockets[nr_sockets].triplet.type = type;
45 shm->sockets[nr_sockets].triplet.protocol = protocol;
47 output(2, "fd[%i] = domain:%i (%s) type:0x%x protocol:%i\n",
48 fd, domain, get_proto_name(domain), type, protocol);
50 /* Set some random socket options. */
51 sso_socket(&shm->sockets[nr_sockets].triplet, &so, fd);
53 nr_sockets++;
55 /* Sometimes, listen on created sockets. */
56 if (rand_bool()) {
57 /* fake a sockaddr. */
58 generate_sockaddr((struct sockaddr **) &sa, (socklen_t *) &salen, domain);
60 ret = bind(fd, sa, salen);
61 /* if (ret == -1)
62 debugf("bind: %s\n", strerror(errno));
63 else
64 debugf("bind: success!\n");
66 ret = listen(fd, (rand() % 2) + 1);
67 /* if (ret == -1)
68 debugf("listen: %s\n", strerror(errno));
69 else
70 debugf("listen: success!\n");
74 /* If we didn't have a function for this sockaddr type, we would
75 * have returned page_rand, so don't free() it or we segv. */
76 if (sa == (struct sockaddr *) page_rand)
77 return fd;
79 if (sa != NULL)
80 free(sa);
82 return fd;
85 static void lock_cachefile(int cachefile, int type)
87 struct flock fl = {
88 .l_len = 0,
89 .l_start = 0,
90 .l_whence = SEEK_SET,
93 fl.l_pid = getpid();
94 fl.l_type = type;
96 if (verbose)
97 output(2, "waiting on lock for cachefile\n");
99 if (fcntl(cachefile, F_SETLKW, &fl) == -1) {
100 perror("fcntl F_SETLKW");
101 exit(1);
104 if (verbose)
105 output(2, "took lock for cachefile\n");
108 static void unlock_cachefile(int cachefile)
110 struct flock fl = {
111 .l_len = 0,
112 .l_start = 0,
113 .l_whence = SEEK_SET,
116 fl.l_pid = getpid();
117 fl.l_type = F_UNLCK;
119 if (fcntl(cachefile, F_SETLK, &fl) == -1) {
120 perror("fcntl F_UNLCK F_SETLK ");
121 exit(1);
124 if (verbose)
125 output(2, "dropped lock for cachefile\n");
128 static unsigned int valid_proto(unsigned int family)
130 const char *famstr;
132 famstr = get_proto_name(family);
134 /* Not used for creating sockets. */
135 if (strncmp(famstr, "PF_UNSPEC", 9) == 0)
136 return FALSE;
137 if (strncmp(famstr, "PF_BRIDGE", 9) == 0)
138 return FALSE;
139 if (strncmp(famstr, "PF_SECURITY", 11) == 0)
140 return FALSE;
142 /* Not actually implemented (or now removed). */
143 if (strncmp(famstr, "PF_NETBEUI", 10) == 0)
144 return FALSE;
145 if (strncmp(famstr, "PF_ASH", 6) == 0)
146 return FALSE;
147 if (strncmp(famstr, "PF_ECONET", 9) == 0)
148 return FALSE;
149 if (strncmp(famstr, "PF_SNA", 6) == 0)
150 return FALSE;
151 if (strncmp(famstr, "PF_WANPIPE", 10) == 0)
152 return FALSE;
154 /* Needs root. */
155 if (orig_uid != 0) {
156 if (strncmp(famstr, "PF_KEY", 6) == 0)
157 return FALSE;
158 if (strncmp(famstr, "PF_PACKET", 9) == 0)
159 return FALSE;
160 if (strncmp(famstr, "PF_LLC", 6) == 0)
161 return FALSE;
164 return TRUE;
167 static int generate_sockets(void)
169 int fd, n, ret = FALSE;
170 int cachefile;
171 unsigned int nr_to_create = NR_SOCKET_FDS;
172 unsigned int buffer[3];
174 cachefile = creat(cachefilename, S_IWUSR|S_IRUSR);
175 if (cachefile == -1)
176 outputerr("Couldn't open cachefile for writing! (%s)\n", strerror(errno));
177 else
178 lock_cachefile(cachefile, F_WRLCK);
181 * Don't loop forever if all protos all are disabled.
183 if (!do_specific_proto) {
184 for (n = 0; n < (int)ARRAY_SIZE(no_protos); n++) {
185 if (!no_protos[n])
186 break;
189 if (n >= (int)ARRAY_SIZE(no_protos))
190 nr_to_create = 0;
193 while (nr_to_create > 0) {
195 struct socket_triplet st;
197 for (st.family = 0; st.family < TRINITY_PF_MAX; st.family++) {
199 /* check for ctrl-c again. */
200 if (shm->exit_reason != STILL_RUNNING)
201 goto out_unlock;
203 if (do_specific_proto == TRUE) {
204 st.family = specific_proto;
205 //FIXME: If we've passed -P and we're spinning here without making progress
206 // then we should abort after a few hundred loops.
209 if (get_proto_name(st.family) == NULL)
210 continue;
212 if (valid_proto(st.family) == FALSE) {
213 if (do_specific_proto == TRUE) {
214 outputerr("Can't do protocol %s\n", get_proto_name(st.family));
215 goto out_unlock;
216 } else {
217 continue;
221 BUG_ON(st.family >= ARRAY_SIZE(no_protos));
222 if (no_protos[st.family])
223 continue;
225 if (sanitise_socket_triplet(&st) == -1)
226 rand_proto_type(&st);
228 fd = open_socket(st.family, st.type, st.protocol);
229 if (fd > -1) {
230 nr_to_create--;
232 if (cachefile != -1) {
233 buffer[0] = st.family;
234 buffer[1] = st.type;
235 buffer[2] = st.protocol;
236 n = write(cachefile, &buffer, sizeof(int) * 3);
237 if (n == -1) {
238 outputerr("something went wrong writing the cachefile!\n");
239 goto out_unlock;
243 if (nr_to_create == 0)
244 goto done;
245 } else {
246 //outputerr("Couldn't open family:%d (%s)\n", st.family, get_proto_name(st.family));
251 done:
252 ret = TRUE;
254 output(1, "created %d sockets\n", nr_sockets);
256 out_unlock:
257 if (cachefile != -1) {
258 unlock_cachefile(cachefile);
259 close(cachefile);
262 return ret;
266 void close_sockets(void)
268 unsigned int i;
269 int fd;
270 int r = 0;
271 struct linger ling = { .l_onoff = FALSE, .l_linger = 0 };
273 for (i = 0; i < nr_sockets; i++) {
275 //FIXME: This is a workaround for a weird bug where we hang forevre
276 // waiting for bluetooth sockets when we setsockopt.
277 // Hopefully at some point we can remove this when someone figures out what's going on.
278 if (shm->sockets[i].triplet.family == PF_BLUETOOTH)
279 continue;
281 /* Grab an fd, and nuke it before someone else uses it. */
282 fd = shm->sockets[i].fd;
283 shm->sockets[i].fd = 0;
285 /* disable linger */
286 r = setsockopt(fd, SOL_SOCKET, SO_LINGER, &ling, sizeof(struct linger));
287 if (r)
288 perror("setsockopt");
290 r = shutdown(fd, SHUT_RDWR);
291 if (r)
292 perror("shutdown");
294 if (close(fd) != 0)
295 output(1, "failed to close socket [%d:%d:%d].(%s)\n",
296 shm->sockets[i].triplet.family,
297 shm->sockets[i].triplet.type,
298 shm->sockets[i].triplet.protocol,
299 strerror(errno));
302 nr_sockets = 0;
305 unsigned int open_sockets(void)
307 int cachefile;
308 unsigned int domain, type, protocol;
309 unsigned int buffer[3];
310 int bytesread=-1;
311 int fd;
312 int ret;
314 cachefile = open(cachefilename, O_RDONLY);
315 if (cachefile < 0) {
316 output(1, "Couldn't find socket cachefile. Regenerating.\n");
317 ret = generate_sockets();
318 return ret;
321 lock_cachefile(cachefile, F_RDLCK);
323 while (bytesread != 0) {
324 bytesread = read(cachefile, buffer, sizeof(int) * 3);
325 if (bytesread == 0)
326 break;
328 domain = buffer[0];
329 type = buffer[1];
330 protocol = buffer[2];
332 if ((do_specific_proto == TRUE && domain != specific_proto) ||
333 (domain < ARRAY_SIZE(no_protos) && no_protos[domain] == TRUE)) {
334 output(1, "ignoring socket cachefile due to specific "
335 "protocol request (or protocol disabled), "
336 "and stale data in cachefile.\n");
337 regenerate:
338 unlock_cachefile(cachefile); /* drop the reader lock. */
339 close(cachefile);
340 unlink(cachefilename);
341 ret = generate_sockets();
342 return ret;
345 fd = open_socket(domain, type, protocol);
346 if (fd < 0) {
347 output(1, "Cachefile is stale. Need to regenerate.\n");
348 close_sockets();
349 goto regenerate;
352 /* check for ctrl-c */
353 if (shm->exit_reason != STILL_RUNNING) {
354 close(cachefile);
355 return FALSE;
359 if (nr_sockets < NR_SOCKET_FDS) {
360 output(1, "Insufficient sockets in cachefile (%d). Regenerating.\n", nr_sockets);
361 goto regenerate;
364 output(1, "%d sockets created based on info from socket cachefile.\n", nr_sockets);
366 unlock_cachefile(cachefile);
367 close(cachefile);
369 return TRUE;