1 | 1 | simandl | #include <arpa/inet.h> |
2 | | | #include <netinet/in.h> |
3 | | | #include <stdio.h> |
4 | | | #include <sys/types.h> |
5 | | | #include <sys/socket.h> |
6 | | | #include <unistd.h> |
7 | | | #include <stdlib.h> |
8 | | | #include <string.h> |
9 | | | #include <ctype.h> |
10 | | | #include <netdb.h> |
11 | | | #include <sys/select.h> |
12 | | | #include <sys/time.h> |
13 | | | #include <ifaddrs.h> |
14 | | | #include <net/if.h> |
15 | | | |
16 | | | #define BUFLEN 1024 |
17 | | | #define UBNT_PORT 10001 |
18 | | | |
19 | | | #define SNDLEN 4 |
20 | | | |
21 | | | |
22 | | | struct udisc_msg_hdr { |
23 | | | uint8_t vh; |
24 | | | uint8_t vl; |
25 | | | uint16_t length; |
26 | | | } __attribute__((packed)); |
27 | | | |
28 | | | struct udisc_rec_hdr { |
29 | | | uint8_t type; |
30 | | | uint16_t length; |
31 | | | } __attribute__((packed)); |
32 | | | |
33 | 5 | jikos | struct cache_entry { |
34 | | | unsigned long s_addr; |
35 | | | unsigned short s_port; |
36 | | | }; |
37 | | | |
38 | | | #define MAXENTRIES 1024 |
39 | | | struct cache_entry cache[MAXENTRIES]; |
40 | | | size_t cachep; |
41 | | | |
42 | | | int add_cache(unsigned long s_addr, unsigned short s_port) |
43 | | | { |
44 | | | if (cachep == 1024) |
45 | | | return 0; |
46 | | | |
47 | | | cache[cachep].s_addr = s_addr; |
48 | | | cache[cachep].s_port = s_port; |
49 | | | cachep++; |
50 | | | |
51 | | | return 1; |
52 | | | } |
53 | | | |
54 | | | int lookup_cache(unsigned long s_addr, unsigned short s_port) |
55 | | | { |
56 | | | int i; |
57 | | | |
58 | | | for (i = 0; i <= cachep; i++) |
59 | | | if (cache[i].s_addr == s_addr && cache[i].s_port == s_port) |
60 | | | return 1; |
61 | | | return 0; |
62 | | | } |
63 | | | |
64 | 1 | simandl | int main(int argc, char *argv[]) |
65 | | | { |
66 | | | |
67 | | | struct sockaddr_in si_srv; |
68 | | | struct sockaddr_in si_clnt; |
69 | | | int sock; |
70 | | | socklen_t soutlen = sizeof(si_srv); |
71 | | | socklen_t sinlen = sizeof(si_srv); |
72 | | | char rcvbuf[BUFLEN]; |
73 | | | char sendbuf[BUFLEN]; |
74 | | | ssize_t rlen; |
75 | | | |
76 | | | struct udisc_msg_hdr *umh; |
77 | | | struct udisc_rec_hdr *urh; |
78 | | | char *p; |
79 | | | char *dest; |
80 | | | |
81 | | | struct addrinfo hints; |
82 | | | struct addrinfo *ai_res; |
83 | | | int result; |
84 | | | int i, t; |
85 | | | |
86 | | | fd_set socks; |
87 | | | struct timeval tv; |
88 | | | |
89 | | | int multicast; |
90 | | | |
91 | | | int responses = 0; |
92 | | | |
93 | | | struct ifaddrs *ifa; |
94 | | | |
95 | 3 | simandl | memset(&hints, 0, sizeof(hints)); |
96 | 1 | simandl | hints.ai_protocol = IPPROTO_UDP; |
97 | | | hints.ai_socktype = SOCK_DGRAM; |
98 | | | hints.ai_flags = AI_CANONNAME; |
99 | | | hints.ai_family = PF_UNSPEC; |
100 | | | |
101 | | | if (argc < 2) { |
102 | | | dest = "233.89.188.1"; |
103 | | | multicast = 1; |
104 | | | } else { |
105 | | | dest = argv[1]; |
106 | | | multicast = 0; |
107 | | | } |
108 | | | |
109 | | | if ((result = getaddrinfo(dest, NULL, &hints, &ai_res))) { |
110 | | | printf("getaddrinfo-failed: %s\n", gai_strerror(result)); |
111 | | | exit(1); |
112 | | | } |
113 | | | |
114 | | | if ((sock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { |
115 | | | perror("socket"); |
116 | | | exit(1); |
117 | | | } |
118 | | | |
119 | | | memset((char *) &si_clnt, 0, sizeof(si_clnt)); |
120 | | | si_clnt.sin_family = AF_INET; |
121 | | | si_clnt.sin_port = htons(UBNT_PORT); |
122 | | | si_clnt.sin_addr = ((struct sockaddr_in *)ai_res->ai_addr)->sin_addr; |
123 | | | |
124 | | | sendbuf[0] = 1; |
125 | | | sendbuf[1] = 0; |
126 | | | sendbuf[2] = 0; |
127 | | | sendbuf[3] = 0; |
128 | | | |
129 | | | if (multicast) { |
130 | | | |
131 | | | char loop = 0; |
132 | | | setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(char)); |
133 | | | |
134 | | | if (getifaddrs(&ifa)) { |
135 | | | perror("getifaddrs"); |
136 | | | exit(1); |
137 | | | } |
138 | | | |
139 | | | for (; ifa; ifa = ifa->ifa_next) { |
140 | | | |
141 | | | if (!(ifa->ifa_flags & IFF_UP)) |
142 | | | continue; |
143 | | | if (!ifa->ifa_addr) |
144 | | | continue; |
145 | | | if (ifa->ifa_addr->sa_family != AF_INET) |
146 | | | continue; |
147 | | | if (!strcmp(ifa->ifa_name, "lo")) |
148 | | | continue; |
149 | | | if (!strncmp(ifa->ifa_name, "dummy", 5)) |
150 | | | continue; |
151 | | | |
152 | | | printf("Probing %s:%d", inet_ntoa(si_clnt.sin_addr), UBNT_PORT); |
153 | | | printf(" via %s (%s).\n", ifa->ifa_name, inet_ntoa(((struct sockaddr_in *)ifa->ifa_addr)->sin_addr)); |
154 | | | |
155 | | | if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &(((struct sockaddr_in *)ifa->ifa_addr)->sin_addr), sizeof(struct in_addr))) { |
156 | | | perror("setsockopt"); |
157 | | | exit(1); |
158 | | | } |
159 | | | |
160 | | | if (sendto(sock, sendbuf, SNDLEN, 0, (struct sockaddr *)&si_clnt, soutlen) == -1) { |
161 | | | perror("socket"); |
162 | | | exit(1); |
163 | | | } |
164 | | | } |
165 | | | |
166 | | | } else { |
167 | | | |
168 | | | printf("Probing %s:%d.\n", inet_ntoa(si_clnt.sin_addr), UBNT_PORT); |
169 | | | |
170 | | | if (sendto(sock, sendbuf, SNDLEN, 0, (struct sockaddr *)&si_clnt, soutlen) == -1) { |
171 | | | perror("socket"); |
172 | | | exit(1); |
173 | | | } |
174 | | | } |
175 | | | |
176 | | | |
177 | | | FD_ZERO(&socks); |
178 | | | FD_SET(sock, &socks); |
179 | | | tv.tv_sec = 0; |
180 | | | tv.tv_usec = 200000; |
181 | | | |
182 | | | while (select(sock + 1, &socks, NULL, NULL, &tv)) { |
183 | | | |
184 | | | if ((rlen = recvfrom(sock, rcvbuf, BUFLEN, 0, (struct sockaddr *)&si_srv, &sinlen)) == -1) { |
185 | | | perror("recvfrom"); |
186 | | | exit(1); |
187 | | | } |
188 | | | |
189 | | | if (ntohs(si_srv.sin_port) != UBNT_PORT) |
190 | | | continue; |
191 | | | |
192 | | | responses++; |
193 | | | |
194 | 5 | jikos | if (!lookup_cache(si_srv.sin_addr.s_addr, si_srv.sin_port)) { |
195 | | | if (!add_cache(si_srv.sin_addr.s_addr, si_srv.sin_port)) { |
196 | | | fprintf(stderr, "too many unique responses\n"); |
197 | | | exit(1); |
198 | | | } |
199 | | | } else { |
200 | | | continue; |
201 | | | } |
202 | | | |
203 | 1 | simandl | printf("\n"); |
204 | | | printf("Response : %s:%d\n", inet_ntoa(si_srv.sin_addr), ntohs(si_srv.sin_port)); |
205 | | | |
206 | | | p = rcvbuf; |
207 | | | |
208 | | | umh = (void*)p; |
209 | | | p += sizeof(struct udisc_msg_hdr); |
210 | | | rlen -= sizeof(struct udisc_msg_hdr); |
211 | | | |
212 | | | printf("Discovery: v%d.%02d\n", umh->vh, umh->vl); |
213 | | | |
214 | | | if (umh->vh != 1 && umh->vl != 0 && ntohs(umh->length) != rlen) { |
215 | | | fprintf(stderr, "protocol invalid\n"); |
216 | | | exit(1); |
217 | | | } |
218 | | | |
219 | | | while (rlen > 0) { |
220 | | | |
221 | | | urh = (void*)p; |
222 | | | p += sizeof(struct udisc_rec_hdr); |
223 | | | rlen -= sizeof(struct udisc_rec_hdr); |
224 | | | |
225 | | | |
226 | | | |
227 | | | |
228 | | | switch (urh->type) { |
229 | | | |
230 | | | |
231 | | | |
232 | | | case 2: |
233 | | | printf("IP Address: "); |
234 | | | for (i = 0; i < 4; i++) { |
235 | | | printf("%d", (unsigned char) p[i + 6]); |
236 | | | if (i < 3) |
237 | | | printf("."); |
238 | | | } |
239 | | | |
240 | | | printf(" ("); |
241 | | | for (i = 0; i < 6; i++) { |
242 | | | printf("%02x", (unsigned char) p[i]); |
243 | | | if (i < 5) |
244 | | | printf(":"); |
245 | | | } |
246 | | | |
247 | | | printf(")\n"); |
248 | | | break; |
249 | | | |
250 | | | |
251 | | | case 1: |
252 | | | printf("MAC Address: "); |
253 | | | for (i = 0; i < 6; i++) { |
254 | | | printf("%02x", (unsigned char) p[i]); |
255 | | | if (i < 5) |
256 | | | printf(":"); |
257 | | | } |
258 | | | |
259 | | | printf("\n"); |
260 | | | break; |
261 | | | |
262 | | | |
263 | | | case 3: |
264 | | | case 11: |
265 | | | case 12: |
266 | | | case 13: |
267 | | | |
268 | | | switch(urh->type) { |
269 | | | |
270 | | | case 3: |
271 | | | printf("FW Version: "); |
272 | | | break; |
273 | | | case 11: |
274 | | | printf("Hostname: "); |
275 | | | break; |
276 | | | case 12: |
277 | | | printf("Model: "); |
278 | | | break; |
279 | | | case 13: |
280 | | | printf("ESSID: "); |
281 | | | break; |
282 | | | } |
283 | | | |
284 | | | for (i = 0; i < ntohs(urh->length); i++) |
285 | | | printf("%c", isprint(p[i]) ? p[i] : '.'); |
286 | | | printf("\n"); |
287 | | | |
288 | | | break; |
289 | | | |
290 | | | case 14: |
291 | | | |
292 | | | printf("Mode: %s (%d)\n", *p == 2 ? "Station" : (*p == 3 ? "AP" : "?"), *p); |
293 | | | break; |
294 | | | |
295 | | | case 10: |
296 | | | t = ntohl(*(uint32_t *)p); |
297 | | | printf("Uptime: %dd, %d:%02d:%02d\n", t / 60 / 60 / 24, t / 60 / 60 % 24, t / 60 % 60, t % 60); |
298 | | | break; |
299 | | | |
300 | 4 | simandl | case 16: |
301 | | | printf("System ID: 0x%02hhX%02hhX\n", p[0], p[1]); |
302 | | | break; |
303 | | | |
304 | 1 | simandl | default: |
305 | | | |
306 | | | printf("Entry %3d: ", urh->type); |
307 | | | |
308 | | | for (i = 0; i < ntohs(urh->length); i++) |
309 | | | printf("%02x ", (unsigned char) p[i]); |
310 | | | |
311 | | | printf(" >"); |
312 | | | |
313 | | | for (i = 0; i < ntohs(urh->length); i++) |
314 | | | printf("%c", isprint(p[i]) ? p[i] : '.'); |
315 | | | printf("<\n"); |
316 | | | |
317 | | | |
318 | | | } |
319 | | | |
320 | | | p += ntohs(urh->length); |
321 | | | rlen -= ntohs(urh->length); |
322 | | | } |
323 | | | |
324 | | | |
325 | | | } |
326 | | | |
327 | | | |
328 | | | close(sock); |
329 | | | |
330 | | | if (!responses) { |
331 | | | printf("No UBNT routers found.\n"); |
332 | | | return 1; |
333 | | | } |
334 | | | |
335 | | | printf("\n"); |
336 | | | |
337 | | | return 0; |
338 | | | |
339 | | | } |
340 | | | |