| 
									
										
										
										
											2018-02-23 22:19:37 +08:00
										 |  |  | #define REDISMODULE_EXPERIMENTAL_API
 | 
					
						
							| 
									
										
										
										
											2019-03-24 18:03:03 +08:00
										 |  |  | #include "redismodule.h"
 | 
					
						
							| 
									
										
										
										
											2018-02-23 22:19:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-19 00:36:46 +08:00
										 |  |  | #include <string.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-23 22:19:37 +08:00
										 |  |  | static RedisModuleString *log_key_name; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-24 18:03:03 +08:00
										 |  |  | static const char log_command_name[] = "commandfilter.log"; | 
					
						
							|  |  |  | static const char ping_command_name[] = "commandfilter.ping"; | 
					
						
							|  |  |  | static const char unregister_command_name[] = "commandfilter.unregister"; | 
					
						
							| 
									
										
										
										
											2019-03-22 01:47:43 +08:00
										 |  |  | static int in_log_command = 0; | 
					
						
							| 
									
										
										
										
											2019-03-19 05:07:28 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-21 20:44:49 +08:00
										 |  |  | static RedisModuleCommandFilter *filter = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-24 18:03:03 +08:00
										 |  |  | int CommandFilter_UnregisterCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) | 
					
						
							| 
									
										
										
										
											2019-03-21 20:44:49 +08:00
										 |  |  | { | 
					
						
							|  |  |  |     (void) argc; | 
					
						
							|  |  |  |     (void) argv; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     RedisModule_ReplyWithLongLong(ctx, | 
					
						
							|  |  |  |             RedisModule_UnregisterCommandFilter(ctx, filter)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return REDISMODULE_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-24 18:03:03 +08:00
										 |  |  | int CommandFilter_PingCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) | 
					
						
							| 
									
										
										
										
											2019-03-19 05:07:28 +08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-03-21 20:44:49 +08:00
										 |  |  |     (void) argc; | 
					
						
							|  |  |  |     (void) argv; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-19 05:07:28 +08:00
										 |  |  |     RedisModuleCallReply *reply = RedisModule_Call(ctx, "ping", "c", "@log"); | 
					
						
							|  |  |  |     if (reply) { | 
					
						
							|  |  |  |         RedisModule_ReplyWithCallReply(ctx, reply); | 
					
						
							|  |  |  |         RedisModule_FreeCallReply(reply); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         RedisModule_ReplyWithSimpleString(ctx, "Unknown command or invalid arguments"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return REDISMODULE_OK; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-02-23 22:19:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-24 18:03:03 +08:00
										 |  |  | int CommandFilter_LogCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) | 
					
						
							| 
									
										
										
										
											2018-02-23 22:19:37 +08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-03-19 05:07:28 +08:00
										 |  |  |     RedisModuleString *s = RedisModule_CreateString(ctx, "", 0); | 
					
						
							| 
									
										
										
										
											2018-02-23 22:19:37 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     int i; | 
					
						
							|  |  |  |     for (i = 1; i < argc; i++) { | 
					
						
							|  |  |  |         size_t arglen; | 
					
						
							|  |  |  |         const char *arg = RedisModule_StringPtrLen(argv[i], &arglen); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-19 05:07:28 +08:00
										 |  |  |         if (i > 1) RedisModule_StringAppendBuffer(ctx, s, " ", 1); | 
					
						
							| 
									
										
										
										
											2018-02-23 22:19:37 +08:00
										 |  |  |         RedisModule_StringAppendBuffer(ctx, s, arg, arglen); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     RedisModuleKey *log = RedisModule_OpenKey(ctx, log_key_name, REDISMODULE_WRITE|REDISMODULE_READ); | 
					
						
							|  |  |  |     RedisModule_ListPush(log, REDISMODULE_LIST_HEAD, s); | 
					
						
							|  |  |  |     RedisModule_CloseKey(log); | 
					
						
							|  |  |  |     RedisModule_FreeString(ctx, s); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-22 01:47:43 +08:00
										 |  |  |     in_log_command = 1; | 
					
						
							| 
									
										
										
										
											2019-03-19 05:07:28 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-23 22:19:37 +08:00
										 |  |  |     size_t cmdlen; | 
					
						
							|  |  |  |     const char *cmdname = RedisModule_StringPtrLen(argv[1], &cmdlen); | 
					
						
							|  |  |  |     RedisModuleCallReply *reply = RedisModule_Call(ctx, cmdname, "v", &argv[2], argc - 2); | 
					
						
							|  |  |  |     if (reply) { | 
					
						
							|  |  |  |         RedisModule_ReplyWithCallReply(ctx, reply); | 
					
						
							|  |  |  |         RedisModule_FreeCallReply(reply); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         RedisModule_ReplyWithSimpleString(ctx, "Unknown command or invalid arguments"); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-03-19 05:07:28 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-22 01:47:43 +08:00
										 |  |  |     in_log_command = 0; | 
					
						
							| 
									
										
										
										
											2019-03-19 05:07:28 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-23 22:19:37 +08:00
										 |  |  |     return REDISMODULE_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-24 18:03:03 +08:00
										 |  |  | void CommandFilter_CommandFilter(RedisModuleCommandFilterCtx *filter) | 
					
						
							| 
									
										
										
										
											2018-02-23 22:19:37 +08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-03-24 18:03:03 +08:00
										 |  |  |     if (in_log_command) return;  /* don't process our own RM_Call() from CommandFilter_LogCommand() */ | 
					
						
							| 
									
										
										
										
											2018-02-23 22:19:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-19 00:36:46 +08:00
										 |  |  |     /* Fun manipulations:
 | 
					
						
							|  |  |  |      * - Remove @delme | 
					
						
							|  |  |  |      * - Replace @replaceme | 
					
						
							|  |  |  |      * - Append @insertbefore or @insertafter | 
					
						
							| 
									
										
										
										
											2021-06-10 20:39:33 +08:00
										 |  |  |      * - Prefix with Log command if @log encountered | 
					
						
							| 
									
										
										
										
											2019-03-19 00:36:46 +08:00
										 |  |  |      */ | 
					
						
							|  |  |  |     int log = 0; | 
					
						
							|  |  |  |     int pos = 0; | 
					
						
							|  |  |  |     while (pos < RedisModule_CommandFilterArgsCount(filter)) { | 
					
						
							|  |  |  |         const RedisModuleString *arg = RedisModule_CommandFilterArgGet(filter, pos); | 
					
						
							|  |  |  |         size_t arg_len; | 
					
						
							|  |  |  |         const char *arg_str = RedisModule_StringPtrLen(arg, &arg_len); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (arg_len == 6 && !memcmp(arg_str, "@delme", 6)) { | 
					
						
							|  |  |  |             RedisModule_CommandFilterArgDelete(filter, pos); | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |         }  | 
					
						
							|  |  |  |         if (arg_len == 10 && !memcmp(arg_str, "@replaceme", 10)) { | 
					
						
							|  |  |  |             RedisModule_CommandFilterArgReplace(filter, pos, | 
					
						
							|  |  |  |                     RedisModule_CreateString(NULL, "--replaced--", 12)); | 
					
						
							|  |  |  |         } else if (arg_len == 13 && !memcmp(arg_str, "@insertbefore", 13)) { | 
					
						
							|  |  |  |             RedisModule_CommandFilterArgInsert(filter, pos, | 
					
						
							|  |  |  |                     RedisModule_CreateString(NULL, "--inserted-before--", 19)); | 
					
						
							|  |  |  |             pos++; | 
					
						
							|  |  |  |         } else if (arg_len == 12 && !memcmp(arg_str, "@insertafter", 12)) { | 
					
						
							|  |  |  |             RedisModule_CommandFilterArgInsert(filter, pos + 1, | 
					
						
							|  |  |  |                     RedisModule_CreateString(NULL, "--inserted-after--", 18)); | 
					
						
							|  |  |  |             pos++; | 
					
						
							|  |  |  |         } else if (arg_len == 4 && !memcmp(arg_str, "@log", 4)) { | 
					
						
							|  |  |  |             log = 1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         pos++; | 
					
						
							| 
									
										
										
										
											2018-02-23 22:19:37 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-03-19 00:36:46 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (log) RedisModule_CommandFilterArgInsert(filter, 0, | 
					
						
							|  |  |  |             RedisModule_CreateString(NULL, log_command_name, sizeof(log_command_name)-1)); | 
					
						
							| 
									
										
										
										
											2018-02-23 22:19:37 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { | 
					
						
							| 
									
										
										
										
											2019-03-24 18:03:03 +08:00
										 |  |  |     if (RedisModule_Init(ctx,"commandfilter",1,REDISMODULE_APIVER_1) | 
					
						
							| 
									
										
										
										
											2018-02-23 22:19:37 +08:00
										 |  |  |             == REDISMODULE_ERR) return REDISMODULE_ERR; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-22 01:47:43 +08:00
										 |  |  |     if (argc != 2) { | 
					
						
							| 
									
										
										
										
											2018-02-23 22:19:37 +08:00
										 |  |  |         RedisModule_Log(ctx, "warning", "Log key name not specified"); | 
					
						
							|  |  |  |         return REDISMODULE_ERR; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-22 01:47:43 +08:00
										 |  |  |     long long noself = 0; | 
					
						
							| 
									
										
										
										
											2018-02-23 22:19:37 +08:00
										 |  |  |     log_key_name = RedisModule_CreateStringFromString(ctx, argv[0]); | 
					
						
							| 
									
										
										
										
											2019-03-22 01:47:43 +08:00
										 |  |  |     RedisModule_StringToLongLong(argv[1], &noself); | 
					
						
							| 
									
										
										
										
											2018-02-23 22:19:37 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (RedisModule_CreateCommand(ctx,log_command_name, | 
					
						
							| 
									
										
										
										
											2019-03-24 18:03:03 +08:00
										 |  |  |                 CommandFilter_LogCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR) | 
					
						
							| 
									
										
										
										
											2018-02-23 22:19:37 +08:00
										 |  |  |             return REDISMODULE_ERR; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-19 05:07:28 +08:00
										 |  |  |     if (RedisModule_CreateCommand(ctx,ping_command_name, | 
					
						
							| 
									
										
										
										
											2019-03-24 18:03:03 +08:00
										 |  |  |                 CommandFilter_PingCommand,"deny-oom",1,1,1) == REDISMODULE_ERR) | 
					
						
							| 
									
										
										
										
											2019-03-19 05:07:28 +08:00
										 |  |  |             return REDISMODULE_ERR; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-21 20:44:49 +08:00
										 |  |  |     if (RedisModule_CreateCommand(ctx,unregister_command_name, | 
					
						
							| 
									
										
										
										
											2019-03-24 18:03:03 +08:00
										 |  |  |                 CommandFilter_UnregisterCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR) | 
					
						
							| 
									
										
										
										
											2019-03-21 20:44:49 +08:00
										 |  |  |             return REDISMODULE_ERR; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-24 18:03:03 +08:00
										 |  |  |     if ((filter = RedisModule_RegisterCommandFilter(ctx, CommandFilter_CommandFilter,  | 
					
						
							| 
									
										
										
										
											2019-03-22 01:47:43 +08:00
										 |  |  |                     noself ? REDISMODULE_CMDFILTER_NOSELF : 0)) | 
					
						
							| 
									
										
										
										
											2019-03-21 20:44:49 +08:00
										 |  |  |             == NULL) return REDISMODULE_ERR; | 
					
						
							| 
									
										
										
										
											2018-02-23 22:19:37 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return REDISMODULE_OK; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-10-24 14:45:25 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | int RedisModule_OnUnload(RedisModuleCtx *ctx) { | 
					
						
							|  |  |  |     RedisModule_FreeString(ctx, log_key_name); | 
					
						
							|  |  |  |     return REDISMODULE_OK; | 
					
						
							|  |  |  | } |