// Copyright 2017 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #define _POSIX_C_SOURCE 200809L #define _GNU_SOURCE #define _DARWIN_C_SOURCE #include #include #include #include #include #include #include #include #include #include "netprotocol.h" static const char* hostname; static struct sockaddr_in6 addr; static bool found = false; static char found_device_nodename[MAX_NODENAME_LENGTH]; static bool fuchsia_address = false; static bool local_address = false; static const char* appname; static bool on_device(device_info_t* device, void* cookie) { if (hostname != NULL && strcmp(hostname, device->nodename)) { // Asking for a specific address and this isn't it. return true; } if (found && strcmp(found_device_nodename, device->nodename) != 0) { fprintf(stderr, "Multiple devices found, including %s and %s. Specify a hostname.\n", found_device_nodename, device->nodename); exit(1); } addr = device->inet6_addr; strncpy(found_device_nodename, device->nodename, MAX_NODENAME_LENGTH); found = true; return true; } static void usage(void) { fprintf(stderr, "usage: %s [options] [hostname]\n", appname); netboot_usage(false); fprintf(stderr, " --fuchsia Use fuchsia link local addresses.\n"); fprintf(stderr, " --local Print local address that routes to remote.\n"); } static struct option netaddr_opts[] = { {"fuchsia", no_argument, NULL, 'f'}, {"local", no_argument, NULL, 'l'}, {NULL, 0, NULL, 0}, }; static bool netaddr_opt_callback(int ch, int argc, char * const *argv) { switch (ch) { case 'f': fuchsia_address = true; break; case 'l': local_address = true; break; default: return false; } return true; } int main(int argc, char** argv) { appname = argv[0]; int index = netboot_handle_custom_getopt(argc, argv, netaddr_opts, 1, netaddr_opt_callback); if (index < 0) { usage(); return -1; } argv += index; argc -= index; if (argc > 1) { usage(); } if (argc == 1) { hostname = argv[0]; if (!*hostname || (*hostname == ':' && hostname[1] == '\0')) hostname = NULL; } if (netboot_discover(NB_SERVER_PORT, NULL, on_device, NULL) || !found) { fprintf(stderr, "Failed to discover %s\n", hostname?hostname:""); return 1; } if (local_address) { // Bind an ephemeral UDP socket to the Fuchsia target address, then inspect // the local address it bound to (poor mans portable "lookup route"). int s; if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) { fprintf(stderr, "error: cannot create socket: %s\n", strerror(errno)); return -1; } if (connect(s, (const struct sockaddr *)&addr, sizeof(addr)) < 0) { fprintf(stderr, "error: cannot \"connect\" socket: %s\n", strerror(errno)); return -1; } socklen_t addrlen = sizeof(addr); if (getsockname(s, (struct sockaddr *)&addr, &addrlen) < 0) { fprintf(stderr, "error: %s\n", strerror(errno)); return -1; } shutdown(s, SHUT_RDWR); } if (fuchsia_address) { // Make it a valid link-local address by fiddling some bits. addr.sin6_addr.s6_addr[11] = 0xFF; } // Get the string form of the address. char addr_s[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &addr.sin6_addr, addr_s, sizeof(addr_s)); // Get the name of the interface. char ifname[IF_NAMESIZE]; if_indextoname(addr.sin6_scope_id, ifname); fprintf(stdout, "%s%%%s\n", addr_s, ifname); return 0; }