/* *TODO: Check for and handle [_res.options & RES_USE_INET6] */ static getdns_return_t extract_addrtuple(struct gaih_addrtuple **result_addrtuple, response_bundle *response, char *intern_buffer, size_t buflen, uint32_t *respstatus) { if(!response) { log_warning("extract_addrtuple():error parsing response."); return GETDNS_RETURN_GENERIC_ERROR; }else if(response->ipv4_count + response->ipv6_count <= 0) { log_info("extract_addrtuple(): No answers: %s.", getdns_get_errorstr_by_id(response->respstatus)); *respstatus = GETDNS_RESPSTATUS_NO_NAME; return GETDNS_RETURN_GOOD; } size_t rec_count = 0, num_answers = 0; size_t idx, min_space, cname_len; num_answers = response->ipv4_count + response->ipv6_count; char *canon_name = response->cname; cname_len = strlen(canon_name) + 2; min_space = cname_len + (sizeof(struct gaih_addrtuple) * num_answers); if( buflen < min_space ) { log_critical("GETDNS: Buffer too small: %zd\n", buflen); return GETDNS_RETURN_MEMORY_ERROR; } struct gaih_addrtuple *gaih_ptr = *result_addrtuple; /*Fill in hostname*/ char *hname; hname = intern_buffer; memcpy(hname, canon_name, cname_len-2); memset(hname + cname_len-1, 0, sizeof(char)); idx = cname_len; if(response->ipv6_count > 0) { char **addr_list = malloc(sizeof(char*)*response->ipv6_count); assert(addr_list); int num = parse_addr_list(response->ipv6, addr_list, response->ipv6_count); for(rec_count = 0; rec_count < num; ++rec_count) { add_addrtuple(addr_list[rec_count], AF_INET6); } free(addr_list); } if(response->ipv4_count > 0) { char **addr_list = malloc(sizeof(char*)*response->ipv4_count); assert(addr_list); int num = parse_addr_list(response->ipv4, addr_list, response->ipv4_count); int addr_idx; for(addr_idx = 0; addr_idx < num; ++addr_idx) { add_addrtuple(addr_list[addr_idx], AF_INET); rec_count++; } free(addr_list); } assert(idx <= min_space); /*Check if we didn't write past the intended space...*/ *respstatus = rec_count > 0 ? GETDNS_RESPSTATUS_GOOD : GETDNS_RESPSTATUS_NO_NAME; return GETDNS_RETURN_GOOD; }
static getdns_return_t parse_addrinfo(char *addr_list_string, const char *cname, const int num_addresses, size_t addrlen, struct addrinfo **result, struct addrinfo *hints) { char **addr_list = malloc(sizeof(char*)*num_addresses); assert(addr_list); size_t answer_idx; struct addrinfo *result_ptr = NULL; getdns_return_t return_code = GETDNS_RETURN_GENERIC_ERROR; if(parse_addr_list(addr_list_string, addr_list, num_addresses) != num_addresses) { return GETDNS_RETURN_GENERIC_ERROR; } for(answer_idx = 0; answer_idx < num_addresses; ++answer_idx) { if( GETDNS_RETURN_GOOD != (return_code = add_addrinfo(&result_ptr, hints, addr_list[answer_idx], addrlen, cname))) { __freeaddrinfo(*result); return return_code; } if(*result == NULL) { *result = result_ptr; } } free(addr_list); return return_code; }
int main (int argc, char *argv[]) { GDBusProxy *proxy; GVariantBuilder builder, ip4builder, ip6builder; GVariant *ip4config, *ip6config; char *tmp; GVariant *val; int i; GError *err = NULL; GPtrArray *dns4_list, *dns6_list; GPtrArray *nbns_list; GPtrArray *dns_domains; struct in_addr temp_addr; int tapdev = -1; char **iter; int shift = 0; gboolean is_restart; gboolean has_ip4_prefix = FALSE; gboolean has_ip4_address = FALSE; gboolean has_ip6_address = FALSE; gchar *bus_name = NM_DBUS_SERVICE_OPENVPN; #if !GLIB_CHECK_VERSION (2, 35, 0) g_type_init (); #endif for (i = 1; i < argc; i++) { if (!strcmp (argv[i], "--")) { i++; break; } if (nm_streq (argv[i], "--debug")) { if (i + 2 >= argc) { g_printerr ("Missing debug arguments (requires <LEVEL> <PREFIX_TOKEN>)\n"); exit (1); } gl.log_level = _nm_utils_ascii_str_to_int64 (argv[++i], 10, 0, LOG_DEBUG, 0); gl.log_prefix_token = argv[++i]; } else if (!strcmp (argv[i], "--tun")) tapdev = 0; else if (!strcmp (argv[i], "--tap")) tapdev = 1; else if (!strcmp (argv[i], "--bus-name")) { if (++i == argc) { g_printerr ("Missing bus name argument\n"); exit (1); } if (!g_dbus_is_name (argv[i])) { g_printerr ("Invalid bus name\n"); exit (1); } bus_name = argv[i]; } else break; } shift = i - 1; if (_LOGD_enabled ()) { GString *args; args = g_string_new (NULL); for (i = 0; i < argc; i++) { if (i > 0) g_string_append_c (args, ' '); if (shift && 1 + shift == i) g_string_append (args, " "); tmp = g_strescape (argv[i], NULL); g_string_append_printf (args, "\"%s\"", tmp); g_free (tmp); } _LOGD ("command line: %s", args->str); g_string_free (args, TRUE); for (iter = environ; iter && *iter; iter++) _LOGD ("environment: %s", *iter); } /* shift the arguments to the right leaving only those provided by openvpn */ argv[shift] = argv[0]; argv += shift; argc -= shift; is_restart = argc >= 7 && !g_strcmp0 (argv[6], "restart"); proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, NULL, bus_name, NM_VPN_DBUS_PLUGIN_PATH, NM_VPN_DBUS_PLUGIN_INTERFACE, NULL, &err); if (!proxy) { _LOGW ("Could not create a D-Bus proxy: %s", err->message); g_error_free (err); exit (1); } g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); g_variant_builder_init (&ip4builder, G_VARIANT_TYPE_VARDICT); g_variant_builder_init (&ip6builder, G_VARIANT_TYPE_VARDICT); /* External world-visible VPN gateway */ val = trusted_remote_to_gvariant (); if (val) g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY, val); else helper_failed (proxy, "VPN Gateway"); /* Internal VPN subnet gateway */ tmp = getenv ("route_vpn_gateway"); val = addr4_to_gvariant (tmp); if (val) g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_INT_GATEWAY, val); else { val = addr6_to_gvariant (tmp); if (val) g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_INT_GATEWAY, val); } /* VPN device */ tmp = getenv ("dev"); val = str_to_gvariant (tmp, FALSE); if (val) g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_CONFIG_TUNDEV, val); else helper_failed (proxy, "Tunnel Device"); if (tapdev == -1) tapdev = strncmp (tmp, "tap", 3) == 0; /* IPv4 address */ tmp = getenv ("ifconfig_local"); if (!tmp && is_restart) tmp = argv[4]; if (tmp && strlen (tmp)) { val = addr4_to_gvariant (tmp); if (val) { has_ip4_address = TRUE; g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS, val); } else helper_failed (proxy, "IP4 Address"); } /* PTP address; for vpnc PTP address == internal IP4 address */ tmp = getenv ("ifconfig_remote"); if (!tmp && is_restart) tmp = argv[5]; val = addr4_to_gvariant (tmp); if (val) { /* Sigh. Openvpn added 'topology' stuff in 2.1 that changes the meaning * of the ifconfig bits without actually telling you what they are * supposed to mean; basically relying on specific 'ifconfig' behavior. */ if (tmp && !strncmp (tmp, "255.", 4)) { guint32 addr; /* probably a netmask, not a PTP address; topology == subnet */ addr = g_variant_get_uint32 (val); g_variant_unref (val); val = g_variant_new_uint32 (nm_utils_ip4_netmask_to_prefix (addr)); g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, val); has_ip4_prefix = TRUE; } else g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_PTP, val); } /* Netmask * * Either TAP or TUN modes can have an arbitrary netmask in newer versions * of openvpn, while in older versions only TAP mode would. So accept a * netmask if passed, otherwise default to /32 for TUN devices since they * are usually point-to-point. */ tmp = getenv ("ifconfig_netmask"); if (tmp && inet_pton (AF_INET, tmp, &temp_addr) > 0) { val = g_variant_new_uint32 (nm_utils_ip4_netmask_to_prefix (temp_addr.s_addr)); g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, val); } else if (!tapdev) { if (has_ip4_address && !has_ip4_prefix) { val = g_variant_new_uint32 (32); g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, val); } } else _LOGW ("No IP4 netmask/prefix (missing or invalid 'ifconfig_netmask')"); val = get_ip4_routes (); if (val) g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_ROUTES, val); else if (is_restart) { g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_PRESERVE_ROUTES, g_variant_new_boolean (TRUE)); } /* IPv6 address */ tmp = getenv ("ifconfig_ipv6_local"); if (tmp && strlen (tmp)) { val = addr6_to_gvariant (tmp); if (val) { g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_ADDRESS, val); has_ip6_address = TRUE; } else helper_failed (proxy, "IP6 Address"); } /* IPv6 remote address */ tmp = getenv ("ifconfig_ipv6_remote"); if (tmp && strlen (tmp)) { val = addr6_to_gvariant (tmp); if (val) g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_PTP, val); else helper_failed (proxy, "IP6 PTP Address"); } /* IPv6 netbits */ tmp = getenv ("ifconfig_ipv6_netbits"); if (tmp && strlen (tmp)) { long int netbits; errno = 0; netbits = strtol (tmp, NULL, 10); if (errno || netbits < 0 || netbits > 128) { _LOGW ("Ignoring invalid prefix '%s'", tmp); } else { val = g_variant_new_uint32 ((guint32) netbits); g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_PREFIX, val); } } val = get_ip6_routes (); if (val) g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_ROUTES, val); else if (is_restart) { g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_PRESERVE_ROUTES, g_variant_new_boolean (TRUE)); } /* DNS and WINS servers */ dns_domains = g_ptr_array_sized_new (3); dns4_list = g_ptr_array_new (); dns6_list = g_ptr_array_new (); nbns_list = g_ptr_array_new (); for (i = 1; i < 256; i++) { char *env_name; env_name = g_strdup_printf ("foreign_option_%d", i); tmp = getenv (env_name); g_free (env_name); if (!tmp || strlen (tmp) < 1) break; if (!g_str_has_prefix (tmp, "dhcp-option ")) continue; tmp += 12; /* strlen ("dhcp-option ") */ if (g_str_has_prefix (tmp, "DNS ")) parse_addr_list (dns4_list, dns6_list, tmp + 4); else if (g_str_has_prefix (tmp, "WINS ")) parse_addr_list (nbns_list, NULL, tmp + 5); else if (g_str_has_prefix (tmp, "DOMAIN ") && is_domain_valid (tmp + 7)) g_ptr_array_add (dns_domains, tmp + 7); } if (dns4_list->len) { val = g_variant_new_array (G_VARIANT_TYPE_UINT32, (GVariant **) dns4_list->pdata, dns4_list->len); g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_DNS, val); } if (has_ip6_address && dns6_list->len) { val = g_variant_new_array (G_VARIANT_TYPE ("ay"), (GVariant **) dns6_list->pdata, dns6_list->len); g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_DNS, val); } if (nbns_list->len) { val = g_variant_new_array (G_VARIANT_TYPE_UINT32, (GVariant **) nbns_list->pdata, nbns_list->len); g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_NBNS, val); } if (dns_domains->len) { val = g_variant_new_strv ((const gchar **) dns_domains->pdata, dns_domains->len); g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_DOMAINS, val); /* Domains apply to both IPv4 and IPv6 configurations */ if (has_ip6_address) { val = g_variant_new_strv ((const gchar **) dns_domains->pdata, dns_domains->len); g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_DOMAINS, val); } } g_ptr_array_unref (dns4_list); g_ptr_array_unref (dns6_list); g_ptr_array_unref (nbns_list); g_ptr_array_unref (dns_domains); /* Tunnel MTU */ tmp = getenv ("tun_mtu"); if (tmp && strlen (tmp)) { long int mtu; errno = 0; mtu = strtol (tmp, NULL, 10); if (errno || mtu < 0 || mtu > 20000) { _LOGW ("Ignoring invalid tunnel MTU '%s'", tmp); } else { val = g_variant_new_uint32 ((guint32) mtu); g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_CONFIG_MTU, val); } } ip4config = g_variant_builder_end (&ip4builder); if (g_variant_n_children (ip4config)) { val = g_variant_new_boolean (TRUE); g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_CONFIG_HAS_IP4, val); } else { g_variant_unref (ip4config); ip4config = NULL; } ip6config = g_variant_builder_end (&ip6builder); if (g_variant_n_children (ip6config)) { val = g_variant_new_boolean (TRUE); g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_CONFIG_HAS_IP6, val); } else { g_variant_unref (ip6config); ip6config = NULL; } if (!ip4config && !ip6config) helper_failed (proxy, "IPv4 or IPv6 configuration"); /* Send the config info to nm-openvpn-service */ send_config (proxy, g_variant_builder_end (&builder), ip4config, ip6config); g_object_unref (proxy); return 0; }
/* *TODO: Check for and handle [_res.options & RES_USE_INET6] */ getdns_return_t extract_hostent(struct hostent *result, response_bundle *response, int af, int reverse, char *intern_buffer, size_t buflen, uint32_t *respstatus) { signal(SIGPIPE, sig_handler); *respstatus = GETDNS_RESPSTATUS_NO_NAME; size_t answer_idx, num_answers = 0, addr_idx = 0; char *addr_string; if(!response) { log_info("extract_addrtuple():error parsing response."); return GETDNS_RETURN_GENERIC_ERROR; } if((af == AF_INET6) || ((af == AF_UNSPEC) && (getdns_options & IFACE_INET6) && (response->ipv6_count > 0))) { num_answers = response->ipv6_count; addr_string = response->ipv6; result->h_length = sizeof(struct in6_addr); af = AF_INET6; }else if(af == AF_INET || af == AF_UNSPEC){ num_answers = response->ipv4_count; addr_string = response->ipv4; result->h_length = sizeof(struct in_addr); af = AF_INET; }else{ log_warning("getdns_gethostinfo: Address family not supported: %d .", af); *respstatus = GETDNS_RESPSTATUS_NO_NAME; return GETDNS_RETURN_WRONG_TYPE_REQUESTED; } if(!reverse && num_answers < 1){ *respstatus = GETDNS_RESPSTATUS_NO_NAME; return GETDNS_RETURN_GENERIC_ERROR; } result->h_addrtype = af; /*Reserve the first section for result->h_addr_list[num_answers]*/ result->h_addr_list = (char**)intern_buffer; intern_buffer += sizeof(char*) * (num_answers + 1); buflen -= sizeof(char*) * (num_answers + 1); if (reverse) { size_t cname_len = strlen(response->cname) + 2; if(buflen <= cname_len) { log_warning("GETDNS: buffer too small.\n"); return GETDNS_RETURN_MEMORY_ERROR; } memcpy(intern_buffer, response->cname, cname_len-2); memset(intern_buffer + cname_len - 1, 0, sizeof(char)); result->h_name = intern_buffer; intern_buffer += cname_len; buflen -= cname_len; result->h_aliases = (char**)intern_buffer; result->h_aliases[0] = NULL; } else { char **addr_list = malloc(sizeof(char*)*num_answers); assert(addr_list); if(parse_addr_list(addr_string, addr_list, num_answers) != num_answers) { *respstatus = GETDNS_RESPSTATUS_NO_NAME; return GETDNS_RETURN_GENERIC_ERROR; } if(buflen <= 0) { log_warning("GETDNS: buffer too small.\n"); return GETDNS_RETURN_MEMORY_ERROR; } for (answer_idx = 0; answer_idx < num_answers; ++answer_idx) { char tmp_name[result->h_length]; memset(tmp_name, 0, result->h_length); inet_pton(af, addr_list[answer_idx], tmp_name); size_t len = sizeof(tmp_name); if(buflen <= len) { log_warning("GETDNS: buffer too small.\n"); return GETDNS_RETURN_MEMORY_ERROR; } memcpy(intern_buffer, tmp_name, len); result->h_addr_list[addr_idx++] = intern_buffer; intern_buffer += len; buflen -= len; } free(addr_list); } *respstatus = response->respstatus; return GETDNS_RETURN_GOOD; }