/* This file is part of GNUnet. (C) 2001, 2002, 2004 Christian Grothoff (and other contributing authors) GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNUnet; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /** * @file applications/tracekit/gnunet-tracekit.c * @brief tool that sends a trace request and prints the received network topology * @author Christian Grothoff */ #include "tracekit.h" #include "platform.h" #define TRACEKIT_VERSION "0.0.3" static Semaphore * doneSem; /** * Parse the options, set the timeout. * @param argc the number of options * @param argv the option list (including keywords) * @return OK on error, SYSERR if we should exit */ static int parseOptions(int argc, char ** argv) { int option_index; int c; FREENONNULL(setConfigurationString("GNUNETD", "LOGFILE", NULL)); while (1) { static struct GNoption long_options[] = { LONG_DEFAULT_OPTIONS, { "depth", 1, 0, 'D' }, { "format", 1, 0, 'F' }, { "priority", 1, 0, 'P' }, { "wait", 1, 0, 'W' }, { 0,0,0,0 } }; option_index = 0; c = GNgetopt_long(argc, argv, "vhdc:L:H:W:D:F:P:", long_options, &option_index); if (c == -1) break; /* No more flags to process */ if (YES == parseDefaultOptions(c, GNoptarg)) continue; switch(c) { case 'D': { unsigned int depth; if (1 != sscanf(GNoptarg, "%ud", &depth)) { LOG(LOG_FAILURE, _("You must pass a number to the '%s' option.\n"), "-D"); return SYSERR; } else { setConfigurationInt("GNUNET-TRACEKIT", "HOPS", depth); } break; } case 'F': { unsigned int format; if (1 != sscanf(GNoptarg, "%ud", &format)) { LOG(LOG_FAILURE, _("You must pass a number to the '%s' option.\n"), "-F"); return SYSERR; } else { setConfigurationInt("GNUNET-TRACEKIT", "FORMAT", format); } break; } case 'h': { static Help help[] = { HELP_CONFIG, { 'D', "depth", "DEPTH", gettext_noop("probe network to the given DEPTH") }, { 'F', "format", "FORMAT", gettext_noop("specify output format; 0 for human readable output, 1 for dot, 2 for vcg") }, HELP_HELP, HELP_LOGLEVEL, { 'P', "priority", "PRIO", gettext_noop("use PRIO for the priority of the trace request") }, HELP_VERSION, { 'W', "wait", "DELAY", gettext_noop("wait DELAY seconds for replies") }, HELP_END, }; formatHelp("gnunet-tracekit [OPTIONS]", _("Trace GNUnet network topology."), help); return SYSERR; } case 'P': { unsigned int prio; if (1 != sscanf(GNoptarg, "%ud", &prio)) { LOG(LOG_FAILURE, _("You must pass a number to the '%s' option.\n"), "-P"); return SYSERR; } else { setConfigurationInt("GNUNET-TRACEKIT", "PRIORITY", prio); } break; } case 'W': { unsigned int wait; if (1 != sscanf(GNoptarg, "%ud", &wait)) { LOG(LOG_FAILURE, _("You must pass a number to the '%s' option.\n"), "-W"); return SYSERR; } else { setConfigurationInt("GNUNET-TRACEKIT", "WAIT", wait); } break; } case 'v': printf("GNUnet v%s, gnunet-tracekit v%s\n", VERSION, TRACEKIT_VERSION); return SYSERR; default: LOG(LOG_FAILURE, _("Use --help to get a list of options.\n")); return SYSERR; } /* end of parsing commandline */ } /* while (1) */ return OK; } static void * receiveThread(GNUNET_TCP_SOCKET * sock) { TRACEKIT_CS_REPLY * buffer; int format; HostIdentity * peersSeen; int psCount; int psSize; HostIdentity * peersResponding; int prCount; int prSize; int i; int j; int match; psCount = 0; psSize = 1; peersSeen = MALLOC(psSize * sizeof(HostIdentity)); prCount = 0; prSize = 1; peersResponding = MALLOC(prSize * sizeof(HostIdentity)); buffer = MALLOC(MAX_BUFFER_SIZE); format = getConfigurationInt("GNUNET-TRACEKIT", "FORMAT"); if (format == 1) printf("digraph G {\n"); if (format == 2) printf("graph: {\n"); while (OK == readFromSocket(sock, (CS_HEADER**)&buffer)) { int count; EncName enc; count = ntohs(buffer->header.size) - sizeof(TRACEKIT_CS_REPLY); if (count < 0) { BREAK(); break; /* faulty reply */ } hash2enc(&buffer->responderId.hashPubKey, &enc); match = NO; for (j=0;jresponderId.hashPubKey, &peersResponding[j].hashPubKey)) match = YES; if (match == NO) { if (prCount == prSize) GROW(peersResponding, prSize, prSize*2); memcpy(&peersResponding[prCount++], &buffer->responderId.hashPubKey, sizeof(HostIdentity)); } count = count / sizeof(HostIdentity); if (ntohs(buffer->header.size) != sizeof(TRACEKIT_CS_REPLY) + count * sizeof(HostIdentity)) { BREAK(); break; } if (count == 0) { switch (format) { case 0: printf(_("'%s' is not connected to any peer.\n"), (char*)&enc); break; case 1: printf(" %.*s;\n", 4, (char*)&enc); break; case 2: /* deferred -- vcg needs all node data in one line */ break; default: printf(_("Format specification invalid. " "Use 0 for user-readable, 1 for dot, 2 for vcg.\n")); break; } } else { EncName other; for (i=0;ipeerList[i].hashPubKey, &peersSeen[j].hashPubKey)) match = YES; if (match == NO) { if (psCount == psSize) GROW(peersSeen, psSize, psSize * 2); memcpy(&peersSeen[psCount++], &((TRACEKIT_CS_REPLY_GENERIC*)buffer)->peerList[i].hashPubKey, sizeof(HostIdentity)); } hash2enc(&((TRACEKIT_CS_REPLY_GENERIC*)buffer)->peerList[i].hashPubKey, &other); switch (format) { case 0: printf(_("'%s' connected to '%s'.\n"), (char*)&enc, (char*)&other); break; case 1: /* dot */ printf(" \"%.*s\" -> \"%.*s\";\n", 4, (char*)&enc, 4, (char*)&other); break; case 2: /* vcg */ printf("\tedge: { sourcename: \"%s\" targetname: \"%s\" }\n", (char*)&enc, (char*)&other); break; default: /* undef */ printf(_("Format specification invalid. " "Use 0 for user-readable, 1 for dot\n")); break; } } } } FREE(buffer); for (i=0;i