1 /*
2 * Copyright (c) 2014-2020 by Farsight Security, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 /* asprintf() does not appear on linux without this */
18 #define _GNU_SOURCE
19
20 #include <assert.h>
21 #include <ctype.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <arpa/inet.h>
26
27 #include "defs.h"
28 #include "sort.h"
29 #include "globals.h"
30
31 /* in the POSIX sort(1) intermediate format, the fields are:
32 * #1 first
33 * #2 last
34 * #3 duration
35 * #4 count
36 * #5 rrname
37 * #7 rrtype
38 * #6 rdata
39 * #8 mode
40 * #9 json
41 */
42
43 #define MAX_KEYS 7
44
45 extern char **environ;
46
47 static struct sortkey keys[MAX_KEYS];
48 static int nkeys = 0;
49
50 /* sort_ready -- finish initializing the sort related metadata.
51 *
52 * If sorting, all keys must be specified, to enable -u.
53 * This adds every possible sort key, ignoring any errors from adding
54 * a key in case the key was already added as specified by the user.
55 */
56 void
sort_ready(void)57 sort_ready(void) {
58 (void) add_sort_key("first");
59 (void) add_sort_key("last");
60 (void) add_sort_key("duration");
61 (void) add_sort_key("count");
62 (void) add_sort_key("name");
63 (void) add_sort_key("type");
64 (void) add_sort_key("data");
65 }
66
67 /* add_sort_key -- add a key for use by POSIX sort.
68 *
69 * Returns NULL if no error, otherwise a static error message.
70 */
71 const char *
add_sort_key(const char * key_name)72 add_sort_key(const char *key_name) {
73 const char *key = NULL;
74 char *computed;
75 int x;
76
77 if (nkeys == MAX_KEYS)
78 return "too many sort keys given.";
79 if (strcasecmp(key_name, "first") == 0)
80 key = "-k1n";
81 else if (strcasecmp(key_name, "last") == 0)
82 key = "-k2n";
83 else if (strcasecmp(key_name, "duration") == 0)
84 key = "-k3n";
85 else if (strcasecmp(key_name, "count") == 0)
86 key = "-k4n";
87 else if (strcasecmp(key_name, "name") == 0)
88 key = "-k5";
89 else if (strcasecmp(key_name, "type") == 0)
90 key = "-k6";
91 else if (strcasecmp(key_name, "data") == 0)
92 key = "-k7";
93 else
94 return "key must be in "
95 "first|last|duration|count|name|type|data";
96 x = asprintf(&computed, "%s%s", key,
97 sorting == reverse_sort ? "r" : "");
98 if (x < 0)
99 my_panic(true, "asprintf");
100 keys[nkeys++] = (struct sortkey){strdup(key_name), computed};
101 return NULL;
102 }
103
104 /* find_sort_key -- return pointer to a sort key, or NULL if it's not specified
105 */
106 sortkey_ct
find_sort_key(const char * key_name)107 find_sort_key(const char *key_name) {
108 int n;
109
110 for (n = 0; n < nkeys; n++) {
111 if (strcmp(keys[n].specified, key_name) == 0)
112 return &keys[n];
113 }
114 return NULL;
115 }
116
117 /* sort_destroy -- drop sort metadata from heap.
118 */
119 void
sort_destroy(void)120 sort_destroy(void) {
121 int n;
122
123 for (n = 0; n < nkeys; n++) {
124 DESTROY(keys[n].specified);
125 DESTROY(keys[n].computed);
126 }
127 }
128
129 /* exec_sort -- replace this fork with a POSIX sort program
130 */
131 __attribute__((noreturn)) void
exec_sort(int p1[],int p2[])132 exec_sort(int p1[], int p2[]) {
133 char *sort_argv[3+MAX_KEYS], **sap;
134 int n;
135
136 if (dup2(p1[0], STDIN_FILENO) < 0 ||
137 dup2(p2[1], STDOUT_FILENO) < 0) {
138 perror("dup2");
139 _exit(1);
140 }
141 close(p1[0]); close(p1[1]);
142 close(p2[0]); close(p2[1]);
143 sap = sort_argv;
144 *sap++ = strdup("sort");
145 *sap++ = strdup("-u");
146 for (n = 0; n < nkeys; n++)
147 *sap++ = strdup(keys[n].computed);
148 *sap++ = NULL;
149 putenv(strdup("LC_ALL=C"));
150 DEBUG(1, true, "\"%s\" args:", path_sort);
151 for (sap = sort_argv; *sap != NULL; sap++)
152 DEBUG(1, false, " [%s]", *sap);
153 DEBUG(1, false, "\n");
154 execve(path_sort, sort_argv, environ);
155 perror("execve");
156 for (sap = sort_argv; *sap != NULL; sap++)
157 DESTROY(*sap);
158 _exit(1);
159 }
160
161 /* sortable_rrname -- return a POSIX-sort-collatable rendition of RR name+type.
162 */
163 char *
sortable_rrname(pdns_tuple_ct tup)164 sortable_rrname(pdns_tuple_ct tup) {
165 struct sortbuf buf = {};
166
167 sortable_dnsname(&buf, json_string_value(tup->obj.rrname));
168 buf.base = realloc(buf.base, buf.size+1);
169 buf.base[buf.size++] = '\0';
170 return buf.base;
171 }
172
173 /* sortable_rdata -- return a POSIX-sort-collatable rendition of RR data set.
174 */
175 char *
sortable_rdata(pdns_tuple_ct tup)176 sortable_rdata(pdns_tuple_ct tup) {
177 struct sortbuf buf = {};
178
179 if (json_is_array(tup->obj.rdata)) {
180 size_t index;
181 json_t *rr;
182
183 json_array_foreach(tup->obj.rdata, index, rr) {
184 if (json_is_string(rr))
185 sortable_rdatum(&buf, tup->rrtype,
186 json_string_value(rr));
187 else
188 fprintf(stderr,
189 "%s: warning: rdata slot "
190 "is not a string\n",
191 program_name);
192 }
193 } else {
194 sortable_rdatum(&buf, tup->rrtype, tup->rdata);
195 }
196 buf.base = realloc(buf.base, buf.size+1);
197 buf.base[buf.size++] = '\0';
198 return buf.base;
199 }
200
201 /* sortable_rdatum -- called only by sortable_rdata(), realloc and normalize.
202 *
203 * this converts (lossily) addresses into hex strings, and extracts the
204 * server-name component of a few other types like MX. all other rdata
205 * are left in their normal string form, because it's hard to know what
206 * to sort by with something like TXT, and extracting the serial number
207 * from an SOA using a language like C is a bit ugly.
208 */
209 void
sortable_rdatum(sortbuf_t buf,const char * rrtype,const char * rdatum)210 sortable_rdatum(sortbuf_t buf, const char *rrtype, const char *rdatum) {
211 if (strcmp(rrtype, "A") == 0) {
212 u_char a[4];
213
214 if (inet_pton(AF_INET, rdatum, a) != 1)
215 memset(a, 0, sizeof a);
216 sortable_hexify(buf, a, sizeof a);
217 } else if (strcmp(rrtype, "AAAA") == 0) {
218 u_char aaaa[16];
219
220 if (inet_pton(AF_INET6, rdatum, aaaa) != 1)
221 memset(aaaa, 0, sizeof aaaa);
222 sortable_hexify(buf, aaaa, sizeof aaaa);
223 } else if (strcmp(rrtype, "NS") == 0 ||
224 strcmp(rrtype, "PTR") == 0 ||
225 strcmp(rrtype, "CNAME") == 0 ||
226 strcmp(rrtype, "DNAME") == 0)
227 {
228 sortable_dnsname(buf, rdatum);
229 } else if (strcmp(rrtype, "MX") == 0 ||
230 strcmp(rrtype, "RP") == 0)
231 {
232 const char *space = strrchr(rdatum, ' ');
233
234 if (space != NULL)
235 sortable_dnsname(buf, space+1);
236 else
237 sortable_hexify(buf, (const u_char *)rdatum,
238 strlen(rdatum));
239 } else {
240 sortable_hexify(buf, (const u_char *)rdatum, strlen(rdatum));
241 }
242 }
243
244 /* sortable_hexify -- convert src into hex string in buffer
245 */
246 void
sortable_hexify(sortbuf_t buf,const u_char * src,size_t len)247 sortable_hexify(sortbuf_t buf, const u_char *src, size_t len) {
248 buf->base = realloc(buf->base, buf->size + len*2);
249 for (size_t i = 0; i < len; i++) {
250 static const char hex[] = "0123456789abcdef";
251 unsigned int ch = src[i];
252 buf->base[buf->size++] = hex[ch >> 4];
253 buf->base[buf->size++] = hex[ch & 0xf];
254 }
255 }
256
257 /* sortable_dnsname -- make a sortable dns name; destructive and lossy.
258 *
259 * to be lexicographically sortable, a dnsname has to be converted to
260 * TLD-first, all uppercase letters must be converted to lower case,
261 * and all characters except dots then converted to hexadecimal. this
262 * transformation is for POSIX sort's use, and is irreversibly lossy.
263 */
264 void
sortable_dnsname(sortbuf_t buf,const char * name)265 sortable_dnsname(sortbuf_t buf, const char *name) {
266 struct counted *c = countoff(name);
267
268 // ensure our result buffer is large enough.
269 size_t new_size = buf->size + c->nalnum;
270 if (new_size == 0) {
271 buf->base = realloc(buf->base, 1);
272 buf->base[0] = '.';
273 buf->size = 1;
274 return;
275 }
276 if (new_size != buf->size)
277 buf->base = realloc(buf->base, new_size);
278 char *p = buf->base + buf->size;
279
280 // collatable names are TLD-first, alphanumeric only, lower case.
281 size_t nchar = 0;
282 for (ssize_t i = (ssize_t)(c->nlabel-1); i >= 0; i--) {
283 size_t dot = (name[c->nchar - nchar - 1] == '.');
284 ssize_t j = (ssize_t)(c->lens[i] - dot);
285 ssize_t k = (ssize_t)(c->nchar - nchar - c->lens[i]);
286 for (ssize_t l = k; l < j+k; l++) {
287 int ch = name[l];
288 if (isalnum(ch))
289 *p++ = (char) tolower(ch);
290 }
291 nchar += c->lens[i];
292 }
293 DESTROY(c);
294
295 // update our counted-string output.
296 buf->size = (size_t)(p - buf->base);
297 assert(buf->size == new_size);
298 }
299