UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2026 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "service.h" 30 31 #include "../../util/pblock.h" 32 #include "../../util/util.h" 33 #include "cx/string.h" 34 35 #define SIMPLE_COOKIE_PAGE_TEMPLATE \ 36 "<!DOCTYPE html>\n" \ 37 "<html>\n" \ 38 " <head>\n" \ 39 " <title>Anti Bot Challenge</title>\n" \ 40 " <script>\n" \ 41 " fuckcounter=0;\n" \ 42 " addEventListener(\"mousemove\", (event) => {\n" \ 43 " fuckcounter++;\n" \ 44 " })\n" \ 45 " setTimeout(function() {\n" \ 46 " const b = document.getElementById(''fuckai'');\n" \ 47 " b.style.display = ''initial'';\n" \ 48 " b.addEventListener(''click'', function() {\n" \ 49 " if (fuckcounter < 50) return;\n" \ 50 " document.cookie = ''%s=%s; path=/'';\n" \ 51 " window.location.reload();\n" \ 52 " })\n" \ 53 " }, 1000);\n" \ 54 " </script>\n" \ 55 " </head>\n" \ 56 " <body>\n" \ 57 " <h1>Simple Anti Bot Check</h1>\n" \ 58 " <p>Please click the button below to continue.</p>\n" \ 59 " <button style=\"display: none\" id=\"fuckai\" type=\"button\">I am a Human</button>\n" \ 60 " </body>\n" \ 61 "</html>" 62 63 static cxstring gen_simple_cookie_page(pool_handle_t *pool, const char *cookie_name, const char *cookie_value) { 64 size_t cname_len = strlen(cookie_name); 65 size_t cvalue_len = strlen(cookie_value); 66 size_t template_len = sizeof(SIMPLE_COOKIE_PAGE_TEMPLATE) - 1; 67 68 size_t len = template_len + cname_len + cvalue_len; 69 char *buffer = pool_malloc(pool, len + 1); 70 if(!buffer) { 71 return (cxstring){ NULL, 0 }; 72 } 73 74 len = snprintf(buffer, len + 1, SIMPLE_COOKIE_PAGE_TEMPLATE, cookie_name, cookie_value); 75 return (cxstring){ buffer, len }; 76 } 77 78 79 80 // Sends a simple bot challenge page, if the request is marked with is-bot. 81 // The challenge page sets the specified cookie via javascript. 82 // 83 // Args: 84 // cookie: cookie name 85 // value: cookie value (optional) 86 int sab_simple_service(pblock *pb, Session *sn, Request *rq) { 87 char *cookie_name = pblock_findval("cookie", pb); 88 char *cookie_value = pblock_findval("value", pb); 89 if(!cookie_name) { 90 log_ereport(LOG_MISCONFIG, "sab-simple-service: no cookie specified"); 91 return REQ_ABORTED; 92 } 93 94 char *is_bot = pblock_findval("is-bot", rq->vars); 95 if(!is_bot) { 96 return REQ_NOACTION; 97 } 98 99 100 // send the simple challenge page, that sets the cookie with javascript 101 102 if(!cookie_value) { 103 cookie_value = "sab-1"; 104 } 105 106 cxstring response = gen_simple_cookie_page(sn->pool, cookie_name, cookie_value); 107 108 pblock_removekey(pb_key_content_type, rq->srvhdrs); 109 pblock_kvinsert(pb_key_content_type, "text/html; charset=utf-8", 24, rq->srvhdrs); 110 pblock_nninsert("content-length", (int)response.length, rq->srvhdrs); 111 protocol_status(sn, rq, 200, NULL); 112 http_start_response(sn, rq); 113 114 net_write(sn->csd, response.ptr, response.length); 115 116 return REQ_PROCEED; 117 } 118