· 10 years ago · Sep 17, 2016, 01:00 AM
1
2# vim: ft=wtf
3$ source user_functions.sh
4$ if is_logged_in
5$ then
6$ echo "<p>Hi, ${COOKIES['USERNAME']}. <a href='/logout.wtf'>Logout</a> <a href='/profile.wtf?user=$(basename $(find_user_file ${COOKIES['USERNAME']}))'>Profile</a></p>"
7$ echo "<a href=/new_post.wtf>New Post</a>";
8$ else
9$ echo "<p>You're not logged in. <a href='/login.wtf'>Login</a> <a href='/new_user.wtf'>Register</a></p>"
10$ fi
11$ if [[ -e .index_cache.html ]]
12$ then
13$ cat .index_cache.html;
14$ else
15$ for post_file in posts/*; do
16$ post_file=$(basename $post_file);
17$ post_title=$(nth_line 2 < posts/$post_file | htmlentities);
18$ post_user=$(nth_line 1 < posts/$post_file | htmlentities);
19$ echo "<li><a href=\"/post.wtf?post=$post_file\">$post_title</a> by ${post_user}</li>";
20$ done;
21$ fi
22
23#!/usr/bin/env bash
24# Some useful standard functions to have around :)
25
26# check if an array contains a given value
27# contains "asdf" "asdf an array of values" => has exit code 0
28function contains {
29 local e;
30 for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done;
31 return 1;
32}
33
34function file_exists {
35 local file=$1;
36 stat ${file} > /dev/null;
37}
38
39function nth_line {
40 local n=$1;
41 local filename;
42 if [[ $# != 1 ]]
43 then
44 filename=$2;
45 sed "${n}q;d" < $filename;
46 else
47 sed "${n}q;d"
48 fi 2> /dev/null
49}
50
51function redirect {
52 local target="$1";
53 echo "<script>window.location.href='${target}';</script>";
54}
55
56# Hacky way of figuring out which date command is appropriate,
57# depending if we're on BSD or GNU coreutils
58YESTERDAY_CMD="";
59TOMORROW_CMD="";
60if date --help | grep "GNU" > /dev/null
61then
62 # Using GNU date
63 TOMORROW_CMD="date -d tomorrow";
64 YESTERDAY_CMD="date -d yesterday";
65else
66 # Using BSD date
67 TOMORROW_CMD="date -v +1d";
68 YESTERDAY_CMD="date -v -1d";
69fi
70
71function set_cookie {
72 local key="$1";
73 local value="$2";
74 local expiry=$(${TOMORROW_CMD});
75 echo "<script>document.cookie = '${key}=${value}; expires=${expiry}; path=/';</script>";
76 COOKIES[$key]="${value}";
77}
78
79function get_cookie {
80 echo "${COOKIES[$1]}";
81}
82
83function remove_cookie {
84 local key="$1";
85 local expiry=$(${YESTERDAY_CMD}); # expiration dates in the past delete cookies
86 echo "<script>document.cookie = '${key}=riperino; expires=${expiry}; path=/';</script>";
87 unset COOKIES[$key];
88}
89
90# take text on input, transform any html special chars to the corresponding entities
91function htmlentities {
92 sed "s/\&/\&amp;/g" | sed "s/</\&lt;/g" | sed "s/>/\&gt;/g";
93}
94
95 # vim: ft=wtf
96
97$ source user_functions.sh
98$ if [[ $method = 'POST' ]]
99$ then
100$ local username=${POST_PARAMS['username']};
101$ local password=${POST_PARAMS['password']};
102$ local userfile=$(find_user_file ${username});
103$ if [[ ${userfile} != 'NONE' ]]
104$ then
105$ # User exists, try to login
106$ if $(check_password ${username} ${password})
107$ then
108$ # correct pass
109$ set_cookie "USERNAME" ${username};
110$ set_cookie "TOKEN" $(nth_line 3 ${userfile});
111$ redirect "/";
112$ else
113$ # incorrect pass
114$ echo "<h3>Sorry, wrong password for user ${username}:(<br>Try again?</h3>";
115$ fi
116$ else
117$ # user doesn't exist
118$ echo "<h3>Sorry, user ${username} doesn't exist :(<br>Try again?</h3>"
119$ fi
120$ fi
121
122# vim: ft=wtf
123$ source user_functions.sh
124$ if is_logged_in
125$ then
126$ remove_cookie 'USERNAME';
127$ remove_cookie 'TOKEN';
128$ redirect "/";
129$ else
130$ echo "<h3>You need to be logged in to log out, bud.</h3>";
131$ fi
132
133# vim: ft=wtf
134$ source user_functions.sh
135$ source post_functions.sh
136$ if [[ $method = 'POST' ]]
137$ then
138$ if is_logged_in
139$ then
140$ post_id=$(create_post "${COOKIES['USERNAME']}" "${POST_PARAMS['title']}" "${POST_PARAMS['text']}");
141$ redirect "/post.wtf?post=${post_id}";
142$ else
143$ echo "<h3>pls login 2 be posting</h3>";
144$ fi
145$ fi
146
147# vim: ft=wtf
148$ source user_functions.sh
149$ if [[ $method = 'POST' ]]
150$ then
151$ local username=${POST_PARAMS['username']};
152$ local password=${POST_PARAMS['password']};
153$ if [[ $(find_user_file "${username}") = 'NONE' ]]
154$ then
155$ # create user
156$ echo "ok, gonna go make that user... ${username}";
157$ local user_id=$(create_user "${username}" "${password}");
158$ redirect "/login.wtf";
159$ else
160$ # user already exists
161$ echo "<h3>Sorry, user ${username} already exists :(<br>Try again?</h3>"
162$ fi
163$ fi
164
165 # vim: ft=wtf
166$ source user_functions.sh
167$ if contains 'post' ${!URL_PARAMS[@]} && file_exists "posts/${URL_PARAMS['post']}"
168$ then
169$ post_id=${URL_PARAMS['post']};
170$ for post_file in $(ls posts/${post_id}/* | sort --field-separator='/' --key=3 -n); do
171$ echo "<div class=\"post\">";
172$ poster=$(nth_line 1 ${post_file} | htmlentities);
173$ title=$(nth_line 2 ${post_file} | htmlentities);
174$ body=$(tail -n +3 ${post_file} | htmlentities 2> /dev/null);
175$ echo "<span class=\"post-poster\">Posted by <a href=\"/profile.wtf?user=$(basename $(find_user_file "${poster}"))\">${poster}</a></span>";
176$ echo "<span class=\"post-title\">$title</span>";
177$ echo "<span class=\"post-body\">$body</span>";
178$ echo "</div>";
179$ done
180$ else
181$ echo "Pls give a (valid) post id";
182$ fi;
183
184<div class="action-btns">
185$ echo "<a href=\"/reply.wtf?post=${post_id}\">Reply</a>"
186<a href="/">Back</a>
187</div>
188
189</body>
190
191</div>
192
193#!/usr/bin/env bash
194source user_functions.sh
195
196# Create a new post. Returns the post id.
197function create_post {
198 local username=$1;
199 local title=$2;
200 local text=$3;
201 local hashed=$(hash_username "${username}");
202
203 # ensure posts dir exists and isn't listable.
204 mkdir posts 2> /dev/null;
205 touch posts/.nolist; # don't allow directory listing on posts
206 touch posts/.noread; # don't allow file reads on post
207
208 local post_id=$(basename $(mktemp --directory posts/XXXXX));
209
210
211 echo ${username} > "posts/${post_id}/1";
212 echo ${title} >> "posts/${post_id}/1";
213 echo ${text} >> "posts/${post_id}/1";
214
215 touch "posts/${post_id}/.nolist";
216 touch "posts/${post_id}/.noread";
217
218
219 # add to our cache for the homepage
220 echo "<li><a href=\"/post.wtf?post=${post_id}\">$(htmlentities <<< ${title})</a> by $(htmlentities <<< ${username})</li>" >> .index_cache.html
221
222 # add post to users' post cache
223 local hashed=$(hash_username "${username}");
224 echo "${post_id}/1" >> "users_lookup/${hashed}/posts";
225
226 echo ${post_id};
227
228}
229
230function reply {
231 local post_id=$1;
232 local username=$2;
233 local text=$3;
234 local hashed=$(hash_username "${username}");
235
236 curr_id=$(for d in posts/${post_id}/*; do basename $d; done | sort -n | tail -n 1);
237 next_reply_id=$(awk '{print $1+1}' <<< "${curr_id}");
238 next_file=(posts/${post_id}/${next_reply_id});
239 echo "${username}" > "${next_file}";
240 echo "RE: $(nth_line 2 < "posts/${post_id}/1")" >> "${next_file}";
241 echo "${text}" >> "${next_file}";
242
243 # add post this is in reply to to posts cache
244 echo "${post_id}/${next_reply_id}" >> "users_lookup/${hashed}/posts";
245}
246
247 # vim: ft=wtf
248$ if contains 'user' ${!URL_PARAMS[@]} && file_exists "users/${URL_PARAMS['user']}"
249$ then
250$ local username=$(head -n 1 users/${URL_PARAMS['user']});
251$ echo "<h3>${username}'s posts:</h3>";
252$ echo "<ol>";
253$ get_users_posts "${username}" | while read -r post; do
254$ post_slug=$(awk -F/ '{print $2 "#" $3}' <<< "${post}");
255$ echo "<li><a href=\"/post.wtf?post=${post_slug}\">$(nth_line 2 "${post}" | htmlentities)</a></li>";
256$ done
257$ echo "</ol>";
258$ if is_logged_in && [[ "${COOKIES['USERNAME']}" = 'admin' ]] && [[ ${username} = 'admin' ]]
259$ then
260$ get_flag1
261$ fi
262$ fi
263
264 # vim: ft=wtf
265$ source user_functions.sh
266$ source post_functions.sh
267$ if [[ $method = 'POST' ]]
268$ then
269$ if is_logged_in
270$ then
271$ if [[ ${POST_PARAMS['text']} != '' && ${URL_PARAMS['post']} != '' ]]
272$ then
273$ reply "${URL_PARAMS['post']}" "${COOKIES['USERNAME']}" "${POST_PARAMS['text']}"
274$ redirect "/post.wtf?post=${URL_PARAMS['post']}#${next_post_id}";
275$ else
276$ redirect "/reply.wtf?post=${URL_PARAMS['post']}";
277$ fi
278$ else
279$ redirect "/login.wtf"
280$ fi
281$ fi
282
283$ if [[ ${URL_PARAMS['post']} = '' ]]
284$ then
285$ echo "u need to be replying to a post, you dumdum";
286$ fi
287
288$ if is_logged_in
289$ then
290$ echo "u r logged in, s0 gib reply"
291$ else
292$ redirect "/login.wtf"
293$ fi
294
295#!/usr/bin/env bash
296cp -R /opt/wtf.sh /tmp/wtf_runtime;
297
298# protect our stuff
299chmod -R 555 /tmp/wtf_runtime/wtf.sh/*.wtf;
300chmod -R 555 /tmp/wtf_runtime/wtf.sh/*.sh;
301chmod 777 /tmp/wtf_runtime/wtf.sh/;
302
303# set all dirs we could want to write into to be owned by www
304# (We don't do whole webroot since we want the people to be able to create
305# files in webroot, but not overwrite existing files)
306chmod -R 777 /tmp/wtf_runtime/wtf.sh/posts/;
307chown -R www:www /tmp/wtf_runtime/wtf.sh/posts/;
308
309chmod -R 777 /tmp/wtf_runtime/wtf.sh/users/;
310chown -R www:www /tmp/wtf_runtime/wtf.sh/users/;
311
312chmod -R 777 /tmp/wtf_runtime/wtf.sh/users_lookup/;
313chown -R www:www /tmp/wtf_runtime/wtf.sh/users_lookup/;
314
315# let's get this party started!
316su www -c "/tmp/wtf_runtime/wtf.sh/wtf.sh 8000";</span>
317</div>
318
319#!/usr/local/bin/bash
320function hash_password {
321 local password=$1;
322 (shasum <<< ${password}) | cut -d\ -f1;
323}
324
325# hash usernames for lookup in the users_lookup table
326function hash_username {
327 local username=$1;
328 (shasum <<< ${username}) | cut -d\ -f1;
329}
330
331# generate a random token, base64 encoded
332# on GNU base64 wraps at 76 characters, so we need to pass --wrap=0
333function generate_token {
334 (head -c 64 | (base64 --wrap=0 || base64)) < /dev/urandom 2> /dev/null;
335}
336
337function find_user_file {
338 local username=$1;
339 local hashed=$(hash_username "${username}");
340 local f;
341 if [[ -n "${username}" && -e "users_lookup/${hashed}" ]]
342 then
343 echo "users/$(cat "users_lookup/${hashed}/userid")";
344 else
345 echo "NONE"; # our failure case -- ugly but w/e...
346 fi;
347 return;
348}
349
350# The caller is responsible for checking that the user doesn't exist already calling this
351function create_user {
352 local username=$1;
353 local password=$2;
354 local hashed_pass=$(hash_password ${password});
355 local hashed_username=$(hash_username "${username}");
356 local token=$(generate_token);
357
358 mkdir users 2> /dev/null; # make sure users directory exists
359 touch users/.nolist; # make sure that the users dir can't be listed
360 touch users/.noread; # don't allow reading of user files directly
361
362 mkdir users_lookup 2> /dev/null; # make sure the username -> userid lookup directory exists
363 touch users_lookup/.nolist; # don't let it be listed
364
365 local user_id=$(basename $(mktemp users/XXXXX));
366
367
368 # user files look like:
369 # username
370 # hashed_pass
371 # token
372 echo "${username}" > "users/${user_id}";
373 echo "${hashed_pass}" >> "users/${user_id}";
374 echo "${token}" >> "users/${user_id}";
375
376
377 mkdir "users_lookup/${hashed_username}" 2> /dev/null;
378 touch "users_lookup/${hashed_username}/.nolist"; # lookup dir for this user can't be readable
379 touch "users_lookup/${hashed_username}/.noread"; # don't allow reading the lookup dir
380 touch "users_lookup/${hashed_username}/posts"; # lookup for posts this user has participated in
381 echo "${user_id}" > "users_lookup/${hashed_username}/userid"; # create reverse lookup
382
383 echo ${user_id};
384}
385
386function check_password {
387 local username=$1;
388 local password=$2;
389 local userfile=$(find_user_file ${username});
390
391 if [[ ${userfile} = 'NONE' ]]
392 then
393 return 1;
394 fi
395
396 local hashed_pass=$(hash_password ${password});
397 local correct_hash=$(head -n2 ${userfile} | tail -n1);
398 [[ ${hashed_pass} = ${correct_hash} ]];
399 return $?;
400}
401
402function is_logged_in {
403 contains 'TOKEN' ${!COOKIES[@]} && contains 'USERNAME' ${!COOKIES[@]};
404 local has_cookies=$?
405 local userfile=$(find_user_file ${COOKIES['USERNAME']});
406 [[ ${has_cookies} \
407 && ${userfile} != 'NONE' \
408 && $(tail -n1 ${userfile} 2>/dev/null) = ${COOKIES['TOKEN']} \
409 && $(head -n1 ${userfile} 2>/dev/null) = ${COOKIES['USERNAME']} \
410 ]];
411 return $?;
412}
413
414function get_users_posts {
415 local username=$1;
416 local hashed=$(hash_username "${username}");
417 # we only have to iterate over posts a user has replied to
418 while read -r post_id; do
419 echo "posts/${post_id}";
420 done < "users_lookup/${hashed}/posts";
421}
422
423
424#!/usr/bin/env bash
425# config stuff
426PROCESS_LIMIT=512 # per connection
427PROFILE=false
428
429# shell options
430shopt -s extglob;
431
432# ~~ PROFILING ~~
433if [[ $PROFILE = true ]]
434then
435 PS4='+ $(date "+%s.%N") $(if [[ ${FUNCNAME} = "" ]]; then echo NONE; else echo "${FUNCNAME}"; fi ) ${LINENO}\011 '
436 exec 3>&2 2>/tmp/bashprof.$$.log
437 set -x
438fi
439
440# sick facts about bash
441declare -a BASH_FACTS=(
442 $'Bash has an `until` keyword, which is equivalent to `while not`.'
443 $'Single and Double quotes do different things in bash -- single quotes do not interpolate variables, while double quotes do.'
444 $'When globbing on arrays in bash, you have the option to use [*] and [@], which appear to both return all the elements of the array. However, [*] acts like a "splat operator", while [@] keeps all everything constrained to the same argument.'
445 $'The bash array access syntax looks like ${array[$idx]}.'
446 $'If you forget the brackets in an array access, bash will just return the first element of the array.'
447 $'Bash didn\'t have Associative Arrays until Bash 4'
448 $'The idomatic way of iterating over all the lines in a file in bash is `while read -r line; do <something with line>; done < <filename>`'
449 $'Loops are just commands. So, you can pipe things into and out of them!'
450 );
451
452source lib.sh # import stdlib
453
454VERSION="0.0.0.0.1 \"alphaest of bets\""
455declare -a REPLY_HEADERS=(
456 "X-Powered-By: wtf.sh ${VERSION}" # Fly the banner of wtf.sh proudly!
457 "X-Bash-Fact: $(shuf --head-count=1 -e "${BASH_FACTS[@]}")" # select a random BASH FACT to include
458);
459
460declare -A URL_PARAMS # hashtable of url parameters
461declare -A POST_PARAMS # hashtable of post parameters
462declare -A HTTP_HEADERS # hashtable of http headers
463declare -A COOKIES # hashtable of cookies
464
465
466
467function log {
468 echo "[`date`] $@" 1>&9
469}
470
471urldecode() {
472 # urldecode <string>
473
474 local url_encoded="${1//+/ }"
475 printf '%b' "${url_encoded//%/\\x}"
476}
477
478max_page_include_depth=64
479page_include_depth=0
480function include_page {
481 # include_page <pathname>
482 local pathname=$1
483 local cmd=""
484 [[ "${pathname:(-4)}" = '.wtf' ]];
485 local can_execute=$?;
486 page_include_depth=$(($page_include_depth+1))
487 if [[ $page_include_depth -lt $max_page_include_depth ]]
488 then
489 local line;
490 while read -r line; do
491 # check if we're in a script line or not ($ at the beginning implies script line)
492 # also, our extension needs to be .wtf
493 [[ "$" = "${line:0:1}" && ${can_execute} = 0 ]];
494 is_script=$?;
495
496 # execute the line.
497 if [[ $is_script = 0 ]]
498 then
499 cmd+=$'\n'"${line#"$"}";
500 else
501 if [[ -n $cmd ]]
502 then
503 eval "$cmd" || log "Error during execution of ${cmd}";
504 cmd=""
505 fi
506 echo $line
507 fi
508 done < ${pathname}
509 else
510 echo "<p>Max include depth exceeded!<p>"
511 fi
512}
513
514function parse_headers {
515 while read -r line; do
516 if [[ $line = $'\r' || $line == $'\n' ]]
517 then
518 break
519 else
520 a=($line)
521 key=${a[0]%?}
522 value=${a[@]:1}
523 HTTP_HEADERS[$key]=${value:0:-1}; # remove \r from end
524 fi
525 done
526}
527
528function parse_cookies {
529 while read -d ';' -r cookie; do
530 local key=$(cut -d\= -f1 <<< "${cookie}");
531 local value=${cookie#*=};
532 COOKIES[${key}]=${value};
533 done <<< "${HTTP_HEADERS['Cookie']};" # append a ; so we still get the last field -- read drops the last thing >_<
534}
535
536function handle_connection {
537 ulimit -u "${PROCESS_LIMIT}"; # limit num processes per connection
538
539 # Parse query and any url parameters that may be in the path
540 IFS=' ' read -r method path version
541
542 # fast fail on empty request
543 if [[ ${method} = '' ]]
544 then
545 return
546 fi
547
548 query=${path##*\?};
549 if [[ $query != $path ]]
550 then
551 while read -d '&' -r param; do
552 IFS='=' read key value <<< ${param};
553 URL_PARAMS[$key]=$(urldecode $value)
554 done <<< "${query}&" # add & so last argument is seen
555 fi
556
557 request=("$method" "$path" "$version")
558 path=$(urldecode $(cut -d\? -f1 <<< "${path}")) # strip url parameters, urldecode
559
560 # parse headers
561 parse_headers;
562
563 # parse out cookie values, if they exist
564 if contains "Cookie" "${!HTTP_HEADERS[@]}"
565 then
566 parse_cookies;
567 fi
568
569 if [[ $method == "POST" ]]
570 then
571 # TODO: handle multipart bodies
572 local line;
573 local n;
574 n=${HTTP_HEADERS['Content-Length']};
575 read -n$n -r line;
576 params=($(sed "s/\&/ /g" <<< "${line}"))
577 for param in ${params[@]}; do
578 IFS='=' read key value <<< ${param};
579 POST_PARAMS[$key]=$(urldecode $value)
580 done
581 fi
582
583 # if we know the IP (via an X-Forwarded-For header), stick the user in a sandbox
584 # (Cloudflare will fill in this IP in prod, we can also have nginx fill it in in dev if we want)
585 if contains "X-Forwarded-For" "${!HTTP_HEADERS[@]}"
586 then
587 sandbox_dir="$((cksum <<< ${HTTP_HEADERS["X-Forwarded-For"]}) | cut -d\ -f1).sandbox";
588 # create sandbox if it doesn't exist
589 if [[ ! -e "${sandbox_dir}" ]]
590 then
591 mkdir "${sandbox_dir}";
592 # copy anything that isn't itself a sandbox to the dir
593 cp -R !(*.sandbox) "${sandbox_dir}";
594 fi
595 cd "${sandbox_dir}";
596 else
597 log "WARNING: Not sandboxing: no X-Forwarded-For header found!"
598 fi
599
600 requested_path=$(pwd)/${path}
601
602 # if a directory is requested, try each of the following, in order
603 # index.wtf, index.html
604 local index_fills=("index.wtf" "index.html");
605 if [[ -d ${requested_path} ]]
606 then
607 for i in ${index_fills}; do
608 if [[ -e "${requested_path}/${i}" ]]
609 then
610 requested_path="${requested_path}/${i}";
611 break;
612 fi
613 done
614 fi
615
616 # check for possible directory traversals / other undesirable path elements by
617 # removing them and 503-ing if the string was changed
618 test_path=$(sed "s/\.\.//g" <<< "${requested_path}")
619 if [[ ${test_path} != ${requested_path} ]]
620 then
621 echo "HTTP/1.1 503 Forbidden"
622 echo "Content-Type: text/html"
623 for reply_header in "${REPLY_HEADERS[@]}"; do
624 echo "${reply_header}"
625 done
626 printf "\r\n\r\n"
627 echo "<html><title>503</title><body>503 Forbidden</body></html>"
628 echo "<p>Sorry, directory traversal is strongly frowned upon here at wtf.sh enterprises</p>";
629 log "503: ${request[@]}"
630 exit 0; # terminate early for 503
631 fi
632
633 [[ ! -e "${requested_path}/.nolist" ]];
634 local can_list=$?;
635 [[ ! -e "$(dirname "${requested_path}")/.noread" ]];
636 local can_read=$?;
637
638 if [[ -e ${requested_path} ]]
639 then
640 if [[ -f ${requested_path} \
641 && ${requested_path:(-4)} != ".log"\
642 && ${can_read} = 0 ]] # can't end in .log, can't have .noread in the parent directory
643 then
644 echo "HTTP/1.1 200 OK"
645 echo "Content-Type: text/html"
646 for reply_header in "${REPLY_HEADERS[@]}"; do
647 echo "${reply_header}"
648 done
649 printf "\r\n\r\n"
650 include_page ${requested_path};
651 elif [[ -d ${requested_path} \
652 && ${can_list} = 0 ]] # handle directory listing if it isn't a file and no `.nolist` file in the directory
653 then
654 log "$(dirname "${requested_path}")/.noread"
655 echo "HTTP/1.1 200 OK"
656 echo "Content-Type: text/html"
657 for reply_header in "${REPLY_HEADERS[@]}"; do
658 echo "${reply_header}"
659 done
660 printf "\r\n\r\n"
661 echo "<h3>Index of ${request[1]}</h3>"
662 echo "<ul>"
663 for d in ${requested_path}/*; do
664 size_info=($(du -h ${requested_path} | tail -n 1))
665 echo "<li><a href="/${request[1]#"/"}${d}">${d}</a>: ${size_info[0]}</li>"
666 done
667 echo "</ul>"
668 echo "<font size=2>generated by wtf.sh ${VERSION} on $(date)</font>"
669 else
670 echo "HTTP/1.1 503 Forbidden"
671 echo "Content-Type: text/html"
672 for reply_header in "${REPLY_HEADERS[@]}"; do
673 echo "${reply_header}"
674 done
675 printf "\r\n\r\n"
676 echo "<title>503 Forbidden</title>";
677 echo "<h3>I'm sorry, I'm afraid I can't let you see that</h3>";
678 echo "<p>It seems that you tried to list a directory with a <code>.nolist</code> file in it, or a <code>.noread</code> file in it's parent, or a forbidden file type.</p>";
679 echo "<p>If you think this was a mistake, I feel bad for you, son. I got 99 problems, but a 503 ain't one.</p>";
680 log "503: ${request[@]}"
681 exit 0;
682 fi
683 log "200: ${request[@]}"
684 exit 0
685 else
686 # If we were noread or nolist, send a 503, even though the resource doesn't even exist -- we don't want to leak what forbidden resources do and do not exist
687 if [[ ${can_read} = 1 || ${can_list} = 1 ]];
688 then
689 echo "HTTP/1.1 503 Not Found";
690 echo "Content-Type: text/html"
691 for reply_header in "${REPLY_HEADERS[@]}"; do
692 echo "${reply_header}"
693 done
694 printf "\r\n\r\n"
695 echo "<title>503 Forbidden</title>";
696 echo "<h3>I'm sorry, I'm afraid I can't let you see that</h3>";
697 echo "<p>It seems that you tried to list a directory with a <code>.nolist</code> file in it, or a <code>.noread</code> file in it's parent, or a forbidden file type.</p>";
698 echo "<p>If you think this was a mistake, I feel bad for you, son. I got 99 problems, but a 503 ain't one.</p>";
699 log "503: ${request[@]}"
700 else
701 echo "HTTP/1.1 404 Not Found"
702 echo "Content-Type: text/html"
703 for reply_header in "${REPLY_HEADERS[@]}"; do
704 echo "${reply_header}"
705 done
706 printf "\r\n\r\n"
707 echo "<html><title>404</title><body>404, not found:<code>${request[1]}</code></body></html>"
708 log "404: ${request[@]}"
709 fi
710 exit 0
711 fi
712}
713
714# start socat on specified port
715function start_server {
716 echo "wtf.sh ${VERSION}, starting!";
717 socat -T10 TCP-LISTEN:$2,fork,readbytes=4096,backlog=256,reuseaddr EXEC:"$1 -r" 9>&1
718 echo "Socket was occupied... try again later...";
719}
720
721if [[ $# != 1 ]]
722then
723 echo "Usage: $0 port"
724 exit
725fi
726
727if [[ $1 == '-r' ]]
728then
729 handle_connection
730else
731 start_server $0 $1 # start server on specified port
732fi</span>
733</div>