/** * Initializations. */ void G_COLD uhc_init(void) { uint i; g_return_if_fail(NULL == uhc_list); uhc_list = hash_list_new(uhc_hash, uhc_equal); for (i = 0; i < N_ITEMS(boot_hosts); i++) { const char *host, *ep, *uhc; uint16 port; uhc = boot_hosts[i].uhc; /* Some consistency checks */ uhc_get_host_port(uhc, &host, &port); g_assert(NULL != host); g_assert(0 != port); ep = is_strprefix(uhc, host); g_assert(NULL != ep); g_assert(':' == ep[0]); uhc_list_append(uhc); } hash_list_shuffle(uhc_list); }
static struct magnet_source * magnet_parse_push_source(const char *uri, const char **error_str) { struct magnet_source *ms; const char *p, *endptr; struct guid guid; clear_error_str(&error_str); g_return_val_if_fail(uri, NULL); p = is_strprefix(uri, "push://"); g_return_val_if_fail(p, NULL); endptr = strchr(p, ':'); /* First push-proxy host */ if (NULL == endptr || GUID_HEX_SIZE != (endptr - p)) endptr = strchr(p, '/'); /* No push-proxy host */ if ( NULL == endptr || GUID_HEX_SIZE != (endptr - p) || !hex_to_guid(p, &guid) ) { *error_str = "Bad GUID in push source"; return NULL; } ms = magnet_parse_proxy_location(endptr, error_str); if (ms) { ms->guid = atom_guid_get(&guid); } return ms; }
static struct magnet_source * magnet_parse_path(const char *path, const char **error_str) { static const struct magnet_source zero_ms; struct magnet_source ms; const char *p, *endptr; clear_error_str(&error_str); g_return_val_if_fail(path, NULL); ms = zero_ms; p = path; if ('/' != *p) { *error_str = "Expected path starting with '/'"; /* Skip this parameter */ return NULL; } g_assert(*p == '/'); endptr = is_strprefix(p, "/uri-res/N2R?"); if (endptr) { struct sha1 sha1; p = endptr; if (!urn_get_sha1(p, &sha1)) { *error_str = "Bad SHA1 in MAGNET URI"; return NULL; } ms.sha1 = atom_sha1_get(&sha1); } else { ms.path = atom_str_get(p); } return wcopy(&ms, sizeof ms); }
/** * Default implementation to get the default gateway by parsing the * output of the "netstat -rn" command. * * @param addrp where gateway address is to be written * * @return 0 on success, -1 on failure with errno set. */ static G_GNUC_COLD int parse_netstat(host_addr_t *addrp) #ifdef HAS_POPEN { FILE *f = NULL; char tmp[80]; uint32 gate = 0; /* * This implementation should be a safe default on UNIX platforms, but * it is inefficient and as such can only constitute a fallback. */ if (-1 != access("/bin/netstat", X_OK)) { f = popen("/bin/netstat -rn", "r"); } else if (-1 != access("/usr/bin/netstat", X_OK)) { f = popen("/usr/bin/netstat -rn", "r"); } if (NULL == f) { errno = ENOENT; /* netstat not found */ return -1; } /* * Typical netstat -rn output: * * Destination Gateway Flags ..... * 0.0.0.0 192.168.0.200 UG * default 192.168.0.200 UG * * Some systems like linux display "0.0.0.0", but traditional UNIX * output is "default" for the default route. */ while (fgets(tmp, sizeof tmp, f)) { char *p; uint32 ip; p = is_strprefix(tmp, "default"); if (NULL == p) p = is_strprefix(tmp, "0.0.0.0"); if (NULL == p || !is_ascii_space(*p)) continue; ip = string_to_ip(p); if (ip != 0) { gate = ip; break; } } pclose(f); if (0 == gate) { errno = ENETUNREACH; return -1; } *addrp = host_addr_get_ipv4(gate); return 0; }
/** * Loads the whitelist into memory. */ static void G_COLD whitelist_retrieve(void) { char line[1024]; FILE *f; filestat_t st; unsigned linenum = 0; file_path_t fp[1]; whitelist_generation++; file_path_set(fp, settings_config_dir(), whitelist_file); f = file_config_open_read_norename("Host Whitelist", fp, N_ITEMS(fp)); if (!f) return; if (fstat(fileno(f), &st)) { g_warning("%s(): fstat() failed: %m", G_STRFUNC); fclose(f); return; } while (fgets(line, sizeof line, f)) { pslist_t *sl_addr, *sl; const char *endptr, *start; host_addr_t addr; uint16 port; uint8 bits; bool item_ok; bool use_tls; char *hname; linenum++; if (!file_line_chomp_tail(line, sizeof line, NULL)) { g_warning("%s(): line %u too long, aborting", G_STRFUNC, linenum); break; } if (file_line_is_skipable(line)) continue; sl_addr = NULL; addr = zero_host_addr; endptr = NULL; hname = NULL; endptr = is_strprefix(line, "tls:"); if (endptr) { use_tls = TRUE; start = endptr; } else { use_tls = FALSE; start = line; } port = 0; if (string_to_host_addr_port(start, &endptr, &addr, &port)) { sl_addr = name_to_host_addr(host_addr_to_string(addr), settings_dns_net()); } else if (string_to_host_or_addr(start, &endptr, &addr)) { uchar c = *endptr; switch (c) { case '\0': case ':': case '/': break; default: if (!is_ascii_space(c)) endptr = NULL; } if (!endptr) { g_warning("%s(): line %d: " "expected a hostname or IP address \"%s\"", G_STRFUNC, linenum, line); continue; } /* Terminate the string for name_to_host_addr() */ hname = h_strndup(start, endptr - start); } else { g_warning("%s(): line %d: expected hostname or IP address \"%s\"", G_STRFUNC, linenum, line); continue; } g_assert(sl_addr != NULL || hname != NULL); g_assert(NULL != endptr); bits = 0; item_ok = TRUE; /* * When an explicit address is given (no hostname) and with no * port, one can suffix the address with bits to indicate a CIDR * range of whitelisted addresses. */ if (0 == port) { /* Ignore trailing items separated by a space */ while ('\0' != *endptr && !is_ascii_space(*endptr)) { uchar c = *endptr++; if (':' == c) { int error; uint32 v; if (0 != port) { g_warning("%s(): line %d: multiple colons after host", G_STRFUNC, linenum); item_ok = FALSE; break; } v = parse_uint32(endptr, &endptr, 10, &error); port = (error || v > 0xffff) ? 0 : v; if (0 == port) { g_warning("%s(): line %d: " "invalid port value after host", G_STRFUNC, linenum); item_ok = FALSE; break; } } else if ('/' == c) { const char *ep; uint32 mask; if (0 != bits) { g_warning("%s(): line %d: " "multiple slashes after host", G_STRFUNC, linenum); item_ok = FALSE; break; } if (string_to_ip_strict(endptr, &mask, &ep)) { if (!host_addr_is_ipv4(addr)) { g_warning("%s(): line %d: " "IPv4 netmask after non-IPv4 address", G_STRFUNC, linenum); item_ok = FALSE; break; } endptr = ep; if (0 == (bits = netmask_to_cidr(mask))) { g_warning("%s(): line %d: " "IPv4 netmask after non-IPv4 address", G_STRFUNC, linenum); item_ok = FALSE; break; } } else { int error; uint32 v; v = parse_uint32(endptr, &endptr, 10, &error); if ( error || 0 == v || (v > 32 && host_addr_is_ipv4(addr)) || (v > 128 && host_addr_is_ipv6(addr)) ) { g_warning("%s(): line %d: " "invalid numeric netmask after host", G_STRFUNC, linenum); item_ok = FALSE; break; } bits = v; } } else { g_warning("%s(): line %d: " "unexpected character after host", G_STRFUNC, linenum); item_ok = FALSE; break; } } } if (item_ok) { struct whitelist *item; if (hname) { item = whitelist_hostname_create(use_tls, hname, port); whitelist_dns_resolve(item, FALSE); } else { PSLIST_FOREACH(sl_addr, sl) { host_addr_t *aptr = sl->data; g_assert(aptr != NULL); item = whitelist_addr_create(use_tls, *aptr, port, bits); whitelist_add(item); } } } else {
/** * Called from gwc_parse_dispatch_lines() for each complete line of output. * * @return FALSE to stop processing of any remaining data. */ static bool gwc_host_line(struct gwc_parse_context *ctx, const char *buf, size_t len) { int c; if (GNET_PROPERTY(bootstrap_debug) > 3) g_message("BOOT GWC host line (%lu bytes): %s", (ulong) len, buf); if (is_strprefix(buf, "ERROR")) { g_warning("GWC cache \"%s\" returned %s", http_async_url(ctx->handle), buf); http_async_cancel(ctx->handle); return FALSE; } if (len <= 2) return TRUE; /* Skip this line silently */ /* * A line starting with "H|" is a host, with "U|" a GWC URL. * Letters are case-insensitive. */ if (buf[1] != '|') goto malformed; c = ascii_toupper(buf[0]); if ('H' == c) { host_addr_t addr; uint16 port; if (string_to_host_addr_port(&buf[2], NULL, &addr, &port)) { ctx->processed++; hcache_add_caught(HOST_G2HUB, addr, port, "GWC"); if (GNET_PROPERTY(bootstrap_debug) > 1) { g_message("BOOT (G2) collected %s from GWC %s", host_addr_port_to_string(addr, port), http_async_url(ctx->handle)); } } return TRUE; } else if ('U' == c) { char *end = strchr(&buf[2], '|'); char *url; if (NULL == end) goto malformed; ctx->processed++; url = h_strndup(&buf[2], ptr_diff(end, &buf[2])); gwc_add(url); hfree(url); return TRUE; } else if ('I' == c) { return TRUE; /* Ignore information line */ } /* * If we come here, we did not recognize the line properly. */ if (GNET_PROPERTY(bootstrap_debug) > 2) { g_warning("GWC ignoring unknown line \"%s\" from %s", buf, http_async_url(ctx->handle)); } return TRUE; malformed: if (GNET_PROPERTY(bootstrap_debug)) { g_warning("GWC ignoring malformed line \"%s\" from %s", buf, http_async_url(ctx->handle)); } return TRUE; }
/** * Parse gtk-gnutella's version number in User-Agent/Server string `str' * and extract relevant information into `ver'. * * @param str the version string to be parsed * @param ver the structure filled with parsed information * @param end filled with a pointer to the first unparsed character * * @returns TRUE if we parsed a gtk-gnutella version correctly, FALSE if we * were not facing a gtk-gnutella version, or if we did not recognize it. */ static bool version_parse(const char *str, version_t *ver, const char **end) { const char *v; int error; /* * Modern version numbers are formatted like this: * * gtk-gnutella/0.85 (04/04/2002; X11; FreeBSD 4.6-STABLE i386) * gtk-gnutella/0.90u (24/06/2002; X11; Linux 2.4.18-pre7 i686) * gtk-gnutella/0.90b (24/06/2002; X11; Linux 2.4.18-2emi i686) * gtk-gnutella/0.90b2 (24/06/2002; X11; Linux 2.4.18-2emi i686) * * The letter after the version number is either 'u' for unstable, 'a' * for alpha, 'b' for beta, or nothing for a stable release. It can be * followed by digits when present. * * In prevision for future possible extensions, we also parse * * gtk-gnutella/0.90.1b2 (24/06/2002; X11; Linux 2.4.18-2emi i686) * * where the third number is the "patchlevel". * * Starting 2006-08-26, the user-agent string includes the SVN revision * at the time of the build. To be compatible with the servents out * there, the version is included as an optional tag following the date. * * gtk-gnutella/0.85 (04/04/2002; r3404; X11; FreeBSD 4.6-STABLE i386) * * However, we also start parsing the new format that we shall use when * all current GTKG out there have expired: * * gtk-gnutella/0.85-3404 (04/04/2002; X11; FreeBSD 4.6-STABLE i386) * gtk-gnutella/0.90b2-3404 (04/04/2002; X11; FreeBSD 4.6-STABLE i386) * * where the '-3404' introduces the build number. * * Since switching to git (2011-09-11), the SVN build is no longer present * but rather the output of "git describe" is used to show any difference * with the latest release tag. * * A release would be described as: * * gtk-gnutella/0.97.1 (2011-09-11; GTK1; Linux i686) * * but any change on top of that would be seen as: * * gtk-gnutella/0.97.1-24-g844b7 (2011-09-11; GTK1; Linux i686) * * to indicate that 24 commits were made after the release, and the commit * ID is 844b7... (only enough hexadecimal digits are output to make the * string unique at the time it was generated. * * If furthermore a build is done from a dirty git repository, the string * "-dirty" is appended after the commit ID: * * gtk-gnutella/0.97.1-24-g844b7-dirty (2011-09-11; GTK1; Linux i686) */ if (NULL == (v = is_strprefix(str, GTA_PRODUCT_NAME "/"))) return FALSE; /* * Parse "major.minor", followed by optional ".patchlevel". */ ver->major = parse_uint32(v, &v, 10, &error); if (error) return FALSE; if (*v++ != '.') return FALSE; ver->minor = parse_uint32(v, &v, 10, &error); if (error) return FALSE; if ('.' == *v) { v++; ver->patchlevel = parse_uint32(v, &v, 10, &error); if (error) return FALSE; } else ver->patchlevel = 0; /* * Parse optional tag letter "x" followed by optional "taglevel". */ if (is_ascii_alpha(*v)) { ver->tag = *v++; if (is_ascii_digit(*v)) { ver->taglevel = parse_uint32(v, &v, 10, &error); if (error) return FALSE; } else ver->taglevel = 0; } else { ver->tag = '\0'; ver->taglevel = 0; } /* * Parse optional "-build" (legacy SVN) or "-change-bxxxxxxxx" (git * version scheme) to be able to sort identical versions with distinct * changes applied to them. */ if ('-' == *v) { v++; if (!is_strprefix(v, "dirty")) { ver->build = parse_uint32(v, &v, 10, &error); if (error) return FALSE; } } else ver->build = 0; if (end != NULL) *end = v; if (GNET_PROPERTY(version_debug) > 1) version_dump(str, ver, "#"); return TRUE; }
/** * Parse extended build information (git's commit string + dirtyness) * from a version string, starting at the first byte after the build number. * * A typical extended build information would look like: * * -g5c02b-dirty * * The hexadecimal string after "-g" points to the leading git commit ID. * The "-dirty" indication is present when the binary was built with * uncommitted changes. * * @returns TRUE if we parsed the extended build information properly, FALSE * if we were not facing a proper extension. */ static bool version_ext_parse(const char *str, version_ext_t *vext) { const char *v; const char *e; size_t commit_len; char commit[SHA1_BASE16_SIZE + 1]; v = is_strprefix(str, "-g"); if (NULL == v) { /* * There may not be any git tag, but there may be a dirty indication. */ e = is_strprefix(str, "dirty"); if (e != NULL) { vext->dirty = TRUE; return TRUE; } else if (' ' == *str || '\0' == *str) { return TRUE; } return FALSE; } /* * Compute the length of the hexadecimal string, up to the next '-' or * the next ' ' or the end of the string. */ e = strchr(v, ' '); if (e != NULL) { const char *d; d = strchr(v, '-'); if (NULL == d || d > e) { commit_len = e - v; /* '-' before ' ' or no '-' at all */ } else { commit_len = d - v; /* '-' after ' ' */ e = d; } } else { e = strchr(v, '-'); if (e != NULL) { commit_len = e - v; } else { commit_len = strlen(v); } } if (commit_len > SHA1_BASE16_SIZE) return FALSE; vext->commit_len = commit_len; memset(commit, '0', sizeof commit - 1); commit[SHA1_BASE16_SIZE] = '\0'; memcpy(commit, v, commit_len); ZERO(&vext->commit); (void) base16_decode(vext->commit.data, sizeof vext->commit, commit, SHA1_BASE16_SIZE); if (e != NULL && is_strprefix(e, "-dirty")) { vext->dirty = TRUE; } else { vext->dirty = FALSE; } return TRUE; }