/** Parses IPv4/6 address + port, to fr_ipaddr_t and integer (port) * * @param[out] out Where to write the ip address value. * @param[out] port_out Where to write the port (0 if no port found). * @param[in] value to parse. * @param[in] inlen Length of value, if value is \0 terminated inlen may be -1. * @param[in] resolve If true and value doesn't look like an IP address, try and resolve value * as a hostname. * @param[in] af If the address type is not obvious from the format, and resolve is true, * the DNS record (A or AAAA) we require. Also controls which parser we pass * the address to if we have no idea what it is. * - AF_UNSPEC - Use the server default IP family. * - AF_INET - Treat value as an IPv4 address. * - AF_INET6 - Treat value as in IPv6 address. * @param[in] mask If true, set address bits to zero. * @return * - 0 if ip address was parsed successfully. * - -1 on failure. */ int fr_inet_pton_port(fr_ipaddr_t *out, uint16_t *port_out, char const *value, ssize_t inlen, int af, bool resolve, bool mask) { char const *p = value, *q; char *end; unsigned long port; char buffer[6]; size_t len; *port_out = 0; len = (inlen >= 0) ? (size_t)inlen : strlen(value); if (*p == '[') { if (!(q = memchr(p + 1, ']', len - 1))) { fr_strerror_printf("Missing closing ']' for IPv6 address"); return -1; } /* * inet_pton doesn't like the address being wrapped in [] */ if (fr_inet_pton6(out, p + 1, (q - p) - 1, false, false, mask) < 0) return -1; if (q[1] == ':') { q++; goto do_port; } return 0; } /* * Host, IPv4 or IPv6 with no port */ q = memchr(p, ':', len); if (!q) return fr_inet_pton(out, p, len, af, resolve, mask); /* * IPv4 or host, with port */ if (fr_inet_pton(out, p, (q - p), af, resolve, mask) < 0) return -1; do_port: /* * Valid ports are a maximum of 5 digits, so if the * input length indicates there are more than 5 chars * after the ':' then there's an issue. */ if (len > (size_t) ((q + sizeof(buffer)) - value)) { error: fr_strerror_printf("IP string contains trailing garbage after port delimiter"); return -1; } p = q + 1; /* Move to first digit */ strlcpy(buffer, p, (len - (p - value)) + 1); port = strtoul(buffer, &end, 10); if (*end != '\0') goto error; /* Trailing garbage after integer */ if ((port > UINT16_MAX) || (port == 0)) { fr_strerror_printf("Port %lu outside valid port range 1-" STRINGIFY(UINT16_MAX), port); return -1; } *port_out = port; return 0; }
/** Allocate a new client from a config section * * @param ctx to allocate new clients in. * @param cs to process as a client. * @param in_server Whether the client should belong to a specific virtual server. * @param with_coa If true and coa_server or coa_pool aren't specified automatically, * create a coa home_server section and add it to the client CONF_SECTION. * @return new RADCLIENT struct. */ RADCLIENT *client_afrom_cs(TALLOC_CTX *ctx, CONF_SECTION *cs, bool in_server, bool with_coa) { RADCLIENT *c; char const *name2; name2 = cf_section_name2(cs); if (!name2) { cf_log_err_cs(cs, "Missing client name"); return NULL; } /* * The size is fine.. Let's create the buffer */ c = talloc_zero(ctx, RADCLIENT); c->cs = cs; memset(&cl_ipaddr, 0, sizeof(cl_ipaddr)); if (cf_section_parse(cs, c, client_config) < 0) { cf_log_err_cs(cs, "Error parsing client section"); error: client_free(c); #ifdef WITH_TCP hs_proto = NULL; cl_srcipaddr = NULL; #endif return NULL; } /* * Global clients can set servers to use, per-server clients cannot. */ if (in_server && c->server) { cf_log_err_cs(cs, "Clients inside of an server section cannot point to a server"); goto error; } /* * Newer style client definitions with either ipaddr or ipaddr6 * config items. */ if (cf_pair_find(cs, "ipaddr") || cf_pair_find(cs, "ipv4addr") || cf_pair_find(cs, "ipv6addr")) { char buffer[128]; /* * Sets ipv4/ipv6 address and prefix. */ c->ipaddr = cl_ipaddr; /* * Set the long name to be the result of a reverse lookup on the IP address. */ fr_inet_ntoh(&c->ipaddr, buffer, sizeof(buffer)); c->longname = talloc_typed_strdup(c, buffer); /* * Set the short name to the name2. */ if (!c->shortname) c->shortname = talloc_typed_strdup(c, name2); /* * No "ipaddr" or "ipv6addr", use old-style "client <ipaddr> {" syntax. */ } else { cf_log_err_cs(cs, "No 'ipaddr' or 'ipv4addr' or 'ipv6addr' configuration " "directive found in client %s", name2); goto error; } c->proto = IPPROTO_UDP; if (hs_proto) { if (strcmp(hs_proto, "udp") == 0) { hs_proto = NULL; #ifdef WITH_TCP } else if (strcmp(hs_proto, "tcp") == 0) { hs_proto = NULL; c->proto = IPPROTO_TCP; # ifdef WITH_TLS } else if (strcmp(hs_proto, "tls") == 0) { hs_proto = NULL; c->proto = IPPROTO_TCP; c->tls_required = true; } else if (strcmp(hs_proto, "radsec") == 0) { hs_proto = NULL; c->proto = IPPROTO_TCP; c->tls_required = true; # endif } else if (strcmp(hs_proto, "*") == 0) { hs_proto = NULL; c->proto = IPPROTO_IP; /* fake for dual */ #endif } else { cf_log_err_cs(cs, "Unknown proto \"%s\".", hs_proto); goto error; } } /* * If a src_ipaddr is specified, when we send the return packet * we will use this address instead of the src from the * request. */ if (cl_srcipaddr) { #ifdef WITH_UDPFROMTO switch (c->ipaddr.af) { case AF_INET: if (fr_inet_pton4(&c->src_ipaddr, cl_srcipaddr, -1, true, false, true) < 0) { cf_log_err_cs(cs, "Failed parsing src_ipaddr: %s", fr_strerror()); goto error; } break; case AF_INET6: if (fr_inet_pton6(&c->src_ipaddr, cl_srcipaddr, -1, true, false, true) < 0) { cf_log_err_cs(cs, "Failed parsing src_ipaddr: %s", fr_strerror()); goto error; } break; default: rad_assert(0); } #else WARN("Server not built with udpfromto, ignoring client src_ipaddr"); #endif cl_srcipaddr = NULL; } /* * A response_window of zero is OK, and means that it's * ignored by the rest of the server timers. */ if (timerisset(&c->response_window)) { FR_TIMEVAL_BOUND_CHECK("response_window", &c->response_window, >=, 0, 1000); FR_TIMEVAL_BOUND_CHECK("response_window", &c->response_window, <=, 60, 0); FR_TIMEVAL_BOUND_CHECK("response_window", &c->response_window, <=, main_config.max_request_time, 0); }
/** Simple wrapper to decide whether an IP value is v4 or v6 and call the appropriate parser * * @param[out] out Where to write the ip address value. * @param[in] value to parse. * @param[in] inlen Length of value, if value is \0 terminated inlen may be -1. * @param[in] resolve If true and value doesn't look like an IP address, try and resolve value * as a hostname. * @param[in] af If the address type is not obvious from the format, and resolve is true, * the DNS record (A or AAAA) we require. Also controls which parser we pass * the address to if we have no idea what it is. * - AF_UNSPEC - Use the server default IP family. * - AF_INET - Treat value as an IPv4 address. * - AF_INET6 - Treat value as in IPv6 address. * @param[in] mask If true, set address bits to zero. * @return * - 0 if ip address was parsed successfully. * - -1 on failure. */ int fr_inet_pton(fr_ipaddr_t *out, char const *value, ssize_t inlen, int af, bool resolve, bool mask) { size_t len, i; bool hostname = true; bool ipv4 = true; bool ipv6 = true; len = (inlen >= 0) ? (size_t)inlen : strlen(value); for (i = 0; i < len; i++) { /* * These are valid for IPv4, IPv6, and host names. */ if ((value[i] >= '0') && (value[i] <= '9')) { continue; } /* * These are invalid for IPv4, but OK for IPv6 * and host names. */ if ((value[i] >= 'a') && (value[i] <= 'f')) { ipv4 = false; continue; } /* * These are invalid for IPv4, but OK for IPv6 * and host names. */ if ((value[i] >= 'A') && (value[i] <= 'F')) { ipv4 = false; continue; } /* * This is only valid for IPv6 addresses. */ if (value[i] == ':') { ipv4 = false; hostname = false; continue; } /* * Valid for IPv4 and host names, not for IPv6. */ if (value[i] == '.') { ipv6 = false; continue; } /* * Netmasks are allowed by us, and MUST come at * the end of the address. */ if (value[i] == '/') { break; } /* * Any characters other than what are checked for * above can't be IPv4 or IPv6 addresses. */ ipv4 = false; ipv6 = false; } /* * It's not an IPv4 or IPv6 address. It MUST be a host * name. */ if (!ipv4 && !ipv6) { /* * Not an IPv4 or IPv6 address, and we weren't * asked to do DNS resolution, we can't do it. */ if (!resolve) { fr_strerror_printf("Not IPv4/6 address, and asked not to resolve"); return -1; } /* * It's not a hostname, either, so bail out * early. */ if (!hostname) { fr_strerror_printf("Invalid address"); return -1; } /* * Fall through to resolving the address, using * whatever address family they prefer. If they * don't specify an address family, force IPv4. */ if (af == AF_UNSPEC) af = AF_INET; } /* * The name has a ':' in it. Therefore it must be an * IPv6 address. Error out if the caller specified IPv4. * Otherwise, force IPv6. */ if (ipv6 && !hostname) { if (af == AF_INET) { fr_strerror_printf("Invalid address"); return -1; } af = AF_INET6; } /* * Use whatever the caller specified, OR what we * insinuated above from looking at the name string. */ switch (af) { case AF_UNSPEC: return fr_inet_pton4(out, value, inlen, resolve, true, mask); case AF_INET: return fr_inet_pton4(out, value, inlen, resolve, false, mask); case AF_INET6: return fr_inet_pton6(out, value, inlen, resolve, false, mask); default: break; } /* * No idea what it is... */ fr_strerror_printf("Invalid address family %i", af); return -1; }