1 /** @file
2 
3   A simple remap plugin for ATS
4 
5   @section license License
6 
7   Licensed to the Apache Software Foundation (ASF) under one
8   or more contributor license agreements.  See the NOTICE file
9   distributed with this work for additional information
10   regarding copyright ownership.  The ASF licenses this file
11   to you under the Apache License, Version 2.0 (the
12   "License"); you may not use this file except in compliance
13   with the License.  You may obtain a copy of the License at
14 
15       http://www.apache.org/licenses/LICENSE-2.0
16 
17   Unless required by applicable law or agreed to in writing, software
18   distributed under the License is distributed on an "AS IS" BASIS,
19   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   See the License for the specific language governing permissions and
21   limitations under the License.
22 
23   @section description
24     This is a very simple plugin: it will add headers that are specified on a remap line
25 
26     Example usage:
27     map /foo http://127.0.0.1/ @plugin=remap_header_add.so @pparam=foo:"x" @pparam=@test:"c" @pparam=a:"b"
28 
29  */
30 #include <cstdio>
31 #include <cstdlib>
32 #include <cstring>
33 
34 #include "ts/ts.h"
35 #include "ts/remap.h"
36 
37 struct remap_line {
38   int argc;
39   char **argv; // store the originals
40 
41   int nvc;     // the number of name value pairs, should be argc - 2.
42   char **name; // at load we will parse out the name and values.
43   char **val;
44 };
45 
46 #define PLUGIN_NAME "headeradd_remap"
47 #define EXTERN extern "C"
48 
49 EXTERN void
ParseArgIntoNv(const char * arg,char ** n,char ** v)50 ParseArgIntoNv(const char *arg, char **n, char **v)
51 {
52   const char *colon_pos = strchr(arg, ':');
53 
54   if (colon_pos == nullptr) {
55     *n = nullptr;
56     *v = nullptr;
57     TSDebug(PLUGIN_NAME, "No name value pair since it was malformed");
58     return;
59   }
60 
61   size_t name_len = colon_pos - arg;
62   *n              = static_cast<char *>(TSmalloc(name_len + 1));
63   memcpy(*n, arg, colon_pos - arg);
64   (*n)[name_len] = '\0';
65 
66   size_t val_len = strlen(colon_pos + 1); // skip past the ':'
67 
68   // check to see if the value is quoted.
69   if (val_len > 1 && colon_pos[1] == '"' && colon_pos[val_len] == '"') {
70     colon_pos++;  // advance past the first quote
71     val_len -= 2; // don't include the trailing quote
72   }
73 
74   *v = static_cast<char *>(TSmalloc(val_len + 1));
75   memcpy(*v, colon_pos + 1, val_len);
76   (*v)[val_len] = '\0';
77 
78   TSDebug(PLUGIN_NAME, "\t name_len=%zu, val_len=%zu, %s=%s", name_len, val_len, *n, *v);
79 }
80 
81 TSReturnCode
TSRemapInit(TSRemapInterface *,char *,int)82 TSRemapInit(TSRemapInterface *, char *, int)
83 {
84   return TS_SUCCESS;
85 }
86 
87 TSReturnCode
TSRemapNewInstance(int argc,char * argv[],void ** ih,char *,int)88 TSRemapNewInstance(int argc, char *argv[], void **ih, char *, int)
89 {
90   remap_line *rl = nullptr;
91 
92   TSDebug(PLUGIN_NAME, "TSRemapNewInstance()");
93 
94   if (!argv || !ih) {
95     TSError("[%s] Unable to load plugin because missing argv or ih", PLUGIN_NAME);
96     return TS_ERROR;
97   }
98 
99   // print all arguments for this particular remapping
100 
101   rl       = static_cast<remap_line *>(TSmalloc(sizeof(remap_line)));
102   rl->argc = argc;
103   rl->argv = argv;
104   rl->nvc  = argc - 2; // the first two are the remap from and to
105   if (rl->nvc) {
106     rl->name = static_cast<char **>(TSmalloc(sizeof(char *) * rl->nvc));
107     rl->val  = static_cast<char **>(TSmalloc(sizeof(char *) * rl->nvc));
108   }
109 
110   TSDebug(PLUGIN_NAME, "NewInstance:");
111   for (int i = 2; i < argc; i++) {
112     ParseArgIntoNv(argv[i], &rl->name[i - 2], &rl->val[i - 2]);
113   }
114 
115   *ih = rl;
116 
117   return TS_SUCCESS;
118 }
119 
120 void
TSRemapDeleteInstance(void * ih)121 TSRemapDeleteInstance(void *ih)
122 {
123   TSDebug(PLUGIN_NAME, "deleting instance %p", ih);
124 
125   if (ih) {
126     remap_line *rl = static_cast<remap_line *>(ih);
127     for (int i = 0; i < rl->nvc; ++i) {
128       TSfree(rl->name[i]);
129       TSfree(rl->val[i]);
130     }
131 
132     TSfree(rl->name);
133     TSfree(rl->val);
134     TSfree(rl);
135   }
136 }
137 
138 TSRemapStatus
TSRemapDoRemap(void * ih,TSHttpTxn txn,TSRemapRequestInfo * rri)139 TSRemapDoRemap(void *ih, TSHttpTxn txn, TSRemapRequestInfo *rri)
140 {
141   remap_line *rl = static_cast<remap_line *>(ih);
142 
143   if (!rl || !rri) {
144     TSError("[%s] rl or rri is null", PLUGIN_NAME);
145     return TSREMAP_NO_REMAP;
146   }
147 
148   TSDebug(PLUGIN_NAME, "TSRemapDoRemap:");
149 
150   TSMBuffer req_bufp;
151   TSMLoc req_loc;
152   if (TSHttpTxnClientReqGet(txn, &req_bufp, &req_loc) != TS_SUCCESS) {
153     TSError("[%s] Error while retrieving client request header", PLUGIN_NAME);
154     return TSREMAP_NO_REMAP;
155   }
156 
157   for (int i = 0; i < rl->nvc; ++i) {
158     TSDebug(PLUGIN_NAME, R"(Attaching header "%s" with value "%s".)", rl->name[i], rl->val[i]);
159 
160     TSMLoc field_loc;
161     if (TSMimeHdrFieldCreate(req_bufp, req_loc, &field_loc) == TS_SUCCESS) {
162       TSMimeHdrFieldNameSet(req_bufp, req_loc, field_loc, rl->name[i], strlen(rl->name[i]));
163       TSMimeHdrFieldAppend(req_bufp, req_loc, field_loc);
164       TSMimeHdrFieldValueStringInsert(req_bufp, req_loc, field_loc, 0, rl->val[i], strlen(rl->val[i]));
165     } else {
166       TSError("[%s] Failure on TSMimeHdrFieldCreate", PLUGIN_NAME);
167     }
168   }
169 
170   return TSREMAP_NO_REMAP;
171 }
172