diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample index c7553b73c0c42394980891c82e4686ca7d9e598d..92d9aaa86a83119bc3e301ac5b63ce4339bd2f2e 100644 --- a/configs/samples/pjsip.conf.sample +++ b/configs/samples/pjsip.conf.sample @@ -263,6 +263,7 @@ ;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=== diff --git a/doc/CHANGES-staging/res_pjsip_endpoint_identifier_ip_match_port.txt b/doc/CHANGES-staging/res_pjsip_endpoint_identifier_ip_match_port.txt new file mode 100644 index 0000000000000000000000000000000000000000..3881d64ab18219a64dcaa450bf5c8414a0d059d6 --- /dev/null +++ b/doc/CHANGES-staging/res_pjsip_endpoint_identifier_ip_match_port.txt @@ -0,0 +1,8 @@ +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. diff --git a/include/asterisk/acl.h b/include/asterisk/acl.h index f7655fe56e3b9c89fcb8c26f87bc84a517c64a81..4abe9fb487ea86f780da01f728fcb02f53eb4204 100644 --- a/include/asterisk/acl.h +++ b/include/asterisk/acl.h @@ -134,6 +134,29 @@ void ast_copy_ha(const struct ast_ha *from, struct ast_ha *to); */ 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 diff --git a/main/acl.c b/main/acl.c index 27da4eec7daf06601dbd96071e2d080c792abffb..bca1e50614768287534e3755c69b2a75f1ffde1f 100644 --- a/main/acl.c +++ b/main/acl.c @@ -572,7 +572,7 @@ static void debug_ha_sense_appended(struct ast_ha *ha) 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; @@ -589,6 +589,8 @@ struct ast_ha *ast_append_ha(const char *sense, const char *stuff, struct ast_ha } while ((tmp = strsep(&list, ","))) { + uint16_t save_port; + if (!(ha = ast_calloc(1, sizeof(*ha)))) { if (error) { *error = 1; @@ -610,7 +612,7 @@ struct ast_ha *ast_append_ha(const char *sense, const char *stuff, struct ast_ha 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) { @@ -619,6 +621,11 @@ struct ast_ha *ast_append_ha(const char *sense, const char *stuff, struct ast_ha 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 */ @@ -667,6 +674,10 @@ struct ast_ha *ast_append_ha(const char *sense, const char *stuff, struct ast_ha 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 @@ -681,6 +692,8 @@ struct ast_ha *ast_append_ha(const char *sense, const char *stuff, struct ast_ha return ret; } + ast_sockaddr_set_port(&ha->addr, save_port); + if (prev) { prev->next = ha; } else { @@ -696,12 +709,30 @@ struct ast_ha *ast_append_ha(const char *sense, const char *stuff, struct ast_ha 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) { @@ -783,6 +814,7 @@ enum ast_acl_sense ast_apply_ha(const struct ast_ha *ha, const struct ast_sockad 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]; @@ -818,13 +850,22 @@ enum ast_acl_sense ast_apply_ha(const struct ast_ha *ha, const struct ast_sockad } } + /* 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; } } diff --git a/res/res_pjsip_endpoint_identifier_ip.c b/res/res_pjsip_endpoint_identifier_ip.c index a4bdab478e708b620089bfb4f199458cca209504..db4edbfb589b588c08fc32dbae61b36172cca1bd 100644 --- a/res/res_pjsip_endpoint_identifier_ip.c +++ b/res/res_pjsip_endpoint_identifier_ip.c @@ -63,7 +63,9 @@ 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> @@ -310,7 +312,7 @@ static int ip_identify_match_host_lookup(struct ip_identify_match *identify, con 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; } @@ -322,7 +324,7 @@ static int ip_identify_match_host_lookup(struct ip_identify_match *identify, con } /* 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; @@ -380,15 +382,20 @@ static int ip_identify_match_handler(const struct aco_option *opt, struct ast_va } 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", @@ -498,20 +505,23 @@ static int ip_identify_apply(const struct ast_sorcery *sorcery, void *obj) /* 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); + } } } @@ -554,7 +564,14 @@ static int match_to_str(const void *obj, const intptr_t *args, char **buf) 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)); @@ -737,7 +754,13 @@ static int cli_print_body(void *obj, void *arg, int flags) 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,