;endpoint=mytrunk
;match=198.51.100.1
;match=198.51.100.2
+;match=192.168.10.0:5061/24
;=============ENDPOINT CONFIGURED AS A TRUNK, INBOUND AUTH AND REGISTRATION===
--- /dev/null
+Subject: res_pjsip_endpoint_identifier_ip
+
+In 'type = identify' sections, the addresses specified for the 'match'
+clause can now include a port number. For IP addresses, the port is
+provided by including a colon after the address, followed by the
+desired port number. If supplied, the netmask should follow the port
+number. To specify a port for IPv6 addresses, the address itself must
+be enclosed in brackets to be parsed correctly.
*/
struct ast_ha *ast_append_ha(const char *sense, const char *stuff, struct ast_ha *path, int *error);
+/*!
+ * \brief Add a new rule with optional port to a list of HAs
+ * \since 13.31.0, 16.8.0, 17.2.0
+ *
+ * \details
+ * This adds the new host access rule to the end of the list
+ * whose head is specified by the path parameter. Rules are
+ * evaluated in a way such that if multiple rules apply to
+ * a single IP address/subnet mask, then the rule latest
+ * in the list will be used.
+ *
+ * \param sense Either "permit" or "deny" (Actually any 'p' word will result
+ * in permission, and any other word will result in denial)
+ * \param stuff The IP address and subnet mask, separated with a '/'. The subnet
+ * mask can either be in dotted-decimal format or in CIDR notation (i.e. 0-32). A
+ * port can be provided by placing it after the IP address, separated with a ':'.
+ * \param path The head of the HA list to which we wish to append our new rule. If
+ * NULL is passed, then the new rule will become the head of the list
+ * \param[out] error The integer error points to will be set non-zero if an error occurs
+ * \return The head of the HA list
+ */
+struct ast_ha *ast_append_ha_with_port(const char *sense, const char *stuff, struct ast_ha *path, int *error);
+
/*!
* \brief Convert HAs to a comma separated string value
* \param ha the starting ha head
ha->sense);
}
-struct ast_ha *ast_append_ha(const char *sense, const char *stuff, struct ast_ha *path, int *error)
+static struct ast_ha *append_ha_core(const char *sense, const char *stuff, struct ast_ha *path, int *error, int port_flags)
{
struct ast_ha *ha;
struct ast_ha *prev = NULL;
}
while ((tmp = strsep(&list, ","))) {
+ uint16_t save_port;
+
if (!(ha = ast_calloc(1, sizeof(*ha)))) {
if (error) {
*error = 1;
ha->sense = allowing;
}
- if (!ast_sockaddr_parse(&ha->addr, address, PARSE_PORT_FORBID)) {
+ if (!ast_sockaddr_parse(&ha->addr, address, port_flags)) {
ast_log(LOG_WARNING, "Invalid IP address: %s\n", address);
ast_free_ha(ha);
if (error) {
return ret;
}
+ /* Be pedantic and zero out the port if we don't want it */
+ if ((port_flags & PARSE_PORT_MASK) == PARSE_PORT_FORBID) {
+ ast_sockaddr_set_port(&ha->addr, 0);
+ }
+
/* If someone specifies an IPv4-mapped IPv6 address,
* we just convert this to an IPv4 ACL
*/
return ret;
}
+ /* ast_sockaddr_apply_netmask() does not preserve the port, so we need to save and
+ * restore it */
+ save_port = ast_sockaddr_port(&ha->addr);
+
if (ast_sockaddr_apply_netmask(&ha->addr, &ha->netmask, &ha->addr)) {
/* This shouldn't happen because ast_sockaddr_parse would
* have failed much earlier on an unsupported address scheme
return ret;
}
+ ast_sockaddr_set_port(&ha->addr, save_port);
+
if (prev) {
prev->next = ha;
} else {
return ret;
}
+struct ast_ha *ast_append_ha(const char *sense, const char *stuff, struct ast_ha *path, int *error)
+{
+ return append_ha_core(sense, stuff, path, error, PARSE_PORT_FORBID);
+}
+
+struct ast_ha *ast_append_ha_with_port(const char *sense, const char *stuff, struct ast_ha *path, int *error)
+{
+ return append_ha_core(sense, stuff, path, error, 0);
+}
+
void ast_ha_join(const struct ast_ha *ha, struct ast_str **buf)
{
for (; ha; ha = ha->next) {
+ const char *addr;
+
+ if (ast_sockaddr_port(&ha->addr)) {
+ addr = ast_sockaddr_stringify(&ha->addr);
+ } else {
+ addr = ast_sockaddr_stringify_addr(&ha->addr);
+ }
+
ast_str_append(buf, 0, "%s%s/",
ha->sense == AST_SENSE_ALLOW ? "!" : "",
- ast_sockaddr_stringify_addr(&ha->addr));
+ addr);
/* Separated to avoid duplicating stringified addresses. */
ast_str_append(buf, 0, "%s", ast_sockaddr_stringify_addr(&ha->netmask));
if (ha->next) {
struct ast_sockaddr result;
struct ast_sockaddr mapped_addr;
const struct ast_sockaddr *addr_to_use;
+ uint16_t save_port;
#if 0 /* debugging code */
char iabuf[INET_ADDRSTRLEN];
char iabuf2[INET_ADDRSTRLEN];
}
}
+ /* ast_sockaddr_apply_netmask() does not preserve the port, so we need to save and
+ * restore it */
+ save_port = ast_sockaddr_port(addr_to_use);
+
/* For each rule, if this address and the netmask = the net address
apply the current rule */
if (ast_sockaddr_apply_netmask(addr_to_use, ¤t_ha->netmask, &result)) {
/* Unlikely to happen since we know the address to be IPv4 or IPv6 */
continue;
}
- if (!ast_sockaddr_cmp_addr(&result, ¤t_ha->addr)) {
+
+ ast_sockaddr_set_port(&result, save_port);
+
+ if (!ast_sockaddr_cmp_addr(&result, ¤t_ha->addr)
+ && (!ast_sockaddr_port(¤t_ha->addr)
+ || ast_sockaddr_port(¤t_ha->addr) == ast_sockaddr_port(&result))) {
res = current_ha->sense;
}
}
hostnames. IP addresses may have a subnet mask appended. The
subnet mask may be written in either CIDR or dotted-decimal
notation. Separate the IP address and subnet mask with a slash
- ('/').
+ ('/'). A source port can also be specified by adding a colon (':')
+ after the address but before the subnet mask, e.g.
+ 3.2.1.0:5061/24.
</para>
</description>
</configOption>
int num_addrs = 0, error = 0, i;
int results = 0;
- num_addrs = ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC);
+ num_addrs = ast_sockaddr_resolve(&addrs, host, 0, AST_AF_UNSPEC);
if (!num_addrs) {
return -1;
}
}
/* We deny what we actually want to match because there is an implicit permit all rule for ACLs */
- identify->matches = ast_append_ha("d", ast_sockaddr_stringify_addr(&addrs[i]), identify->matches, &error);
+ identify->matches = ast_append_ha_with_port("d", ast_sockaddr_stringify(&addrs[i]), identify->matches, &error);
if (!identify->matches || error) {
results = -1;
}
while ((current_string = ast_strip(strsep(&input_string, ",")))) {
- char *mask = strrchr(current_string, '/');
+ char *mask;
+ struct ast_sockaddr address;
int error = 0;
if (ast_strlen_zero(current_string)) {
continue;
}
- if (mask) {
- identify->matches = ast_append_ha("d", current_string, identify->matches, &error);
+ mask = strrchr(current_string, '/');
+
+ /* If it looks like a netmask is present, or we can immediately parse as an IP,
+ * hand things off to the ACL */
+ if (mask || ast_sockaddr_parse(&address, current_string, 0)) {
+ identify->matches = ast_append_ha_with_port("d", current_string, identify->matches, &error);
if (!identify->matches || error) {
ast_log(LOG_ERROR, "Failed to add address '%s' to ip endpoint identifier '%s'\n",
/* Resolve the match addresses now */
i = ao2_iterator_init(identify->hosts, 0);
while ((current_string = ao2_iterator_next(&i))) {
- struct ast_sockaddr address;
int results = 0;
-
- /* If the provided string is not an IP address perform SRV resolution on it */
- if (identify->srv_lookups && !ast_sockaddr_parse(&address, current_string, 0)) {
- results = ip_identify_match_srv_lookup(identify, "_sip._udp", current_string,
- results);
- if (results != -1) {
- results = ip_identify_match_srv_lookup(identify, "_sip._tcp",
- current_string, results);
- }
- if (results != -1) {
- results = ip_identify_match_srv_lookup(identify, "_sips._tcp",
- current_string, results);
+ char *colon = strrchr(current_string, ':');
+
+ /* We skip SRV lookup if a colon is present, assuming a port was specified */
+ if (!colon) {
+ /* No port, and we know this is not an IP address, so perform SRV resolution on it */
+ if (identify->srv_lookups) {
+ results = ip_identify_match_srv_lookup(identify, "_sip._udp", current_string,
+ results);
+ if (results != -1) {
+ results = ip_identify_match_srv_lookup(identify, "_sip._tcp",
+ current_string, results);
+ }
+ if (results != -1) {
+ results = ip_identify_match_srv_lookup(identify, "_sips._tcp",
+ current_string, results);
+ }
}
}
static void match_to_var_list_append(struct ast_variable **head, struct ast_ha *ha)
{
char str[MAX_OBJECT_FIELD];
- const char *addr = ast_strdupa(ast_sockaddr_stringify_addr(&ha->addr));
+ const char *addr;
+
+ if (ast_sockaddr_port(&ha->addr)) {
+ addr = ast_strdupa(ast_sockaddr_stringify(&ha->addr));
+ } else {
+ addr = ast_strdupa(ast_sockaddr_stringify_addr(&ha->addr));
+ }
+
snprintf(str, MAX_OBJECT_FIELD, "%s%s/%s", ha->sense == AST_SENSE_ALLOW ? "!" : "",
addr, ast_sockaddr_stringify_addr(&ha->netmask));
indent = CLI_INDENT_TO_SPACES(context->indent_level);
for (match = ident->matches; match; match = match->next) {
- const char *addr = ast_sockaddr_stringify_addr(&match->addr);
+ const char *addr;
+
+ if (ast_sockaddr_port(&match->addr)) {
+ addr = ast_sockaddr_stringify(&match->addr);
+ } else {
+ addr = ast_sockaddr_stringify_addr(&match->addr);
+ }
ast_str_append(&context->output_buffer, 0, "%*s: %s%s/%d\n",
indent,