-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathworker.js
137 lines (122 loc) · 3.47 KB
/
worker.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/**
* This handled a request to update a given DNS record.
* The request should have the following format:
*
* { "addr": "<ipv4_addr>", "timestamp": <unix_timestamp> }
*
* The request must be made by the machine that the record will be pointed to
* and contain the HMAC (of the request body) in the "Authorization" header
*/
addEventListener("fetch", (event) => {
event.respondWith(handleRequest(event.request));
});
/**
* Handles the request and validates if changes should be made or not
* @param {Request} request
*/
async function handleRequest(request) {
if (request.method === "POST") {
let valid_request = await is_valid(request);
if (valid_request) {
const addr = request.headers.get("cf-connecting-ip");
await updateRecord(addr);
return new Response("Não há gente como a gente", { status: 200 });
}
}
return new Response("Por cima", { status: 401 });
}
/**
* Checks if it is a valid and authentic request
* @param {Request} request
*/
async function is_valid(request) {
const window = 300; // 5 minutes in seconds
const rawBody = await request.text();
let bodyContent = {};
try {
bodyContent = JSON.parse(rawBody);
} catch (e) {
return false;
}
const sourceAddr = request.headers.get("cf-connecting-ip");
const signature = request.headers.get("authorization");
if (!signature || !bodyContent.addr || sourceAddr != bodyContent.addr) {
return false;
}
const valid_hmac = await verifyHMAC(signature, rawBody);
if (!valid_hmac) {
return false;
}
const now = Math.floor(Date.now() / 1000);
if (now - bodyContent.timestamp > window) {
return false;
}
return true;
}
/**
* Verifies the provided HMAC matches the message
* @param {String} signature
* @param {String} message
*/
async function verifyHMAC(signature, message) {
let encoder = new TextEncoder();
let key = await crypto.subtle.importKey(
"raw",
encoder.encode(SHARED_KEY),
{ name: "HMAC", hash: { name: "SHA-256" } },
false,
["verify"]
);
result = await crypto.subtle.verify(
"HMAC",
key,
hexToArrayBuffer(signature),
encoder.encode(message)
);
return result;
}
/**
* Updates the DNS record with the provided IP
* @param {String} addr
*/
async function updateRecord(addr) {
const base = "https://api.cloudflare.com/client/v4/zones";
const init = { headers: { Authorization: `Bearer ${CF_API_TOKEN}` } };
let record;
let record_res = await fetch(
`${base}/${ZONE}/dns_records?name=${DNS_RECORD}`,
init
);
if (record_res.ok) {
record = (await record_res.json()).result[0];
} else {
console.log("Get record failed");
return;
}
if (record.content != addr) {
init.method = "PATCH";
init.body = JSON.stringify({ content: addr });
await fetch(`${base}/${ZONE}/dns_records/${record.id}`, init);
console.log("Updated record");
} else {
console.log("Record content is the same, skipping update");
}
}
/**
* Transforms an HEX string into an ArrayBuffer
* Original work of: https://github.com/LinusU/hex-to-array-buffer
* @param {String} hex
*/
function hexToArrayBuffer(hex) {
if (typeof hex !== "string") {
throw new TypeError("Expected input to be a string");
}
if (hex.length % 2 !== 0) {
throw new RangeError("Expected string to be an even number of characters");
}
var view = new Uint8Array(hex.length / 2);
for (var i = 0; i < hex.length; i += 2) {
view[i / 2] = parseInt(hex.substring(i, i + 2), 16);
}
return view.buffer;
}