mirror of
https://github.com/reddit-archive/reddit.git
synced 2026-01-22 21:38:11 -05:00
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.
143 lines
3.7 KiB
C
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);
|
|
}
|
|
}
|