bool hplugins_addpacket(unsigned short cmd, unsigned short length, void (*receive) (int fd), unsigned int point, unsigned int pluginID) { struct HPluginPacket *packet; int i; if (point >= hpPHP_MAX) { ShowError("HPM->addPacket:%s: unknown point '%u' specified for packet 0x%04x (len %d)\n",HPM->pid2name(pluginID),point,cmd,length); return false; } for (i = 0; i < VECTOR_LENGTH(HPM->packets[point]); i++) { if (VECTOR_INDEX(HPM->packets[point], i).cmd == cmd ) { ShowError("HPM->addPacket:%s: can't add packet 0x%04x, already in use by '%s'!", HPM->pid2name(pluginID), cmd, HPM->pid2name(VECTOR_INDEX(HPM->packets[point], i).pluginID)); return false; } } VECTOR_ENSURE(HPM->packets[point], 1, 1); VECTOR_PUSHZEROED(HPM->packets[point]); packet = &VECTOR_LAST(HPM->packets[point]); packet->pluginID = pluginID; packet->cmd = cmd; packet->len = length; packet->receive = receive; return true; }
/** * Saves changed achievements for a character. * @param[in] char_id character identifier. * @param[out] cp pointer to loaded achievements. * @param[in] p pointer to map-sent character achievements. * @return number of achievements saved. */ static int inter_achievement_tosql(int char_id, struct char_achievements *cp, const struct char_achievements *p) { StringBuf buf; int i = 0, rows = 0; nullpo_ret(cp); nullpo_ret(p); Assert_ret(char_id > 0); StrBuf->Init(&buf); StrBuf->Printf(&buf, "REPLACE INTO `%s` (`char_id`, `ach_id`, `completed_at`, `rewarded_at`", char_achievement_db); for (i = 0; i < MAX_ACHIEVEMENT_OBJECTIVES; i++) StrBuf->Printf(&buf, ", `obj_%d`", i); StrBuf->AppendStr(&buf, ") VALUES "); for (i = 0; i < VECTOR_LENGTH(*p); i++) { int j = 0; bool save = false; struct achievement *pa = &VECTOR_INDEX(*p, i), *cpa = NULL; ARR_FIND(0, VECTOR_LENGTH(*cp), j, ((cpa = &VECTOR_INDEX(*cp, j)) && cpa->id == pa->id)); if (j == VECTOR_LENGTH(*cp)) save = true; else if (memcmp(cpa, pa, sizeof(struct achievement)) != 0) save = true; if (save) { StrBuf->Printf(&buf, "%s('%d', '%d', '%"PRId64"', '%"PRId64"'", rows ?", ":"", char_id, pa->id, (int64)pa->completed_at, (int64)pa->rewarded_at); for (j = 0; j < MAX_ACHIEVEMENT_OBJECTIVES; j++) StrBuf->Printf(&buf, ", '%d'", pa->objective[j]); StrBuf->AppendStr(&buf, ")"); rows++; } } if (rows > 0 && SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf))) { Sql_ShowDebug(inter->sql_handle); StrBuf->Destroy(&buf); // Destroy the buffer. return 0; } // Destroy the buffer. StrBuf->Destroy(&buf); if (rows) { ShowInfo("achievements saved for char %d (total: %d, saved: %d)\n", char_id, VECTOR_LENGTH(*p), rows); /* Sync with inter-db acheivements. */ VECTOR_CLEAR(*cp); VECTOR_ENSURE(*cp, VECTOR_LENGTH(*p), 1); VECTOR_PUSHARRAY(*cp, VECTOR_DATA(*p), VECTOR_LENGTH(*p)); } return rows; }
/** * Imports a shared symbol. * * @param name The symbol name. * @param pID The requesting plugin ID. * @return The symbol value. * @retval NULL if the symbol wasn't found. */ void *hplugin_import_symbol(char *name, unsigned int pID) { int i; ARR_FIND(0, VECTOR_LENGTH(HPM->symbols), i, strcmp(VECTOR_INDEX(HPM->symbols, i)->name, name) == 0); if (i != VECTOR_LENGTH(HPM->symbols)) return VECTOR_INDEX(HPM->symbols, i)->ptr; ShowError("HPM:get_symbol:%s: '"CL_WHITE"%s"CL_RESET"' not found!\n",HPM->pid2name(pID),name); return NULL; }
/** * Adds an entry to a plugin data store. * * @param type[in] The store type. * @param pluginID[in] The plugin identifier. * @param storeptr[in,out] A pointer to the store. The store will be initialized if necessary. * @param data[in] The data entry to add. * @param classid[in] The entry class identifier. * @param autofree[in] Whether the entry should be automatically freed when removed. */ void hplugins_addToHPData(enum HPluginDataTypes type, uint32 pluginID, struct hplugin_data_store **storeptr, void *data, uint32 classid, bool autofree) { struct hplugin_data_store *store; struct hplugin_data_entry *entry; int i; nullpo_retv(storeptr); if (!HPM->data_store_validate(type, storeptr, true)) { /* woo it failed! */ ShowError("HPM:addToHPData:%s: failed, type %u (%u|%u)\n", HPM->pid2name(pluginID), type, pluginID, classid); return; } store = *storeptr; /* duplicate check */ ARR_FIND(0, VECTOR_LENGTH(store->entries), i, VECTOR_INDEX(store->entries, i)->pluginID == pluginID && VECTOR_INDEX(store->entries, i)->classid == classid); if (i != VECTOR_LENGTH(store->entries)) { ShowError("HPM:addToHPData:%s: error! attempting to insert duplicate struct of id %u and classid %u\n", HPM->pid2name(pluginID), pluginID, classid); return; } /* hplugin_data_entry is always same size, probably better to use the ERS (with reasonable chunk size e.g. 10/25/50) */ CREATE(entry, struct hplugin_data_entry, 1); /* input */ entry->pluginID = pluginID; entry->classid = classid; entry->flag.free = autofree ? 1 : 0; entry->data = data; VECTOR_ENSURE(store->entries, 1, 1); VECTOR_PUSH(store->entries, entry); }
void intercom_claim(intercom_ctx *ctx, struct client *client) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); intercom_packet_claim *packet = malloc(sizeof(intercom_packet_claim) + CLAIM_MAX * sizeof(intercom_packet_claim_entry)); int i; uint32_t nonce = rand(); packet->hdr = (intercom_packet_hdr) { .type = INTERCOM_CLAIM, .nonce = nonce, .ttl = 255, }; memcpy(&packet->sender, ctx->ip.s6_addr, sizeof(uint8_t) * 16); memcpy(&packet->mac, client->mac, sizeof(uint8_t) * 6); packet->lastseen = now.tv_sec - client->lastseen.tv_sec; intercom_packet_claim_entry *entry = (intercom_packet_claim_entry*)((uint8_t*)(packet) + sizeof(intercom_packet_claim)); for (i = 0; i < VECTOR_LEN(client->addresses) && i < CLAIM_MAX; i++) { struct client_ip *ip = &VECTOR_INDEX(client->addresses, i); entry->lastseen = now.tv_sec - ip->lastseen.tv_sec; memcpy(&entry->address, ip->address.s6_addr, sizeof(uint8_t) * 16); entry++; } packet->num_addresses = i; ssize_t packet_len = sizeof(intercom_packet_claim) + i * sizeof(intercom_packet_claim_entry); intercom_recently_seen_add(ctx, &packet->hdr); intercom_send_packet(ctx, (uint8_t*)packet, packet_len); }
/** * Executes an event on all loaded plugins. * * @param type The event type to trigger. */ void hplugin_trigger_event(enum hp_event_types type) { int i; for (i = 0; i < VECTOR_LENGTH(HPM->plugins); i++) { struct hplugin *plugin = VECTOR_INDEX(HPM->plugins, i); if (plugin->hpi->event[type] != NULL) plugin->hpi->event[type](); } }
/** * Retrieves an entry from a plugin data store. * * @param type[in] The store type. * @param pluginID[in] The plugin identifier. * @param store[in] The store. * @param classid[in] The entry class identifier. * * @return The retrieved entry, or NULL. */ void *hplugins_getFromHPData(enum HPluginDataTypes type, uint32 pluginID, struct hplugin_data_store *store, uint32 classid) { int i; if (!HPM->data_store_validate(type, &store, false)) { /* woo it failed! */ ShowError("HPM:getFromHPData:%s: failed, type %u (%u|%u)\n", HPM->pid2name(pluginID), type, pluginID, classid); return NULL; } if (!store) return NULL; ARR_FIND(0, VECTOR_LENGTH(store->entries), i, VECTOR_INDEX(store->entries, i)->pluginID == pluginID && VECTOR_INDEX(store->entries, i)->classid == classid); if (i != VECTOR_LENGTH(store->entries)) return VECTOR_INDEX(store->entries, i)->data; return NULL; }
/** * Checks whether a plugin is currently loaded * * @param filename The plugin filename. * @retval true if the plugin exists and is currently loaded. * @retval false otherwise. */ bool hplugin_exists(const char *filename) { int i; for (i = 0; i < VECTOR_LENGTH(HPM->plugins); i++) { if (strcmpi(VECTOR_INDEX(HPM->plugins, i)->filename,filename) == 0) return true; } return false; }
bool intercom_has_ifname(intercom_ctx *ctx, char *ifname) { for (int i = 0; i < VECTOR_LEN(ctx->interfaces); i++) { intercom_if *iface = &VECTOR_INDEX(ctx->interfaces, i); if (strcmp(ifname, iface->ifname) == 0) return true; } return false; }
/** * Removes an entry from a plugin data store. * * @param type[in] The store type. * @param pluginID[in] The plugin identifier. * @param store[in] The store. * @param classid[in] The entry class identifier. */ void hplugins_removeFromHPData(enum HPluginDataTypes type, uint32 pluginID, struct hplugin_data_store *store, uint32 classid) { struct hplugin_data_entry *entry; int i; if (!HPM->data_store_validate(type, &store, false)) { /* woo it failed! */ ShowError("HPM:removeFromHPData:%s: failed, type %u (%u|%u)\n", HPM->pid2name(pluginID), type, pluginID, classid); return; } if (!store) return; ARR_FIND(0, VECTOR_LENGTH(store->entries), i, VECTOR_INDEX(store->entries, i)->pluginID == pluginID && VECTOR_INDEX(store->entries, i)->classid == classid); if (i == VECTOR_LENGTH(store->entries)) return; entry = VECTOR_INDEX(store->entries, i); VECTOR_ERASE(store->entries, i); // Erase and compact aFree(entry->data); // when it's removed we delete it regardless of autofree aFree(entry); }
void intercom_update_interfaces(intercom_ctx *ctx) { for (int i = 0; i < VECTOR_LEN(ctx->interfaces); i++) { intercom_if *iface = &VECTOR_INDEX(ctx->interfaces, i); iface->ifindex = if_nametoindex(iface->ifname); if (!iface->ifindex) continue; if (join_mcast(ctx->fd, ctx->groupaddr.sin6_addr, iface)) iface->ok = true; } }
/** * Adds a configuration listener for a plugin. * * @param pluginID The plugin identifier. * @param type The configuration type to listen for. * @param name The configuration entry name. * @param func The callback function. * @retval true if the listener was added successfully. * @retval false in case of error. */ bool hplugins_addconf(unsigned int pluginID, enum HPluginConfType type, char *name, void (*parse_func) (const char *key, const char *val), int (*return_func) (const char *key)) { struct HPConfListenStorage *conf; int i; if (parse_func == NULL) { ShowError("HPM->addConf:%s: missing setter function for config '%s'\n",HPM->pid2name(pluginID),name); return false; } if (type == HPCT_BATTLE && return_func == NULL) { ShowError("HPM->addConf:%s: missing getter function for config '%s'\n",HPM->pid2name(pluginID),name); return false; } if (type >= HPCT_MAX) { ShowError("HPM->addConf:%s: unknown point '%u' specified for config '%s'\n",HPM->pid2name(pluginID),type,name); return false; } ARR_FIND(0, VECTOR_LENGTH(HPM->config_listeners[type]), i, strcmpi(name, VECTOR_INDEX(HPM->config_listeners[type], i).key) == 0); if (i != VECTOR_LENGTH(HPM->config_listeners[type])) { ShowError("HPM->addConf:%s: duplicate '%s', already in use by '%s'!", HPM->pid2name(pluginID), name, HPM->pid2name(VECTOR_INDEX(HPM->config_listeners[type], i).pluginID)); return false; } VECTOR_ENSURE(HPM->config_listeners[type], 1, 1); VECTOR_PUSHZEROED(HPM->config_listeners[type]); conf = &VECTOR_LAST(HPM->config_listeners[type]); conf->pluginID = pluginID; safestrncpy(conf->key, name, HPM_ADDCONF_LENGTH); conf->parse_func = parse_func; conf->return_func = return_func; return true; }
/*========================================== * Inbox Request *------------------------------------------*/ void mapif_rodex_sendinbox(int fd, int char_id, int8 opentype, int8 flag, int count, int64 mail_id, struct rodex_maillist *mails) { int per_packet = (UINT16_MAX - 24) / sizeof(struct rodex_message); int sent = 0; bool is_first = true; nullpo_retv(mails); Assert_retv(char_id > 0); Assert_retv(count >= 0); Assert_retv(mail_id >= 0); do { int i = 24, j, size, limit; int to_send = count - sent; bool is_last = true; if (to_send <= per_packet) { size = to_send * sizeof(struct rodex_message) + 24; limit = to_send; is_last = true; } else { limit = min(to_send, per_packet); if (limit != to_send) { is_last = false; } size = limit * sizeof(struct rodex_message) + 24; } WFIFOHEAD(fd, size); WFIFOW(fd, 0) = 0x3895; WFIFOW(fd, 2) = size; WFIFOL(fd, 4) = char_id; WFIFOB(fd, 8) = opentype; WFIFOB(fd, 9) = flag; WFIFOB(fd, 10) = is_last; WFIFOB(fd, 11) = is_first; WFIFOL(fd, 12) = limit; WFIFOQ(fd, 16) = mail_id; for (j = 0; j < limit; ++j, ++sent, i += sizeof(struct rodex_message)) { memcpy(WFIFOP(fd, i), &VECTOR_INDEX(*mails, sent), sizeof(struct rodex_message)); } WFIFOSET(fd, size); is_first = false; } while (sent < count); }
/** * Adds a plugin-defined command-line argument. * * @param pluginID the current plugin's ID. * @param name the command line argument's name, including the leading '--'. * @param has_param whether the command line argument expects to be followed by a value. * @param func the triggered function. * @param help the help string to be displayed by '--help', if any. * @return the success status. */ bool hpm_add_arg(unsigned int pluginID, char *name, bool has_param, CmdlineExecFunc func, const char *help) { int i; if (!name || strlen(name) < 3 || name[0] != '-' || name[1] != '-') { ShowError("HPM:add_arg:%s invalid argument name: arguments must begin with '--' (from %s)\n", name, HPM->pid2name(pluginID)); return false; } ARR_FIND(0, VECTOR_LENGTH(cmdline->args_data), i, strcmp(VECTOR_INDEX(cmdline->args_data, i).name, name) == 0); if (i != VECTOR_LENGTH(cmdline->args_data)) { ShowError("HPM:add_arg:%s duplicate! (from %s)\n",name,HPM->pid2name(pluginID)); return false; } return cmdline->arg_add(pluginID, name, '\0', func, help, has_param ? CMDLINE_OPT_PARAM : CMDLINE_OPT_NORMAL); }
/** * Creates a new console command entry. * * @param name The command name. * @param func The command callback. */ void console_parse_create(char *name, CParseFunc func) { int i; char *tok; char sublist[CP_CMD_LENGTH * 5]; struct CParseEntry *cmd; safestrncpy(sublist, name, CP_CMD_LENGTH * 5); tok = strtok(sublist,":"); ARR_FIND(0, VECTOR_LENGTH(console->input->command_list), i, strcmpi(tok, VECTOR_INDEX(console->input->command_list, i)->cmd) == 0); if (i == VECTOR_LENGTH(console->input->command_list)) { CREATE(cmd, struct CParseEntry, 1); safestrncpy(cmd->cmd, tok, CP_CMD_LENGTH); cmd->type = CPET_UNKNOWN; VECTOR_ENSURE(console->input->commands, 1, 1); VECTOR_PUSH(console->input->commands, cmd); VECTOR_ENSURE(console->input->command_list, 1, 1); VECTOR_PUSH(console->input->command_list, cmd); }
safestrncpy(sublist, name, CP_CMD_LENGTH * 5); tok = strtok(sublist,":"); ARR_FIND(0, VECTOR_LENGTH(console->input->command_list), i, strcmpi(tok, VECTOR_INDEX(console->input->command_list, i)->cmd) == 0); if (i == VECTOR_LENGTH(console->input->command_list)) { CREATE(cmd, struct CParseEntry, 1); safestrncpy(cmd->cmd, tok, CP_CMD_LENGTH); cmd->type = CPET_UNKNOWN; VECTOR_ENSURE(console->input->commands, 1, 1); VECTOR_PUSH(console->input->commands, cmd); VECTOR_ENSURE(console->input->command_list, 1, 1); VECTOR_PUSH(console->input->command_list, cmd); } cmd = VECTOR_INDEX(console->input->command_list, i); while ((tok = strtok(NULL, ":")) != NULL) { if (cmd->type == CPET_UNKNOWN) { cmd->type = CPET_CATEGORY; VECTOR_INIT(cmd->u.children); } Assert_retv(cmd->type == CPET_CATEGORY); ARR_FIND(0, VECTOR_LENGTH(cmd->u.children), i, strcmpi(VECTOR_INDEX(cmd->u.children, i)->cmd,tok) == 0); if (i == VECTOR_LENGTH(cmd->u.children)) { struct CParseEntry *entry; CREATE(entry, struct CParseEntry, 1); safestrncpy(entry->cmd, tok, CP_CMD_LENGTH); entry->type = CPET_UNKNOWN; VECTOR_ENSURE(console->input->commands, 1, 1); VECTOR_PUSH(console->input->commands, entry);
void intercom_add_interface(intercom_ctx *ctx, char *ifname) { if (intercom_has_ifname(ctx, ifname)) return; intercom_if iface = { .ok = false, .ifname = ifname }; VECTOR_ADD(ctx->interfaces, iface); intercom_update_interfaces(ctx); } void intercom_init(intercom_ctx *ctx) { struct in6_addr mgroup_addr; inet_pton(AF_INET6, INTERCOM_GROUP, &mgroup_addr); // TODO Fehler abfangen ctx->groupaddr = (struct sockaddr_in6) { .sin6_family = AF_INET6, .sin6_addr = mgroup_addr, .sin6_port = htons(INTERCOM_PORT), }; ctx->fd = socket(PF_INET6, SOCK_DGRAM | SOCK_NONBLOCK, 0); if (ctx->fd < 0) exit_error("creating socket"); struct sockaddr_in6 server_addr = {}; server_addr.sin6_family = AF_INET6; server_addr.sin6_addr = in6addr_any; server_addr.sin6_port = htons(INTERCOM_PORT); if (bind(ctx->fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { perror("bind failed"); exit(EXIT_FAILURE); } } void intercom_seek(intercom_ctx *ctx, const struct in6_addr *address) { intercom_packet_seek packet; uint32_t nonce = rand(); packet.hdr = (intercom_packet_hdr) { .type = INTERCOM_SEEK, .nonce = nonce, .ttl = 255, }; memcpy(&packet.address, address, 16); intercom_recently_seen_add(ctx, &packet.hdr); intercom_send_packet(ctx, (uint8_t*)&packet, sizeof(packet)); } void intercom_send_packet(intercom_ctx *ctx, uint8_t *packet, ssize_t packet_len) { for (int i = 0; i < VECTOR_LEN(ctx->interfaces); i++) { intercom_if *iface = &VECTOR_INDEX(ctx->interfaces, i); if (!iface->ok) continue; struct sockaddr_in6 groupaddr = ctx->groupaddr; groupaddr.sin6_scope_id = iface->ifindex; printf("intercom send %i %zi\n", iface->ifindex, packet_len); ssize_t rc = sendto(ctx->fd, packet, packet_len, 0, &groupaddr, sizeof(groupaddr)); if (rc < 0) iface->ok = false; } } bool intercom_recently_seen(intercom_ctx *ctx, intercom_packet_hdr *hdr) { for (int i = 0; i < VECTOR_LEN(ctx->recent_packets); i++) { intercom_packet_hdr *ref_hdr = &VECTOR_INDEX(ctx->recent_packets, i); if (ref_hdr->nonce == hdr->nonce && ref_hdr->type == hdr->type) return true; } return false; } void intercom_recently_seen_add(intercom_ctx *ctx, intercom_packet_hdr *hdr) { if (VECTOR_LEN(ctx->recent_packets) > INTERCOM_MAX_RECENT) VECTOR_DELETE(ctx->recent_packets, 0); VECTOR_ADD(ctx->recent_packets, *hdr); } void intercom_handle_seek(struct l3ctx *ctx, intercom_packet_seek *packet) { icmp6_send_solicitation(ctx, (const struct in6_addr *)packet->address); }
/** * Loads console commands list **/ void console_load_defaults(void) { /** * Defines a main category. * * Categories can't be used as commands! * E.G. * - sql update skip * 'sql' is the main category * CP_DEF_C(category) **/ #define CP_DEF_C(x) { #x , CPET_CATEGORY, NULL , NULL, NULL } /** * Defines a sub-category. * * Sub-categories can't be used as commands! * E.G. * - sql update skip * 'update' is a sub-category * CP_DEF_C2(command, category) **/ #define CP_DEF_C2(x,y) { #x , CPET_CATEGORY, NULL , #y, NULL } /** * Defines a command that is inside a category or sub-category * CP_DEF_S(command, category/sub-category) **/ #define CP_DEF_S(x,y) { #x, CPET_FUNCTION, CPCMD_C_A(x,y), #y, NULL } /** * Defines a command that is _not_ inside any category * CP_DEF_S(command) **/ #define CP_DEF(x) { #x , CPET_FUNCTION, CPCMD_A(x), NULL, NULL } struct { char *name; int type; CParseFunc func; char *connect; struct CParseEntry *self; } default_list[] = { CP_DEF(help), /** * Server related commands **/ CP_DEF_C(server), CP_DEF_S(ers_report,server), CP_DEF_S(mem_report,server), CP_DEF_S(malloc_usage,server), CP_DEF_S(exit,server), /** * Sql related commands **/ CP_DEF_C(sql), CP_DEF_C2(update,sql), CP_DEF_S(skip,update), }; int len = ARRAYLENGTH(default_list); struct CParseEntry *cmd; int i; VECTOR_ENSURE(console->input->commands, len, 1); for(i = 0; i < len; i++) { CREATE(cmd, struct CParseEntry, 1); safestrncpy(cmd->cmd, default_list[i].name, CP_CMD_LENGTH); cmd->type = default_list[i].type; switch (cmd->type) { case CPET_FUNCTION: cmd->u.func = default_list[i].func; break; case CPET_CATEGORY: VECTOR_INIT(cmd->u.children); break; case CPET_UNKNOWN: break; } VECTOR_PUSH(console->input->commands, cmd); default_list[i].self = cmd; if (!default_list[i].connect) { VECTOR_ENSURE(console->input->command_list, 1, 1); VECTOR_PUSH(console->input->command_list, cmd); } } for (i = 0; i < len; i++) { int k; if (!default_list[i].connect) continue; ARR_FIND(0, VECTOR_LENGTH(console->input->commands), k, strcmpi(default_list[i].connect, VECTOR_INDEX(console->input->commands, k)->cmd) == 0); if (k != VECTOR_LENGTH(console->input->commands)) { struct CParseEntry *parent = VECTOR_INDEX(console->input->commands, k); Assert_retb(parent->type == CPET_CATEGORY); cmd = default_list[i].self; VECTOR_ENSURE(parent->u.children, 1, 1); VECTOR_PUSH(parent->u.children, cmd); } } #undef CP_DEF_C #undef CP_DEF_C2 #undef CP_DEF_S #undef CP_DEF }