Update README for archival
[reddit.git] / scripts / traffic / verify.c
blobce8eef8f4da6af401a28009ed7a0295ba7777768
1 #include <stdio.h>
2 #include <assert.h>
3 #include <stdbool.h>
4 #include <string.h>
5 #include <stdlib.h>
7 #include <openssl/sha.h>
8 #include <openssl/hmac.h>
10 #include "utils.h"
12 #define MAX_LINE 2048
13 #define SHA1_HASH_LENGTH 40
15 int main(int argc, char** argv)
17 const char* secret;
18 secret = getenv("TRACKING_SECRET");
19 if (!secret) {
20 fprintf(stderr, "TRACKING_SECRET not set\n");
21 return 1;
24 char input_line[MAX_LINE];
25 unsigned int hash_length = SHA_DIGEST_LENGTH;
26 unsigned char input_hash[hash_length];
27 unsigned char expected_hash[hash_length];
28 int secret_length = strlen(secret);
30 while (fgets(input_line, MAX_LINE, stdin) != NULL) {
31 /* get the fields */
32 char *ip, *path, *query, *response_code, *unique_id;
34 split_fields(
35 input_line,
36 &ip,
37 &path,
38 &query,
39 &response_code,
40 &unique_id,
41 NO_MORE_FIELDS
44 /* in the query string, grab the fields we want to verify */
45 char *id = NULL;
46 char *hash = NULL;
47 char *url = NULL;
49 char *key, *value;
50 while (parse_query_param(&query, &key, &value) >= 0) {
51 if (strcmp(key, "id") == 0) {
52 id = value;
53 } else if (strcmp(key, "hash") == 0) {
54 hash = value;
55 } else if (strcmp(key, "url") == 0) {
56 url = value;
60 if (id == NULL || hash == NULL)
61 continue;
63 /* decode the params */
64 int id_length = url_decode(id);
65 if (id_length < 0)
66 continue;
68 if (url_decode(hash) != SHA1_HASH_LENGTH)
69 continue;
71 int url_length = 0;
72 if (url != NULL) {
73 url_length = url_decode(url);
74 if (url_length < 0)
75 continue;
78 /* validation:
79 * for clicks just check the response code--validation was done in
80 the click redirect app
81 * for impression pixels check the hash
83 if (strcmp("/click", path) == 0) {
84 if (strcmp(response_code, "302") != 0) {
85 continue;
87 } else {
88 /* turn the expected hash into bytes */
89 bool bad_hash = false;
90 for (int i = 0; i < hash_length; i++) {
91 int count = sscanf(&hash[i*2], "%2hhx", &input_hash[i]);
92 if (count != 1) {
93 bad_hash = true;
94 break;
98 if (bad_hash)
99 continue;
101 /* generate the expected hash */
102 HMAC_CTX ctx;
104 // NOTE: EMR has openssl <1.0, so these HMAC methods don't return
105 // error codes -- see https://www.openssl.org/docs/crypto/hmac.html
106 HMAC_Init(&ctx, secret, secret_length, EVP_sha1());
107 HMAC_Update(&ctx, id, id_length);
108 HMAC_Final(&ctx, expected_hash, &hash_length);
110 /* check that the hashes match */
111 if (memcmp(input_hash, expected_hash, SHA_DIGEST_LENGTH) != 0)
112 continue;
115 /* split out the fullname and subreddit if necessary */
116 char *fullname = id;
117 char *subreddit = NULL;
119 for (char *c = id; *c != '\0'; c++) {
120 if (*c == '-') {
121 subreddit = c + 1;
122 *c = '\0';
123 break;
127 /* output stuff! */
128 fputs(unique_id, stdout);
129 fputc('\t', stdout);
131 fputs(path, stdout);
132 fputc('\t', stdout);
134 fputs(fullname, stdout);
135 fputc('\t', stdout);
137 if (subreddit != NULL) {
138 fputs(subreddit, stdout);
141 fputc('\n', stdout);