Commit | Line | Data |
---|---|---|
6693db17 | 1 | /*- |
82a5c12e MD |
2 | * Copyright 2001, David Leonard. All rights reserved. |
3 | * Redistribution and use in source and binary forms with or without | |
4 | * modification are permitted provided that this notice is preserved. | |
5 | * This software is provided ``as is'' without express or implied warranty. | |
6 | * | |
7 | * $OpenBSD: list.c,v 1.5 2007/09/04 22:39:31 hshoexer Exp $ | |
82a5c12e MD |
8 | */ |
9 | ||
10 | #include <stdio.h> | |
11 | #include <stdlib.h> | |
12 | #include <string.h> | |
13 | #include <netdb.h> | |
14 | #include <unistd.h> | |
15 | #include <errno.h> | |
16 | #include <err.h> | |
17 | ||
18 | #include <sys/types.h> | |
19 | #include <sys/socket.h> | |
20 | #include <sys/sockio.h> | |
21 | #include <sys/ioctl.h> | |
22 | ||
23 | #include <netinet/in.h> | |
24 | #include <net/if.h> | |
25 | ||
26 | #include <arpa/inet.h> | |
27 | ||
28 | #include "hunt.h" | |
29 | #include "list.h" | |
30 | ||
31 | /* Wait at most 5 seconds for a reply */ | |
32 | #define LIST_DELAY 5 | |
33 | ||
34 | struct driver *drivers = NULL; | |
35 | int numdrivers = 0; | |
36 | int maxdrivers = 0; | |
37 | ||
38 | u_int16_t Server_port; | |
39 | ||
40 | static int numprobes = 0; | |
41 | static int probe_sock[64]; | |
42 | static struct timeval probe_timeout; | |
43 | ||
44 | struct driver * | |
6beb426b | 45 | next_driver(void) |
82a5c12e MD |
46 | { |
47 | ||
48 | return next_driver_fd(-1); | |
49 | } | |
50 | ||
51 | struct driver * | |
6beb426b | 52 | next_driver_fd(int fd) |
82a5c12e MD |
53 | { |
54 | fd_set r; | |
55 | int maxfd = -1; | |
435f923e SW |
56 | int i, s, ret; |
57 | socklen_t len; | |
82a5c12e MD |
58 | struct driver *driver; |
59 | u_int16_t resp; | |
60 | ||
61 | if (fd == -1 && numprobes == 0) | |
62 | return NULL; | |
63 | ||
64 | again: | |
65 | FD_ZERO(&r); | |
66 | if (fd != -1) { | |
67 | FD_SET(fd, &r); | |
68 | maxfd = fd; | |
69 | } | |
70 | for (i = 0; i < numprobes; i++) { | |
71 | FD_SET(probe_sock[i], &r); | |
72 | if (probe_sock[i] > maxfd) | |
73 | maxfd = probe_sock[i]; | |
74 | } | |
75 | ||
76 | probe_timeout.tv_sec = LIST_DELAY; | |
77 | probe_timeout.tv_usec = 0; | |
78 | ret = select(maxfd + 1, &r, NULL, NULL, &probe_timeout); | |
79 | ||
80 | if (ret == -1) { | |
81 | if (errno == EINTR) | |
82 | goto again; | |
83 | err(1, "select"); | |
84 | } | |
85 | ||
86 | if (ret == 0) { | |
87 | /* Timeout - close all sockets */ | |
88 | for (i = 0; i < numprobes; i++) | |
89 | close(probe_sock[i]); | |
90 | numprobes = 0; | |
91 | return NULL; | |
92 | } | |
93 | ||
6693db17 | 94 | if (fd != -1 && FD_ISSET(fd, &r)) |
82a5c12e MD |
95 | /* Keypress. Return magic number */ |
96 | return (struct driver *)-1; | |
97 | ||
98 | for (i = 0; i < numprobes; i++) | |
99 | /* Find the first ready socket */ | |
100 | if (FD_ISSET(probe_sock[i], &r)) | |
101 | break; | |
102 | ||
103 | s = probe_sock[i]; | |
104 | ||
105 | if (numdrivers >= maxdrivers) { | |
106 | if (maxdrivers) { | |
107 | maxdrivers *= 2; | |
108 | drivers = realloc(drivers, sizeof *driver * maxdrivers); | |
109 | } else { | |
110 | maxdrivers = 16; | |
111 | drivers = calloc(sizeof *driver, maxdrivers); | |
112 | } | |
113 | if (drivers == NULL) | |
114 | err(1, "malloc"); | |
115 | } | |
116 | driver = &drivers[numdrivers]; | |
117 | len = sizeof driver->addr; | |
118 | ret = recvfrom(s, &resp, sizeof resp, 0, &driver->addr, &len); | |
119 | if (ret == -1) | |
120 | goto again; | |
121 | driver->response = ntohs(resp); | |
122 | ||
123 | switch (driver->addr.sa_family) { | |
124 | case AF_INET: | |
125 | case AF_INET6: | |
126 | ((struct sockaddr_in *)&driver->addr)->sin_port = | |
127 | htons(driver->response); | |
128 | break; | |
129 | } | |
130 | numdrivers++; | |
131 | return driver; | |
132 | } | |
133 | ||
134 | /* Return the hostname for a driver. */ | |
135 | const char * | |
6beb426b | 136 | driver_name(struct driver *driver) |
82a5c12e MD |
137 | { |
138 | const char *name; | |
139 | static char buf[80]; | |
140 | struct hostent *hp; | |
141 | struct sockaddr_in *sin; | |
142 | ||
143 | name = NULL; | |
144 | ||
145 | if (driver->addr.sa_family == AF_INET) { | |
146 | sin = (struct sockaddr_in *)&driver->addr; | |
15b85273 SW |
147 | hp = gethostbyaddr(&sin->sin_addr, sizeof sin->sin_addr, |
148 | AF_INET); | |
82a5c12e MD |
149 | if (hp != NULL) |
150 | name = hp->h_name; | |
151 | else { | |
6693db17 | 152 | name = inet_ntop(AF_INET, &sin->sin_addr, |
82a5c12e MD |
153 | buf, sizeof buf); |
154 | } | |
155 | } | |
156 | ||
157 | return name; | |
158 | } | |
159 | ||
160 | static int | |
161 | start_probe(struct sockaddr *addr, u_int16_t req) | |
162 | { | |
163 | u_int16_t msg; | |
164 | int s; | |
165 | int enable; | |
166 | ||
167 | if (numprobes >= (int)(sizeof probe_sock / sizeof probe_sock[0])) { | |
168 | /* Just ridiculous */ | |
169 | return -1; | |
170 | } | |
171 | ||
172 | s = socket(addr->sa_family, SOCK_DGRAM, 0); | |
173 | if (s < 0) { | |
174 | warn("socket"); | |
175 | return -1; | |
176 | } | |
177 | ||
178 | enable = 1; | |
179 | setsockopt(s, SOL_SOCKET, SO_BROADCAST, &enable, sizeof enable); | |
180 | ||
181 | switch (addr->sa_family) { | |
182 | case AF_INET: | |
183 | case AF_INET6: | |
6693db17 | 184 | ((struct sockaddr_in *)addr)->sin_port = |
82a5c12e MD |
185 | htons(Server_port); |
186 | break; | |
187 | } | |
188 | ||
189 | msg = htons(req); | |
190 | if (sendto(s, &msg, sizeof msg, 0, addr, addr->sa_len) == -1) | |
191 | warn("sendto"); | |
192 | probe_sock[numprobes++] = s; | |
193 | ||
194 | return 0; | |
195 | } | |
196 | ||
197 | void | |
6beb426b | 198 | probe_cleanup(void) |
82a5c12e MD |
199 | { |
200 | int i; | |
201 | ||
202 | for (i = 0; i < numprobes; i++) | |
203 | close(probe_sock[i]); | |
204 | numprobes = 0; | |
205 | } | |
206 | ||
207 | /* | |
208 | * If we have no preferred host then send a broadcast message to everyone. | |
209 | * Otherwise, send the request message only to the preferred host. | |
210 | */ | |
211 | void | |
6beb426b | 212 | probe_drivers(u_int16_t req, char *preferred) |
82a5c12e MD |
213 | { |
214 | struct sockaddr_in *target; | |
215 | struct sockaddr_in localhost; | |
216 | struct hostent *he; | |
217 | char *inbuf = NULL, *ninbuf; | |
218 | struct ifconf ifc; | |
219 | struct ifreq *ifr; | |
220 | int fd, inlen = 8192; | |
221 | int i, len; | |
222 | ||
223 | numdrivers = 0; | |
224 | ||
225 | probe_cleanup(); | |
226 | ||
227 | /* Send exclusively to a preferred host. */ | |
228 | if (preferred) { | |
229 | struct sockaddr_in sin; | |
230 | ||
231 | target = NULL; | |
232 | ||
233 | if (!target) { | |
234 | sin.sin_family = AF_INET; | |
235 | sin.sin_len = sizeof sin; | |
236 | if (inet_pton(AF_INET, preferred, &sin.sin_addr) == 1) | |
237 | target = &sin; | |
238 | } | |
239 | ||
240 | if (!target && (he = gethostbyname(preferred)) != NULL) { | |
241 | sin.sin_family = he->h_addrtype; | |
242 | sin.sin_len = sizeof sin; | |
243 | memcpy(&sin.sin_addr, he->h_addr, he->h_length); | |
244 | target = &sin; | |
245 | } | |
246 | ||
247 | if (!target) | |
248 | errx(1, "Bad hostname: %s", preferred); | |
249 | ||
250 | start_probe((struct sockaddr *)target, req); | |
251 | return; | |
252 | } | |
253 | ||
254 | /* Send a query to the local machine: */ | |
255 | localhost.sin_family = AF_INET; | |
256 | localhost.sin_len = sizeof localhost; | |
257 | localhost.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | |
258 | start_probe((struct sockaddr *)&localhost, req); | |
259 | ||
260 | if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) | |
261 | err(1, "socket"); | |
262 | ||
263 | /* Find all attached networks: */ | |
264 | while (1) { | |
265 | ifc.ifc_len = inlen; | |
266 | if ((ninbuf = realloc(inbuf, inlen)) == NULL) | |
267 | err(1, "malloc"); | |
268 | ifc.ifc_buf = inbuf = ninbuf; | |
6693db17 | 269 | if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0) |
82a5c12e MD |
270 | err(1, "SIOCGIFCONF"); |
271 | if (ifc.ifc_len + (int)sizeof(*ifr) < inlen) | |
272 | break; | |
273 | inlen *= 2; | |
274 | } | |
275 | ||
276 | /* Send a request to every attached broadcast address: */ | |
277 | ifr = ifc.ifc_req; | |
278 | for (i = 0; i < ifc.ifc_len; | |
279 | i += len, ifr = (struct ifreq *)((caddr_t)ifr + len)) { | |
280 | len = sizeof(ifr->ifr_name) + | |
281 | (ifr->ifr_addr.sa_len > sizeof(struct sockaddr) ? | |
282 | ifr->ifr_addr.sa_len : sizeof(struct sockaddr)); | |
283 | ||
284 | if (ifr->ifr_addr.sa_family != AF_INET) | |
285 | continue; | |
286 | ||
287 | if (ioctl(fd, SIOCGIFFLAGS, (caddr_t)ifr) < 0) { | |
288 | warn("%s: SIOCGIFFLAGS", ifr->ifr_name); | |
289 | continue; | |
290 | } | |
291 | if ((ifr->ifr_flags & IFF_UP) == 0) | |
292 | continue; | |
293 | if ((ifr->ifr_flags & IFF_BROADCAST) != 0) { | |
294 | if (ioctl(fd, SIOCGIFBRDADDR, (caddr_t)ifr) < 0) { | |
295 | warn("%s: SIOCGIFBRDADDR", ifr->ifr_name); | |
296 | continue; | |
297 | } | |
298 | target = (struct sockaddr_in *)&ifr->ifr_dstaddr; | |
299 | } else if ((ifr->ifr_flags & IFF_POINTOPOINT) != 0) { | |
300 | if (ioctl(fd, SIOCGIFDSTADDR, (caddr_t)ifr) < 0) { | |
301 | warn("%s: SIOCGIFDSTADDR", ifr->ifr_name); | |
302 | continue; | |
303 | } | |
304 | target = (struct sockaddr_in *)&ifr->ifr_broadaddr; | |
305 | } else | |
306 | continue; | |
307 | ||
308 | start_probe((struct sockaddr *)target, req); | |
d9f85b33 | 309 | } |
310 | free(inbuf); | |
311 | close(fd); | |
82a5c12e | 312 | } |