#include #include #include #include #include "match.h" #include "util.h" #include "wire.h" #include "try.h" void match_rule_free(match_rule_t *rule) { if (rule) { free(rule->sender); free(rule->interface); free(rule->member); free(rule->path); free(rule->path_namespace); free(rule->destination); free(rule->arg0namespace); for (int i = 0; i < MATCH_RULE_MAX_ARG; i++) { free(rule->arg[i]); free(rule->arg_path[i]); } free(rule); } } match_rule_t *match_rule_from_string(char *d) { char *key = NULL; char acc[MATCH_RULE_MAX]; int acci = 0; match_rule_t *rule; if (strlen(d) >= MATCH_RULE_MAX) { return NULL; } rule = calloc(sizeof(match_rule_t), 1); if (!rule) { return NULL; } while (*d) { /* key */ key = d; while (*d && *d != '=') d++; if (*d != '=') { goto fail; } *d = '\0'; d++; if (!*d) { goto fail; } /* value */ bool quote = false; acci = 0; acc[acci] = '\0'; for (;;) { if (*d == '\'') { /* '\'' starts a quoted region */ quote = !quote; d++; } else if (!quote && *d == ',') { /* ',' outside quoted region means next key-value pair */ acc[acci] = '\0'; acci = 0; d++; break; } else if (!quote && *d == '\\') { /* '\\' outside quoted region means escaped character */ /* TODO: not entirely to spec */ d++; if (*d != '\'') { goto fail; } if (acci >= MATCH_RULE_MAX) { goto fail; } acc[acci++] = *d; d++; } else if (!*d) { /* end of string */ if (quote) { /* end of string, yet we were insinde a quoted region */ /* unexpected end of input */ goto fail; } acc[acci] = '\0'; acci = 0; break; } else if (quote) { if (acci >= MATCH_RULE_MAX) { goto fail; } acc[acci++] = *d; d++; } else { goto fail; } } if (strcmp(key, "type") == 0 && !rule->type) { if (strcmp(acc, "signal") == 0) rule->type = DBUS_MESSAGE_SIGNAL; else if (strcmp(acc, "method_call") == 0) rule->type = DBUS_MESSAGE_METHOD_CALL; else if (strcmp(acc, "method_return") == 0) rule->type = DBUS_MESSAGE_METHOD_RETURN; else if (strcmp(acc, "error") == 0) rule->type = DBUS_MESSAGE_ERROR; else goto fail; } else if (strcmp(key, "sender") == 0 && !rule->sender) { rule->sender = string_dup(acc); } else if (strcmp(key, "interface") == 0 && !rule->interface) { rule->interface = string_dup(acc); } else if (strcmp(key, "member") == 0 && !rule->member) { rule->member = string_dup(acc); } else if (strcmp(key, "path") == 0 && !rule->path && !rule->path_namespace) { rule->path = string_dup(acc); } else if (strcmp(key, "path_namespace") == 0 && !rule->path && !rule->path_namespace) { rule->path_namespace = string_dup(acc); } else if (strcmp(key, "destination") == 0 && !rule->destination) { rule->destination = string_dup(acc); } else if (strcmp(key, "arg0namespace") == 0 && !rule->arg0namespace) { rule->arg0namespace = string_dup(acc); } else if (strncmp(key, "arg", 3) == 0) { char *part = key + 3; if (!*part) { goto fail; } // thanks: https://github.com/bus1/dbus-broker/blob/55fe5443d88ee2b93afa340b82ad89508ff017e2/src/bus/match.c#L105 size_t partn = strlen(part); uint32_t index = 0; for (unsigned int i = 0; i < 2 && partn; i++, part++, --partn) { if (*part >= '0' && *part <= '9') { index = index * 10 + *part - '0'; } else { break; } } if (index == 0 && rule->arg0namespace) { goto fail; } if (index >= MATCH_RULE_MAX_ARG) { goto fail; } if (rule->arg[index] || rule->arg_path[index]) { goto fail; } if (*part == '\0') { rule->arg[index] = string_dup(acc); } else if (strcmp(part, "path") == 0) { rule->arg_path[index] = string_dup(acc); } else { goto fail; } } else { goto fail; } } return rule; fail: match_rule_free(rule); return NULL; } #define _check_header_field_str(M_rule_field, M_header_field) \ do { \ if (rule->M_rule_field && (!msg->fields[M_header_field].present || strcmp(rule->M_rule_field, msg->fields[M_header_field].t.str) != 0)) { \ return -1; \ } \ } while(0) int match_rule_check(match_rule_t *rule, wire_message_t *msg, wire_context_t *ctx) { if (rule->type && msg->type != rule->type) { return -1; } _check_header_field_str(sender, DBUS_HEADER_FIELD_SENDER); _check_header_field_str(interface, DBUS_HEADER_FIELD_INTERFACE); _check_header_field_str(member, DBUS_HEADER_FIELD_MEMBER); _check_header_field_str(path, DBUS_HEADER_FIELD_PATH); /* todo: path_namespace */ _check_header_field_str(destination, DBUS_HEADER_FIELD_DESTINATION); wire_message_body_string_t strings[MATCH_RULE_MAX_ARG]; memset(strings, 0, sizeof(strings)); /* TODO: handle failure */ TRYST(wire_collect_strings(ctx, msg, strings, MATCH_RULE_MAX_ARG)); for (int i = 0; i < MATCH_RULE_MAX_ARG; i++) { if (rule->arg[i] && (!strings[i].str || strcmp(strings[i].str, rule->arg[i]) != 0)) { return -1; } /* todo: arg_path */ } /* todo: arg0namespace */ return 0; }