diff options
Diffstat (limited to 'src/dhcp/nm-dhcp-dhclient-utils.c')
| -rw-r--r-- | src/dhcp/nm-dhcp-dhclient-utils.c | 331 |
1 files changed, 57 insertions, 274 deletions
diff --git a/src/dhcp/nm-dhcp-dhclient-utils.c b/src/dhcp/nm-dhcp-dhclient-utils.c index 4df90d76..16a76d23 100644 --- a/src/dhcp/nm-dhcp-dhclient-utils.c +++ b/src/dhcp/nm-dhcp-dhclient-utils.c @@ -95,7 +95,6 @@ grab_request_options (GPtrArray *store, const char* line) return end; } - static void add_hostname4 (GString *str, const char *hostname, gboolean use_fqdn) { @@ -125,7 +124,7 @@ add_ip4_config (GString *str, GBytes *client_id, const char *hostname, gboolean * as long as all the characters are printable. */ for (i = 1; (p[0] == 0) && i < l; i++) { - if (!g_ascii_isprint (p[i])) + if (!g_ascii_isprint (p[i]) || p[i] == '\\' || p[i] == '"') break; } @@ -138,8 +137,9 @@ add_ip4_config (GString *str, GBytes *client_id, const char *hostname, gboolean g_string_append_printf (str, "%02x", (guint8) p[i]); } } else { - /* Printable; just add to the line minus the 'type' */ + /* Printable; just add to the line with type 0 */ g_string_append_c (str, '"'); + g_string_append (str, "\\x00"); g_string_append_len (str, p + 1, l - 1); g_string_append_c (str, '"'); } @@ -177,31 +177,60 @@ read_client_id (const char *str) { gs_free char *s = NULL; char *p; + int i = 0, j = 0; nm_assert (!strncmp (str, CLIENTID_TAG, NM_STRLEN (CLIENTID_TAG))); - str += NM_STRLEN (CLIENTID_TAG); + + if (!g_ascii_isspace (*str)) + return NULL; while (g_ascii_isspace (*str)) str++; if (*str == '"') { + /* Parse string literal with escape sequences */ s = g_strdup (str + 1); p = strrchr (s, '"'); if (p) *p = '\0'; else return NULL; - } else - s = g_strdup (str); + if (!s[0]) + return NULL; + + while (s[i]) { + if ( s[i] == '\\' + && s[i + 1] == 'x' + && g_ascii_isxdigit (s[i + 2]) + && g_ascii_isxdigit (s[i + 3])) { + s[j++] = (g_ascii_xdigit_value (s[i + 2]) << 4) + + g_ascii_xdigit_value (s[i + 3]); + i += 4; + continue; + } + if ( s[i] == '\\' + && s[i + 1] >= '0' && s[i + 1] <= '7' + && s[1 + 2] >= '0' && s[i + 2] <= '7' + && s[1 + 3] >= '0' && s[i + 3] <= '7') { + s[j++] = ((s[i + 1] - '0') << 6) + + ((s[i + 2] - '0') << 3) + + ( s[i + 3] - '0'); + i += 4; + continue; + } + s[j++] = s[i++]; + } + return g_bytes_new_take (g_steal_pointer (&s), j); + } + + /* Otherwise, try to read a hexadecimal sequence */ + s = g_strdup (str); g_strchomp (s); if (s[strlen (s) - 1] == ';') s[strlen (s) - 1] = '\0'; - if (!s[0]) - return NULL; - - return nm_dhcp_utils_client_id_string_to_bytes (s); + return nm_utils_hexstr2bin (s); } GBytes * @@ -279,6 +308,7 @@ nm_dhcp_dhclient_create_config (const char *interface, g_return_val_if_fail (!anycast_addr || nm_utils_hwaddr_valid (anycast_addr, ETH_ALEN), NULL); g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6), NULL); + nm_assert (!out_new_client_id || !*out_new_client_id); new_contents = g_string_new (_("# Created by NetworkManager\n")); fqdn_opts = g_ptr_array_sized_new (5); @@ -332,6 +362,8 @@ nm_dhcp_dhclient_create_config (const char *interface, continue; /* Otherwise capture and return the existing client id */ + if (out_new_client_id) + g_clear_pointer (out_new_client_id, g_bytes_unref); NM_SET_OUT (out_new_client_id, read_client_id (p)); } @@ -444,14 +476,20 @@ nm_dhcp_dhclient_create_config (const char *interface, /* Roughly follow what dhclient's quotify_buf() and pretty_escape() functions do */ char * -nm_dhcp_dhclient_escape_duid (const GByteArray *duid) +nm_dhcp_dhclient_escape_duid (GBytes *duid) { char *escaped; - const guint8 *s = duid->data; + const guint8 *s, *s0; + gsize len; char *d; - d = escaped = g_malloc0 ((duid->len * 4) + 1); - while (s < (duid->data + duid->len)) { + g_return_val_if_fail (duid, NULL); + + s0 = g_bytes_get_data (duid, &len); + s = s0; + + d = escaped = g_malloc ((len * 4) + 1); + while (s < (s0 + len)) { if (!g_ascii_isprint (*s)) { *d++ = '\\'; *d++ = '0' + ((*s >> 6) & 0x7); @@ -465,6 +503,7 @@ nm_dhcp_dhclient_escape_duid (const GByteArray *duid) } else *d++ = *s++; } + *d++ = '\0'; return escaped; } @@ -476,7 +515,7 @@ isoctal (const guint8 *p) && p[2] >= '0' && p[2] <= '7'); } -GByteArray * +GBytes * nm_dhcp_dhclient_unescape_duid (const char *duid) { GByteArray *unescaped; @@ -507,7 +546,7 @@ nm_dhcp_dhclient_unescape_duid (const char *duid) g_byte_array_append (unescaped, &p[i], 1); } - return unescaped; + return g_byte_array_free_to_bytes (unescaped); error: g_byte_array_free (unescaped, TRUE); @@ -516,10 +555,10 @@ error: #define DUID_PREFIX "default-duid \"" -GByteArray * +GBytes * nm_dhcp_dhclient_read_duid (const char *leasefile, GError **error) { - GByteArray *duid = NULL; + GBytes *duid = NULL; char *contents; char **line, **split, *p, *e; @@ -603,259 +642,3 @@ nm_dhcp_dhclient_save_duid (const char *leasefile, g_string_free (s, TRUE); return success; } - -static void -add_lease_option (GHashTable *hash, char *line) -{ - char *spc; - size_t len; - - /* Find the space after "option" */ - spc = strchr (line, ' '); - if (!spc) - return; - - /* Find the option tag's data, which is after the second space */ - if (g_str_has_prefix (line, "option ")) { - while (g_ascii_isspace (*spc)) - spc++; - spc = strchr (spc + 1, ' '); - if (!spc) - return; - } - - /* Split the line at the space */ - *spc = '\0'; - spc++; - - /* Kill the ';' at the end of the line, if any */ - len = strlen (spc); - if (*(spc + len - 1) == ';') - *(spc + len - 1) = '\0'; - - /* Strip leading quote */ - while (g_ascii_isspace (*spc)) - spc++; - if (*spc == '"') - spc++; - - /* Strip trailing quote */ - len = strlen (spc); - if (len > 0 && spc[len - 1] == '"') - spc[len - 1] = '\0'; - - if (spc[0]) - g_hash_table_insert (hash, g_strdup (line), g_strdup (spc)); -} - -#define LEASE_INVALID G_MININT64 -static GTimeSpan -lease_validity_span (const char *str_expire, GDateTime *now) -{ - GDateTime *expire = NULL; - struct tm expire_tm; - GTimeSpan span; - - g_return_val_if_fail (now != NULL, LEASE_INVALID); - g_return_val_if_fail (str_expire != NULL, LEASE_INVALID); - - /* Skip initial number (day of week?) */ - if (!isdigit (*str_expire++)) - return LEASE_INVALID; - if (!isspace (*str_expire++)) - return LEASE_INVALID; - /* Read lease expiration (in UTC) */ - if (!strptime (str_expire, "%t%Y/%m/%d %H:%M:%S", &expire_tm)) - return LEASE_INVALID; - - expire = g_date_time_new_utc (expire_tm.tm_year + 1900, - expire_tm.tm_mon + 1, - expire_tm.tm_mday, - expire_tm.tm_hour, - expire_tm.tm_min, - expire_tm.tm_sec); - if (!expire) - return LEASE_INVALID; - - span = g_date_time_difference (expire, now); - g_date_time_unref (expire); - - /* GDateTime only supports a range of less then 10000 years, so span can - * not overflow or be equal to LEASE_INVALID */ - return span; -} - -/** - * nm_dhcp_dhclient_read_lease_ip_configs: - * @multi_idx: the multi index instance for the ip config object - * @addr_family: whether to read IPv4 or IPv6 leases - * @iface: the interface name to match leases with - * @ifindex: interface index of @iface - * @route_table: the route table for the default route. - * @route_metric: the route metric for the default route. - * @contents: the contents of a dhclient leasefile - * @now: the current UTC date/time; pass %NULL to automatically use current - * UTC time. Testcases may need a different value for 'now' - * - * Reads dhclient leases from @contents and parses them into either - * #NMIP4Config or #NMIP6Config objects depending on the value of @addr_family. - * - * Returns: a #GSList of #NMIP4Config objects (if @addr_family is %AF_INET) or a list of - * #NMIP6Config objects (if @addr_family is %AF_INET6) containing the lease data. - */ -GSList * -nm_dhcp_dhclient_read_lease_ip_configs (NMDedupMultiIndex *multi_idx, - int addr_family, - const char *iface, - int ifindex, - guint32 route_table, - guint32 route_metric, - const char *contents, - GDateTime *now) -{ - GSList *parsed = NULL, *iter, *leases = NULL; - char **line, **split = NULL; - GHashTable *hash = NULL; - gint32 now_monotonic_ts; - - g_return_val_if_fail (contents != NULL, NULL); - nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); - - split = g_strsplit_set (contents, "\n\r", -1); - if (!split) - return NULL; - - for (line = split; line && *line; line++) { - *line = g_strstrip (*line); - - if (*line[0] == '#') { - /* Comment */ - } else if (!strcmp (*line, "}")) { - /* Lease ends */ - parsed = g_slist_append (parsed, hash); - hash = NULL; - } else if (!strcmp (*line, "lease {")) { - /* Beginning of a new lease */ - if (hash) { - /* Ignore malformed lease that doesn't end before new one starts */ - g_hash_table_destroy (hash); - } - - hash = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free); - } else if (hash && strlen (*line)) - add_lease_option (hash, *line); - } - g_strfreev (split); - - /* Check if the last lease in the file was properly ended */ - if (hash) { - /* Ignore malformed lease that doesn't end before new one starts */ - g_hash_table_destroy (hash); - hash = NULL; - } - - if (now) - g_date_time_ref (now); - else - now = g_date_time_new_now_utc (); - now_monotonic_ts = nm_utils_get_monotonic_timestamp_s (); - - for (iter = parsed; iter; iter = g_slist_next (iter)) { - NMIP4Config *ip4; - NMPlatformIP4Address address; - const char *value; - GTimeSpan expiry; - guint32 tmp, gw = 0; - - hash = iter->data; - - /* Make sure this lease is for the interface we want */ - value = g_hash_table_lookup (hash, "interface"); - if (!value || strcmp (value, iface)) - continue; - - value = g_hash_table_lookup (hash, "expire"); - if (!value) - continue; - expiry = lease_validity_span (value, now); - if (expiry == LEASE_INVALID) - continue; - - /* scale expiry to seconds (and CLAMP into the range of guint32) */ - expiry = CLAMP (expiry / G_TIME_SPAN_SECOND, 0, NM_PLATFORM_LIFETIME_PERMANENT-1); - if (expiry <= 0) { - /* the address is already expired. Don't even add it. */ - continue; - } - - memset (&address, 0, sizeof (address)); - - /* IP4 address */ - value = g_hash_table_lookup (hash, "fixed-address"); - if (!value) - continue; - if (!inet_pton (AF_INET, value, &address.address)) - continue; - address.peer_address = address.address; - - /* Gateway */ - value = g_hash_table_lookup (hash, "option routers"); - if (!value) - continue; - if (!inet_pton (AF_INET, value, &gw)) - continue; - - /* Netmask */ - value = g_hash_table_lookup (hash, "option subnet-mask"); - if (value && inet_pton (AF_INET, value, &tmp)) - address.plen = nm_utils_ip4_netmask_to_prefix (tmp); - - /* Get default netmask for the IP according to appropriate class. */ - if (!address.plen) - address.plen = _nm_utils_ip4_get_default_prefix (address.address); - - address.timestamp = now_monotonic_ts; - address.lifetime = address.preferred = expiry; - address.addr_source = NM_IP_CONFIG_SOURCE_DHCP; - - ip4 = nm_ip4_config_new (multi_idx, ifindex); - nm_ip4_config_add_address (ip4, &address); - - { - const NMPlatformIP4Route r = { - .rt_source = NM_IP_CONFIG_SOURCE_DHCP, - .gateway = gw, - .table_coerced = nm_platform_route_table_coerce (route_table), - .metric = route_metric, - }; - - nm_ip4_config_add_route (ip4, &r, NULL); - } - - value = g_hash_table_lookup (hash, "option domain-name-servers"); - if (value) { - char **dns, **dns_iter; - - dns = g_strsplit_set (value, ",", -1); - for (dns_iter = dns; dns_iter && *dns_iter; dns_iter++) { - if (inet_pton (AF_INET, *dns_iter, &tmp)) - nm_ip4_config_add_nameserver (ip4, tmp); - } - if (dns) - g_strfreev (dns); - } - - value = g_hash_table_lookup (hash, "option domain-name"); - if (value && value[0]) - nm_ip4_config_add_domain (ip4, value); - - /* FIXME: static routes */ - - leases = g_slist_append (leases, ip4); - } - - g_date_time_unref (now); - g_slist_free_full (parsed, (GDestroyNotify) g_hash_table_destroy); - return leases; -} - |