[
  {
    "path": ".gitignore",
    "content": "asmhttpd\nasmhttpd.o\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.,\n 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Lesser General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n                            NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year name of author\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  <signature of Ty Coon>, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.\n"
  },
  {
    "path": "MACROS",
    "content": "\nThe syscall() macro should be invoked as follows:\n\tsyscall(#,arg1,arg2,arg3,arg4,arg5,arg6)\n\nError checking is built into the macro. You _MUST_ define an error-handler.\nIf the syscall() macro is invoked without an error handler defined, the\nassembler will throw an error.\n\nYou define error handlers like this:\n\n\tech(label_for_your_error_code)\n\t; do shit\n\tech(other_label_for_stuff)\n\nThe error checking consists of:\n\n\tor rax,rax\n\tjs @ERROR_HANDLER\n\nSo, the FLAGS are set based on the error return of the syscall, which is nice.\n\nThe arguments are loaded with 32-bit 'mov' by default.\nYou can change that in the following ways:\n\t+arg\t: Load arg with a 64-bit 'mov'\n\t^arg\t: Load arg with a 32-bit 'mov' (redundant in this usage)\n\t^^arg\t: Load arg with a 16-bit 'mov'\n\t^^^arg\t: Load arg with an 8-bit 'mov'\n\t[arg]\t: Load arg with 'lea' (eg, \"lea rdi,[arg]\")\n\t+[arg]\t: Dereference arg and load (eg, \"mov rdi,[arg]\")\n\t^[arg]\n\t^^[arg]\n\t^^^[arg]\n\t[+arg]\t: Discard brackets and load with 'mov' (eg, \"mov rdi,arg\")\n\t[^arg]\t  (This functionality is stupid)\n\t[^^arg]\n\t[^^^arg]\n\tNULL\t: Make the argument NULL (Always 64-bit, eg \"xor rdi,rdi\")\n\nNote that the syscall number (\"#\" in the example) is also expanded in this fashion\n\n--\n\nIn our thread-based model, each thread calls mmap() to grab it's own memory,\nand sets %rbp the base of that memory.\n\nThe tmalloc(symbol,$bytes) macro \"reserves\" space in that memory. It defines\n'symbol' to be [rbp+bytes].\n\nSo, for example:\n\ntmalloc(crap,8)\ntmalloc(shit,16)\ntmalloc(moreshit,32)\n\ncrap is \"rbp\"\nshit is \"rbp+8\"\nmoreshit is \"rbp+24\"\n"
  },
  {
    "path": "Makefile",
    "content": "\nasmhttpd: asmhttpd.o\n\tld $^ -o $@\n\n%.o: %.asm\n\tnasm -f elf64 -O0 -o $@ $<\n\nclean:\n\trm -f *.o asmhttpd\n"
  },
  {
    "path": "README",
    "content": "asmhttpd - The tiniest webserver ever written\n\nHOW TO BUILD:\n-------------\n\n\tJust run \"make\". You will need NASM on your system.\n\nHOW TO RUN:\n-----------\n\n\t./asmhttpd /path/to/your/webroot\n\tsudo ./asmhttpd /path/to/your/webroot\n\nIf run as root, it will listen on port 80. Otherwise, it will use port 8080.\n\nHOW TINY IS IT?\n---------------\n\nThe entire text and data fit on a single 4K page:\n\n  {0}[calvin ~] cat /proc/$(pgrep -n asmhttpd)/maps\n  00401000-00402000 r-xp 00001000 00:16 2483272 asmhttpd\n\nEach client allocates an additional 4K page and a thread while connected.\n\nThe code is written in a monolithic \"branching tree\" style with no functions,\nand uses registers for all local variables. RAM is only used for buffering the\nHTTP request, and for building structures necessary for system calls.\n\nBecause there is no stack, and the targets of all branch instructions are\nconstants, traditional buffer overflow exploits are impossible.\n"
  },
  {
    "path": "asmhttpd.asm",
    "content": "; vim: syntax=nasm\n;\n; asmhttpd: A minimalist HTTP webserver for Linux, written in x86_64 assembly.\n;\n; Copyright (C) 2012 Calvin Owens <jcalvinowens@gmail.com>\n;\n; This program is free software; you can redistribute it and/or modify\n; it under the terms of the GNU General Public License as published by\n; the Free Software Foundation; either version 2 of the License, or\n; (at your option) any later version.\n;\n; This program is distributed in the hope that it will be useful,\n; but WITHOUT ANY WARRANTY; without even the implied warranty of\n; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n; GNU General Public License for more details.\n\n[bits 64]\nglobal _start\n\n%include \"constants.inc\"\t\t; Flags for system calls, etc.\n%include \"syscall.inc\"\t\t\t; syscall() definition\n\n%assign thread_memory_offset 0 ; This is rewritten each time tmalloc() is called\n%macro reserve_thread_memory 2\n\t%define %1 rbp+%[thread_memory_offset]\n\t%define len%1 %2\n\t%assign thread_memory_offset thread_memory_offset+%[%2]\n%endmacro\n%define tmalloc(a,b) reserve_thread_memory a,b\n\n%define GLOBAL_REQUEST_TIMEOUT 5\n%define GLOBAL_MAX_HTTP_REQLEN 1024\n%define THREAD_MEM_SIZE 4096\n%define CLIENT_TIMEOUT_MS 60000\n%define NAME_MAX 254\n\nsegment .text\n\nSEGMENT_BEGIN:\n\nSetsock_ARGUMENT:\n\tdd 0x00000001\t\t\t; Turn option ON\n\nSighand_SIGPIPE:\n\tdq 0x0000000000000001\t\t; SIG_IGN is 1\n\tdq 0x0000000000000000\n\nSocketAddress:\n\tdw 0x0002\t\t\t; AF_INET is 2 (AF_INET6 is 0xa)\n\tdw 0x5000\t\t\t; TCP Port we want to bind to (:80) (Net byte ordering)\n\tdd 0x00000000                   ; Address to bind to (0.0.0.0 or ::)\n\tdd 0x00000000\n\tdd 0x00000000\n\tdd 0x00000000\n\tdd 0x00000000\n\nSocketAddressNotRoot:\n\tdw 0x0002\t\t\t; AF_INET is 2 (AF_INET6 is 0xa)\n\tdw 0x901F\t\t\t; TCP Port we want to bind to (:8080) (Net byte ordering)\n\tdd 0x00000000                   ; Address to bind to (0.0.0.0 or ::)\n\tdd 0x00000000\n\tdd 0x00000000\n\tdd 0x00000000\n\tdd 0x00000000\n\nDecAsciiConvTable:\n\tdb 0x30,0x31,0x21,0x32,0x33,0x21,0x34,0x21,0x35,0x36,0x21,0x37,0x38,0x21,0x39,0x21\n\nResponse200:\n\tdb \"HTTP/1.1 200 OK\",0x0d,0x0a\n%define lenResponse200 17\nHeader12:\n\tdb \"Content-Length: \"\n%define lenHeader12 16\n\nError400:\n\tdb \"HTTP/1.1 400 Bad Request\",0x0d,0x0a,0x0d,0x0a\n%define lenError400 28\nError403:\n\tdb \"HTTP/1.1 403 Forbidden\",0x0d,0x0a,0x0d,0x0a\n%define lenError403 26\nError404:\n\tdb \"HTTP/1.1 404 Not Found\",0x0d,0x0a,0x0d,0x0a\n%define lenError404 26\nError408:\n\tdb \"HTTP/1.1 408 Request Timeout\",0x0d,0x0a,0x0d,0x0a\n%define lenError408 32\nError411:\n\tdb \"HTTP/1.1 411 Length Required\",0x0d,0x0a,0x0d,0x0a\n%define lenError411 32\nError413:\n\tdb \"HTTP/1.1 413 Request Entity Too Large\",0x0d,0x0a,0x0d,0x0a\n%define lenError413 41\nError414:\n\tdb \"HTTP/1.1 414 Request URI Too Long\",0x0d,0x0a,0x0d,0x0a\n%define lenError414 37\nError500:\n\tdb \"HTTP/1.1 500 Internal Server Error\",0x0d,0x0a,0x0d,0x0a\n%define lenError500 38\nError501:\n\tdb \"HTTP/1.1 501 Not Implemented\",0x0d,0x0a,0x0d,0x0a\n%define lenError501 32\n\nError599:\n\tdb \"HTTP/1.1 599 System Call Not Implemented\",0x0d,0x0a,0x0d,0x0a\n\tdb \"Your kernel is probably missing openat2(): Upgrade to >= 5.6\",0x0a\n%define lenError599 105\n\nHelpMessage:\n\tdb \"Usage: ./asmhttpd <webroot>\",0x0a\n%define lenHelpMessage 28\n\nech(____die)\t; Who shall catch the catchers?\n\nDieClientDisconnected:\nsyscall(sys_close,+rbx)\nsyscall(sys_munmap,+rbp,THREAD_MEM_SIZE)\nsyscall(sys_exit,-1)\n\nDieError403:\nlea rcx,[Error403]\nmov edx,lenError403\njmp __die\n\nDieError404:\nlea rcx,[Error404]\nmov edx,lenError404\njmp __die\n\nDieError414:\nlea rcx,[Error414]\nmov edx,lenError414\njmp __die\n\nDieError500:\nlea rcx,[Error500]\nmov edx,lenError500\njmp __die\n\nDieError408:\nlea rcx,[Error408]\nmov edx,lenError408\njmp __die\n\nDieError400:\nlea rcx,[Error400]\nmov edx,lenError400\njmp __die\n\nDieError599:\nlea rcx,[Error599]\nmov edx,lenError599\njmp __die\n\nPrintHelpMessageAndDie:\nsyscall(sys_write,2,[HelpMessage],lenHelpMessage)\njmp ____die\n\n__die:\nsyscall(sys_write,+rbx,+rcx,+rdx)\nsyscall(sys_close,+rbx)\nsyscall(sys_munmap,+rbp,THREAD_MEM_SIZE)\n____die:\nsyscall(sys_exit,-1);\n\nHandleInitSysError:\nHandleServeSysError:\nmov rbx,rax\nsyscall(sys_exit,+rbx)\n\nHandleGeneralSysErrorAfterOpen:\nsyscall(sys_close,+r13)\nHandleGeneralSysError:\ncmp eax,-EPERM\nje DieError403\t; Permission Denied\ncmp eax,-ENOENT\nje DieError404\t; File not found\ncmp eax,-EACCES\nje DieError403\t; Permission denied\ncmp eax,-ENAMETOOLONG\nje DieError414\t; URI too long (although, is it?)\ncmp eax,-EPIPE\nje DieClientDisconnected\ncmp eax,-ENOSYS\nje DieError599\n\njmp DieError500\n\n_start:\n\nech(HandleInitSysError)\n\n; Parse command line arguments. At the start of the program, [rsp] contains a\n; qword that tells us how many arguments we got, and [rsp+8] is the beginning of\n; a list of pointers to the NUL-terminated argument strings.\n\n; Make sure we have 2 arguments:\n;\t0:\t[rsp+8*1]\tExecutable name (we ignore this)\n;\t1:\t[rsp+8*2]\tWebroot\nmov r11,[rsp]\ncmp r11,2\njne PrintHelpMessageAndDie\n\nsyscall(sys_open,+[rsp+8*2],O_DIRECTORY,NULL)\nmov r15,rax\t; Save webroot file descriptor\n\n; Ignore SIGPIPE\nsyscall(sys_rt_sigact,13,[Sighand_SIGPIPE],NULL,8)\n\n; Create and set SO_REUSEADDR on the socket\nsyscall(sys_socket,2,1,NULL)\nmov rbx,rax\n\nsyscall(sys_setsockopt,+rbx,SOL_SOCKET,SO_REUSEADDR,[Setsock_ARGUMENT],4)\nsyscall(sys_getuid)\ntest rax,rax\njnz .NotRoot\n\n; Bind, and drop privs (nobody)\nsyscall(sys_bind,+rbx,[SocketAddress],24)\nsyscall(sys_setgid,65535)\nsyscall(sys_setuid)\njmp .OverNotRoot\n\n.NotRoot:\nsyscall(sys_bind,+rbx,[SocketAddressNotRoot],24)\n\n.OverNotRoot:\nsyscall(sys_listen,+rbx,1024)\n\n; Now, we can unmap everything but our single page, including the stack!\n; If you use L5 pagetables, change the shift by 47 to 56\n\nlea rdi,[SEGMENT_BEGIN]\nshr rdi,12\ninc rdi\nshl rdi,12\n\n; Unmap everything after .text\nmov rsi,1\nshl rsi,47\nsub rsi,rdi\nsub rsi,4096\t; Highest page is off-limits\nsyscall(sys_munmap)\n\n; Unmap everything before .text\nmov rsi,rdi\nshr rsi,12\ndec rsi\nshl rsi,12\ndec rsi\nxor rdi,rdi\nsyscall(sys_munmap)\n\nServeHTTP:\n\nech(HandleServeSysError)\n\nmov r8,rbx ; file descriptor for listening socket\nmov r9,CLONE_FS|CLONE_THREAD|CLONE_SIGHAND|CLONE_VM|CLONE_FILES\nmov r10d,sys_accept\nmov r12d,sys_clone\nxor rsi,rsi\nxor rdx,rdx\n\n.AcceptNextRequest:\n\nsyscall(+r10,+r8)\nmov rbx,rax\nsyscall(+r12,+r9)\njnz .AcceptNextRequest\n\n;; (rbx contains client connection file descriptor)\n\nHandleConnection:\nech(HandleGeneralSysError)\n\nsyscall(sys_mmap,NULL,THREAD_MEM_SIZE,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS|MAP_POPULATE,NULL,NULL)\nmov rbp,rax\t; Store offset for tmalloc() macro\n\ntmalloc(PollStruct,8)\n\n; Build a struct pollfd in memory\nmov [PollStruct],rbx\nmov dword [PollStruct+4],POLLIN\n\ntmalloc(ClientRequest,GLOBAL_MAX_HTTP_REQLEN)\n\n; Load offset and counter for receieve loop\nlea r9,[ClientRequest]\nmov r10d,GLOBAL_MAX_HTTP_REQLEN\n\nReceiveRequest:\n\nsyscall(sys_poll,[+PollStruct],1,CLIENT_TIMEOUT_MS)\njz DieError408\n\nsyscall(sys_read,+rbx,+r9,+r10)\njz DieClientDisconnected\n\n; Did we get enough data to be valid?\nmov ecx,GLOBAL_MAX_HTTP_REQLEN\nsub rcx,r10\nadd rcx,rax\ncmp rcx,18\njge GotEnough\n\n; Need to wait for more, adjust offsets and block again\nadd r9,rax\nsub r10,rax\njmp ReceiveRequest\n\nGotEnough:\nmov esi,0x0a0d0a0d\nlea rdi,[r9+rax-4]\nlea r8,[ClientRequest]\n\n.FindTerminator:\ncmp esi,dword [rdi]\nje short .FoundTerminator\n\ndec rdi\t\t; If we haven't reached the beginning...\ncmp rdi,r8\t; ...then keep looking\njne short .FindTerminator\n\nadd r9,rax\t; Adjust offset and counter\nsub r10,rax\njz DieError414\t; If zero, send 412 REQUEST URI TOO LARGE\n\njmp ReceiveRequest\t; Go back up and wait for more data\n\n.FoundTerminator:\nlea r14,[r9+rax]\t; Store last byte received from client\n\n; Skip past the METHOD specification, find the start of the URI:\nmov eax,0x20\nlea rdi,[ClientRequest]\nmov ecx,20\nrepne scasb\njne DieError400\t\t; Go thou and fuck with me no more\n\nmov r13,rdi\n\n; NULL-terminate the filename\nmov ecx,GLOBAL_MAX_HTTP_REQLEN\nrepne scasb\njne DieError400\t\t; This should never happen\n\nmov byte [rdi-1],0x00\nlea r12,[rdi-1]\t\t; Store addr of last byte of URI\n\nlea r8,[rbp+thread_memory_offset]\t; Load scratch address, to store offsets of escaped %xx literals\nxor r10,r10\t; Zero index registers\nxor r9,r9\nmov eax,0x25\t; '%' in ASCII\nmov rcx,r12\t; Counter for 'repne scasb'...\nsub rcx,r13\t; ...subtract from end to get length\ncmp rcx,3\t; If there are less than 3 characters...\njl ExpandDone\t; ...there can't be anything to unescape\nmov rdi,r13\t; Beginning of URI\n\nExpandChar:\nrepne scasb\t; Search for a '%' character\njne ExpandOut\t; ...if we didn't find one, we're done\ncmp rcx,1\t; If there are less than 2 characters left...\njle ExpandOut\t; ...don't try to unescape\nmovzx rdx,word [rdi]\t; Pull the ASCII-encoded hex byte into dx\nror dl,4\t; You are not expected to understand this\nror dx,4\nror dl,4\nand dh,0x44\nshr dh,2\nadd dl,dh\nshl dh,3\nor dl,dh\nmov [rdi-1],dl\t; Overwrite the '%' character with the newly-unescaped byte\nmov word [rdi],r10w\t; Write NULL's over the just-unescaped ASCII-encoded hex\nmov [r8+r9*8],rdi\t; Add the address to our list of unescaped literals\ninc r9\t; Increment the counter\njmp short ExpandChar\t; ...and do it again\n\nExpandOut:\nor r9,r9\t; If r9 is zero...\njz ExpandDone\t; ...then there was nothing to un-escape - jump over copying\nmov [r8+r9*8],rdi\t; Store the offset of the last byte of the URI\n\nxor rax,rax\t; Zero out rax (index register for the loop)\nmov rdi,[r8]\t; Get the offset of the first un-escaped literal\nmov rsi,rdi\t; rsi holds the destination\n\nDoCopying:\nadd rsi,2\t; Jump over unescaped xx\nmov rcx,[r8+rax*8+8]\t; Load the offset of the next escapee\nsub rcx,rsi\t; Subtract where we are now to get how far to copy\nrep movsb\t; Boom\ninc rax\t; Increment the idex register\ncmp rax,r9\t; If we're not done...\njne DoCopying\t; ...do it again\n\nmov rcx,r9\t; Use the number of %xx's we unescaped as a counter\nxor dx,dx\t; Zero dx so we can write NULL words\nmov [rdi],dx\t; NUL terminate the (now shorter) string\n\nExpandDone:\n\ntmalloc(OpenAtStruct,24)\nmov qword [OpenAtStruct],0\nmov qword [OpenAtStruct+8],0\nmov qword [OpenAtStruct+16],RESOLVE_IN_ROOT\nsyscall(sys_openat2,+r15,+r13,[OpenAtStruct],lenOpenAtStruct)\nmov r13,rax\t; Save file descriptor\n\nech(HandleGeneralSysErrorAfterOpen)\t; Now if we crash we have to close the file to\n\t\t\t\t\t; avoid leaking file descriptors\n\ntmalloc(StatStruct,144)\n\nsyscall(sys_fstat,+r13,[StatStruct])\n\n; Make sure we have a regular file or a symlink\nmov r12w,[StatStruct+24]\t; Get the file mode\nshr r12w,14\ncmp r12w,2\njne DieError403\n\nmov r12,[StatStruct+48]\t; Get the file size out of the struct from stat()\n\nlea rsp,[rbp+thread_memory_offset]\t; Get scratch space, store it in rsp for awhile\n\n; Convert the filesize to ASCII\nmov rax,r12\t\t\t; The file's length to convert (as an unsigned quadword)\nmov ecx,16\t\t\t; Counter for the conversion loop\nlea rsi,[DecAsciiConvTable]\t; Address of table for ASCII conversion\nmov r8,0x199999999999999a\t; Multiplicative inverse of 10d (0xa)\nxor r9,r9\t\t\t; Zero out registers for the ASCII string\nxor r10,r10\n\nDivisionLoop:\n\tshld r9,r10,8\t\t; Shift the most significant byte of r10 into r9\n\tshl r10,8\t\t; (shld only shifts in, not out, so we have to discard the byte)\n\tmul r8\t\t\t; Execute the division\n\tshr rax,60\t\t; Shift the most significant byte of the remainder to the least\n\tmovzx rax,byte [rsi+rax]; Load the value from our conversion table\n\tor r10,rax\t\t; Store the new value in r10\n\tmov rax,rdx\t\t; Restore the working quotient for the next iteration\n\tloop DivisionLoop\t; ...and do it again\n\nmov [rsp],r10\t\t\t; Store the ASCII decimal value in memory\nmov [rsp+8],r9\n\nmov eax,0x30\t; Skip over the leading zeros (they break stupid browsers...)\nmov ecx,16\nmov rdi,rsp\nrepe scasb\n\nmov r11,[rdi-1]\t; Move the significant values back to the beginning of [rsp]\nmov [rsp],r11\nmov r11,[rdi-1+8]\nmov [rsp+8],r11\n\nlea rsp,[rsp+rcx+1]\t; Skip past the ASCII number\n\nmov rdi,rsp\t; Copy to the scratch area...\nmov rsi,Response200\t; ...from the canned HTTP 200 response\nmov ecx,lenResponse200\t; Size\nrep movsb\t; Boom\n\nmov rsi,Header12\t; Copy the \"Content-Length: \" header\nmov ecx,lenHeader12\t; Size\nrep movsb\t; Boom\n\nlea rsi,[rbp+thread_memory_offset]\t; Load rsi with the address of the ASCII-expressed file size\nmov rcx,rsp\t; Get the address of the start of the send buffer\nsub rcx,rsi\t; Subtract the offset of the size from the offset of the buffer (strlen(num))\nrep movsb\t; ...and copy the difference in bytes to the header field\n\nmov dword [rdi], 0x0a0d0a0d\t; Terminate the HTTP response\n\nlea rdx,[rdi+4]\t; Address from which to send response plus 4...\nsub rdx,rsp\t; ...and subtract the end to get the length\nsyscall(sys_sendto,+rbx,[+rsp],+rdx,MSG_MORE,NULL,NULL)\n\nkeep_sending:\nsyscall(sys_sendfile,+rbx,+r13,NULL,+r12)\nsub r12,rax\njnz keep_sending\n\n; Close file descriptor and socket\nsyscall(sys_close,+r13)\nsyscall(sys_close,+rbx)\n\n; Deallocate thread-local memory and exit\nsyscall(sys_munmap,+rbp,THREAD_MEM_SIZE)\nsyscall(sys_exit,NULL)\n\nud2\t; SIGILL\n"
  },
  {
    "path": "constants.inc",
    "content": ";; syscalls\n%define sys_accept\t43\n%define sys_bind\t49\n%define sys_chdir\t80\n%define sys_chroot\t161\n%define sys_clone\t56\n%define sys_close\t3\n%define sys_exit\t60\n%define sys_fstat\t5\n%define sys_getuid\t102\n%define sys_listen\t50\n%define sys_mmap\t9\n%define sys_munmap\t11\n%define sys_open\t2\n%define sys_openat2\t437\n%define sys_poll\t7\n%define sys_read\t0\n%define sys_rt_sigact\t13\n%define sys_sendfile\t40\n%define sys_sendto\t44\n%define sys_setgid\t106\n%define sys_setsockopt\t54\n%define sys_setuid\t105\n%define sys_socket\t41\n%define sys_write\t1\n\n;; errno\n%define\tEPERM\t\t1;\t/* Operation not permitted */\n%define\tENOENT\t\t2;\t/* No such file or directory */\n%define\tEACCES\t\t13;\t/* Permission denied */\n%define\tEPIPE\t\t32;\t/* Broken pipe */\n%define\tENAMETOOLONG\t36;\t/* File name too long */\n%define\tENOSYS\t\t38;\t/* Invalid system call number */\n\n;; For clone()\n%define CLONE_VM\t0x00000100; /* Set if VM shared between processes.  */\n%define CLONE_FS\t0x00000200; /* Set if fs info shared between processes.  */\n%define CLONE_FILES\t0x00000400; /* Set if open files shared between processes.  */\n%define CLONE_SIGHAND\t0x00000800; /* Set if signal handlers shared.  */\n%define CLONE_THREAD\t0x00010000; /* Set to add to same thread group.  */\n\n;; For mmap()\n%define PROT_READ\t0x1;\t\t/* Page can be read.  */\n%define PROT_WRITE\t0x2;\t\t/* Page can be written.  */\n%define MAP_PRIVATE\t0x02;\t\t/* Changes are private.  */\n%define MAP_ANONYMOUS\t0x20;\t\t/* Don't use a file.  */\n%define MAP_POPULATE\t0x08000;\t/* Populate (prefault) pagetables.  */\n\n;; For poll()\n%define POLLIN\t\t0x1;\t\t/* There is data to read */\n\n;; For sendto()\n%define MSG_MORE\t0x8000;\t\t/* TCP_CORK behavior */\n\n;; For setsockopt()\n%define SOL_SOCKET\t1;\n%define SO_REUSEADDR\t2;\n\n;; For open()\n%define O_DIRECTORY     0x10000;\n\n;; For openat2()\n%define RESOLVE_IN_ROOT\t0x10;\n"
  },
  {
    "path": "syscall.inc",
    "content": "; See the MACROS file for a description of how this monstrosity works\n\n;; r64,r32,r16,r8,arg\n%macro load_syscall_argument 0-*\n\t%if %0 == 4\n\t\t; Empty Argument\n\t%elif %0 == 5\n\t\t%defstr ___arg_str %[%5]\n\t\t%substr ___arg_c ___arg_str 1\n\t\t\t%if ___arg_c == '['\n\t\t\t%substr ___arg_c2 ___arg_str 2\n\t\t\t%if ___arg_c2 == \"+\"\n\t\t\t\t%substr ___arg_n ___arg_str 3,-2\n\t\t\t\t%deftok ___arg_n_t %[___arg_n]\n\t\t\t\tmov %1,___arg_n_t\n\t\t\t\t%undef ___arg_n_t\n\t\t\t\t%undef ___arg_n\n\t\t\t%else\n\t\t\t\tlea %1,%5\n\t\t\t%endif\n\t\t\t%undef ___arg_c2\n\t\t%elif ___arg_c == '+'\n\t\t\t%substr ___arg_n ___arg_str 2,-1\n\t\t\t%deftok ___arg_n_t %[___arg_n]\n\t\t\tmov %1,___arg_n_t\n\t\t\t%undef ___arg_n_t\n\t\t\t%undef ___arg_n\n\t\t%elif ___arg_c == '^'\n\t\t\t%define ___forced_imm_bit_len 32\n\t\t\t%substr ___arg_c2 ___arg_str 1,2\n\t\t\t%if ___arg_c2 == \"^^\"\n\t\t\t\t%define ___forced_imm_bit_len 16\n\t\t\t\t%substr ___arg_c3 ___arg_str 1,3\n\t\t\t\t%if ___arg_c3 == \"^^^\"\n\t\t\t\t\t%define ___forced_imm_bit_len 8\n\t\t\t\t%endif\n\t\t\t\t%undef ___arg_c3\n\t\t\t%endif\n\t\t\t%undef ___arg_c2\n\t\t\t%if ___forced_imm_bit_len == 32\n\t\t\t\t%substr ___arg_n ___arg_str 2,-1\n\t\t\t\t%deftok ___arg_n_t %[___arg_n]\n\t\t\t\t%substr ___arg_n_c ___arg_n 1,1\n\t\t\t\t%if ___arg_n_c != '['\n\t\t\t\t\t%warning \"^thing is redundant in SYSCALL: thing is interpreted as 32bit by default\" \n\t\t\t\t%endif\n\t\t\t\tmov %2,___arg_n_t\n\t\t\t\t%undef ___arg_n_t\n\t\t\t\t%undef ___arg_n\n\t\t\t%elif ___forced_imm_bit_len == 16\n\t\t\t\t%substr ___arg_n ___arg_str 3,-1\n\t\t\t\t%deftok ___arg_n_t %[___arg_n]\n\t\t\t\tmov %3,___arg_n_t\n\t\t\t\t%undef ___arg_n_t\n\t\t\t\t%undef ___arg_n\n\t\t\t%elif ___forced_imm_bit_len == 8\n\t\t\t\t%substr ___arg_n ___arg_str 4,-1\n\t\t\t\t%deftok ___arg_n_t %[___arg_n]\n\t\t\t\tmov %4,___arg_n_t\n\t\t\t\t%undef ___arg_n_t\n\t\t\t\t%undef ___arg_n\n\t\t\t%endif\n\t\t\t%undef ___forced_imm_bit_len\n\t\t%elif ___arg_str == 'NULL'\n\t\t\txor %1,%1\n\t\t%else\n\t\t\tmov %2,%5\n\t\t%endif\n\t\t%undef ___arg_c\n\t\t%undef ___arg_str\n\t%else\n\t\t%fatal \"You fucked up the SYSCALL-load-arguments macro!\"\n\t%endif\n%endmacro\n\n%macro macro_do_syscall 1-7\n\tload_syscall_argument rax,eax,ax,al,%1\n\t%if %0 >= 2\n\tload_syscall_argument rdi,edi,di,dil,%2\n\t%endif\n\t%if %0 >= 3\n\tload_syscall_argument rsi,esi,si,sil,%3\n\t%endif\n\t%if %0 >= 4\n\tload_syscall_argument rdx,edx,dx,dl,%4\n\t%endif\n\t%if %0 >= 5\n\tload_syscall_argument r10,r10d,r10w,r10b,%5\n\t%endif\n\t%if %0 >= 6\n\tload_syscall_argument r8,r8d,r8w,r8b,%6\n\t%endif\n\t%if %0 >= 7\n\tload_syscall_argument r9,r9d,r9w,r9b,%7\n\t%endif\n\tsyscall\t; Invoke loadall286+\n\t%ifdef __syserror_handler\n\t\tor rax,rax ; Set flags based on the return value\n\t\tjs __syserror_handler\n\t%else\n\t\t%fatal \"__syserror_handler is not defined!\"\n\t%endif\n%endmacro ;; Yo dawg, I heard you liked macros...\n%define syscall(a,b,c,d,e,f,g) macro_do_syscall a,b,c,d,e,f,g\n%define syscall(a,b,c,d,e,f) macro_do_syscall a,b,c,d,e,f\n%define syscall(a,b,c,d,e) macro_do_syscall a,b,c,d,e\n%define syscall(a,b,c,d) macro_do_syscall a,b,c,d\n%define syscall(a,b,c) macro_do_syscall a,b,c\n%define syscall(a,b) macro_do_syscall a,b\n%define syscall(a) macro_do_syscall a\n\n%macro change_syscall_error_handler 1\n\t%define __syserror_handler %1\n%endmacro\n%define ech(a) change_syscall_error_handler a\n"
  }
]