1 // Copyright 2017 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #define _POSIX_C_SOURCE 200809L
6
7 #define _GNU_SOURCE
8 #define _DARWIN_C_SOURCE
9
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <getopt.h>
13 #include <net/if.h>
14 #include <netinet/in.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <sys/time.h>
19
20 #include "netprotocol.h"
21
22 static const char* hostname;
23 static struct sockaddr_in6 addr;
24 static bool found = false;
25 static char found_device_nodename[MAX_NODENAME_LENGTH];
26 static bool fuchsia_address = false;
27 static bool local_address = false;
28 static const char* appname;
29
on_device(device_info_t * device,void * cookie)30 static bool on_device(device_info_t* device, void* cookie) {
31 if (hostname != NULL && strcmp(hostname, device->nodename)) {
32 // Asking for a specific address and this isn't it.
33 return true;
34 }
35
36 if (found && strcmp(found_device_nodename, device->nodename) != 0) {
37 fprintf(stderr, "Multiple devices found, including %s and %s. Specify a hostname.\n",
38 found_device_nodename, device->nodename);
39 exit(1);
40 }
41
42 addr = device->inet6_addr;
43 strncpy(found_device_nodename, device->nodename, MAX_NODENAME_LENGTH);
44 found = true;
45 return true;
46 }
47
usage(void)48 static void usage(void) {
49 fprintf(stderr, "usage: %s [options] [hostname]\n", appname);
50 netboot_usage(false);
51 fprintf(stderr, " --fuchsia Use fuchsia link local addresses.\n");
52 fprintf(stderr, " --local Print local address that routes to remote.\n");
53 }
54
55 static struct option netaddr_opts[] = {
56 {"fuchsia", no_argument, NULL, 'f'},
57 {"local", no_argument, NULL, 'l'},
58 {NULL, 0, NULL, 0},
59 };
60
netaddr_opt_callback(int ch,int argc,char * const * argv)61 static bool netaddr_opt_callback(int ch, int argc, char* const* argv) {
62 switch (ch) {
63 case 'f':
64 fuchsia_address = true;
65 break;
66 case 'l':
67 local_address = true;
68 break;
69 default:
70 return false;
71 }
72 return true;
73 }
74
main(int argc,char ** argv)75 int main(int argc, char** argv) {
76 appname = argv[0];
77 int index = netboot_handle_custom_getopt(argc, argv, netaddr_opts, 1, netaddr_opt_callback);
78 if (index < 0) {
79 usage();
80 return -1;
81 }
82
83 argv += index;
84 argc -= index;
85
86 if (argc > 1) {
87 usage();
88 }
89
90 if (argc == 1) {
91 hostname = argv[0];
92 if (!*hostname || (*hostname == ':' && hostname[1] == '\0'))
93 hostname = NULL;
94 }
95
96 if (netboot_discover(NB_SERVER_PORT, NULL, on_device, NULL) || !found) {
97 fprintf(stderr, "Failed to discover %s\n", hostname ? hostname : "");
98 return 1;
99 }
100
101 if (local_address) {
102 // Bind an ephemeral UDP socket to the Fuchsia target address, then inspect
103 // the local address it bound to (poor mans portable "lookup route").
104 int s;
105 if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
106 fprintf(stderr, "error: cannot create socket: %s\n", strerror(errno));
107 return -1;
108 }
109 if (connect(s, (const struct sockaddr*)&addr, sizeof(addr)) < 0) {
110 fprintf(stderr, "error: cannot \"connect\" socket: %s\n", strerror(errno));
111 return -1;
112 }
113 socklen_t addrlen = sizeof(addr);
114 if (getsockname(s, (struct sockaddr*)&addr, &addrlen) < 0) {
115 fprintf(stderr, "error: %s\n", strerror(errno));
116 return -1;
117 }
118 shutdown(s, SHUT_RDWR);
119 }
120
121 if (fuchsia_address) {
122 // Make it a valid link-local address by fiddling some bits.
123 addr.sin6_addr.s6_addr[11] = 0xFF;
124 }
125
126 // Get the string form of the address.
127 char addr_s[INET6_ADDRSTRLEN];
128 inet_ntop(AF_INET6, &addr.sin6_addr, addr_s, sizeof(addr_s));
129
130 // Get the name of the interface.
131 char ifname[IF_NAMESIZE];
132 if_indextoname(addr.sin6_scope_id, ifname);
133 fprintf(stdout, "%s%%%s\n", addr_s, ifname);
134
135 return 0;
136 }
137