Files
reddit/scripts/traffic/verify.c
Brian Simpson 00e03edbfd Traffic processing: Validate clicks by checking response code.
Double checking in the click app and in the processing scripts was
difficult. Just trust the click app and assume any request that got
a 302 response is valid.
2014-12-12 17:00:29 -05:00

143 lines
3.7 KiB
C

#include <stdio.h>
#include <assert.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/sha.h>
#include <openssl/hmac.h>
#include "utils.h"
#define MAX_LINE 2048
int main(int argc, char** argv)
{
const char* secret;
secret = getenv("TRACKING_SECRET");
if (!secret) {
fprintf(stderr, "TRACKING_SECRET not set\n");
return 1;
}
char input_line[MAX_LINE];
unsigned int hash_length = SHA_DIGEST_LENGTH;
unsigned char input_hash[hash_length];
unsigned char expected_hash[hash_length];
int secret_length = strlen(secret);
while (fgets(input_line, MAX_LINE, stdin) != NULL) {
/* get the fields */
char *ip, *path, *query, *response_code, *unique_id;
split_fields(
input_line,
&ip,
&path,
&query,
&response_code,
&unique_id,
NO_MORE_FIELDS
);
/* in the query string, grab the fields we want to verify */
char *id = NULL;
char *hash = NULL;
char *url = NULL;
char *key, *value;
while (parse_query_param(&query, &key, &value) >= 0) {
if (strcmp(key, "id") == 0) {
id = value;
} else if (strcmp(key, "hash") == 0) {
hash = value;
} else if (strcmp(key, "url") == 0) {
url = value;
}
}
if (id == NULL || hash == NULL)
continue;
/* decode the params */
int id_length = url_decode(id);
if (id_length < 0)
continue;
if (url_decode(hash) != 40)
continue;
int url_length = 0;
if (url != NULL) {
url_length = url_decode(url);
if (url_length < 0)
continue;
}
/* validation:
* for clicks just check the response code--validation was done in
the click redirect app
* for impression pixels check the hash
*/
if (strcmp("/click", path) == 0) {
if (strcmp(response_code, "302") != 0) {
continue;
}
} else {
/* turn the expected hash into bytes */
bool bad_hash = false;
for (int i = 0; i < hash_length; i++) {
int count = sscanf(&hash[i*2], "%2hhx", &input_hash[i]);
if (count != 1) {
bad_hash = true;
break;
}
}
if (bad_hash)
continue;
/* generate the expected hash */
HMAC_CTX ctx;
// NOTE: EMR has openssl <1.0, so these HMAC methods don't return
// error codes -- see https://www.openssl.org/docs/crypto/hmac.html
HMAC_Init(&ctx, secret, secret_length, EVP_sha1());
HMAC_Update(&ctx, id, id_length);
HMAC_Final(&ctx, expected_hash, &hash_length);
/* check that the hashes match */
if (memcmp(input_hash, expected_hash, SHA_DIGEST_LENGTH) != 0)
continue;
}
/* split out the fullname and subreddit if necessary */
char *fullname = id;
char *subreddit = NULL;
for (char *c = id; *c != '\0'; c++) {
if (*c == '-') {
subreddit = c + 1;
*c = '\0';
break;
}
}
/* output stuff! */
fputs(unique_id, stdout);
fputc('\t', stdout);
fputs(path, stdout);
fputc('\t', stdout);
fputs(fullname, stdout);
fputc('\t', stdout);
if (subreddit != NULL) {
fputs(subreddit, stdout);
}
fputc('\n', stdout);
}
}