#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rxpc/session_libevent.h" #include "rxpc/session.h" #include "rxpc/stream.h" #define DEVICE_IPADDR "fe80::aede:48ff:fe33:4455" #define DEVICE_INTERFACE "t2_ncm" // this assumes you have a udev rule #define DISCOVERY_PORT 59602 static struct event_base *global_evbase = NULL; // TODO: is there a better way to do this int print_service_port(xpc_object_t o, const char *serviceName) { xpc_object_t services = xpc_dictionary_get_value(o, "Services"); if (!services) return -1; xpc_object_t service = xpc_dictionary_get_value(services,serviceName); if (!service) return -1; xpc_object_t port = xpc_dictionary_get_value(service, "Port"); if (!port) return -1; const char *portStr = xpc_string_get_string_ptr(port); if (!portStr) return -1; printf("%s\t%s\n", serviceName, portStr); return 0; } xpc_object_t servicesObj = NULL; bool verbose = false; void rxpc_session_message_print(struct rxpc_stream *stream, struct rxpc_msg_header *header, const void *data) { const char *msg_type; if (verbose) printf("rxpc: got message { type = %u, flags = %u, msg_id = %" PRIu64 " }\n", header->type, header->flags, header->msg_id); if (header->length > 0) { xpc_object_t o = xpc_deserialize(data, header->length); xpc_object_t message_type = xpc_dictionary_get_value(o, "MessageType"); if (message_type && !strncmp( xpc_string_get_string_ptr(message_type), "Handshake", 10)) { servicesObj = o; event_base_loopexit(global_evbase,0); // don't free o here, main() will do that later. } else { xpc_free(o); } } } static void reply_hello(struct rxpc_stream *stream){ rxpc_stream_send(stream, RXPC_TYPE_HELLO, RXPC_FLAG_REPLY_CHANNEL, 1, NULL); } static void send_hello(struct rxpc_stream *stream){ struct rxpc_stream_callbacks cbs = {0}; xpc_object_t dict; dict = xpc_dictionary_create(NULL, NULL, 0); // optional: xpc_dictionary_set_int64("ServiceVersion", value); rxpc_stream_send(stream, RXPC_TYPE_HELLO, 0, 0, dict); xpc_free(dict); cbs.opened = reply_hello; stream->session->stream_reply = rxpc_stream_open(stream->session, &cbs); rxpc_session_send_pending(stream->session); } int main(int argc, char **argv) { struct event_base *evbase; struct evdns_base *evdns; struct bufferevent *socket; struct rxpc_libevent_session *session; struct rxpc_stream_callbacks cbs; int opt, option_index; int port = DISCOVERY_PORT; char address[NI_MAXHOST] = DEVICE_IPADDR; char interface[NI_MAXHOST] = DEVICE_INTERFACE; char full_address[NI_MAXHOST]; size_t addr; char *serviceName = NULL; bool dump = false; bool list = false; /* We make sure the network interface is specified, so that * we actually connect to the t2. A udev rule renaming the * T2's USB CDC NCM interface to "t2_ncm" is expected by default. */ while (1) { // TODO: also support short options static struct option long_options[] = { {"port", required_argument, 0, 'p'}, {"address", required_argument, 0, 'a'}, {"interface", required_argument, 0, 'i'}, {"help", no_argument, 0, 'h'}, {"dump", no_argument, 0, 'd'}, {"list", no_argument, 0, 'l'}, {"service", required_argument, 0, 's'}, {"verbose", no_argument, 0, 'v'}, {0, 0, 0, 0} }; option_index = 0; opt = getopt_long(argc, argv, "p:a:i:h:d:s:l:v:", long_options, &option_index); if (opt == -1) break; switch (opt) { case 'p': port = strtol(optarg, NULL, 0); break; case 'a': strncpy(address, optarg, sizeof(address-8)); break; case 'i': strncpy(interface, optarg, sizeof(interface)); break; case 'd': dump = true; break; case 'v': verbose = true; break; case 's': serviceName = malloc(strlen(optarg)); strcpy(serviceName, optarg); break; case 'l': list = true; break; case 'h': case '?': printf("usage: %s [--verbose] [--port=remote_port] [--address=ip_address]" " [--interface=net_interface_name]\n", argv[0]); printf("--help Print this help\n"); printf("--dump Print the raw xpc data in JSONish format\n"); printf("--list Print the name and port of each service\n"); printf("--service [name] Print the name and port of a specific service\n"); break; default: abort(); } } strcat(address, "%"); strncat(address, interface, sizeof(address)-strlen(address)); if (verbose) printf("Connecting to: %s:%d\n", address, port); evbase = event_base_new(); if (!evbase) { fprintf(stderr, "event_base_new failed\n"); return -ENOMEM; } evdns = evdns_base_new(evbase, 1); cbs.opened = send_hello; cbs.message = rxpc_session_message_print; session = rxpc_libevent_session_create(evbase, &cbs); if (bufferevent_socket_connect_hostname(session->bev, evdns, AF_UNSPEC, address, port)) { fprintf(stderr, "bufferevent_socket_connect failed\n"); return -ENOMEM;; } global_evbase = evbase; event_base_loop(evbase, 0); evdns_base_free(evdns, 1); event_base_free(evbase); int ret = 0; if (servicesObj) { if (dump) { xpc_debug_print_helper(servicesObj, stdout); } else if (list) { xpc_object_t services = xpc_dictionary_get_value(servicesObj, "Services"); const char *key; int i = 0; key = xpc_dictionary_get_key_by_index(services, i); while (key) { ret = print_service_port(servicesObj, key); if (ret) break; i++; key = xpc_dictionary_get_key_by_index(services, i); } } else if (serviceName) { ret = print_service_port(servicesObj, serviceName); } xpc_free(servicesObj); if (serviceName) { free(serviceName); } } return ret; }