├── .gitignore ├── LICENSE ├── README.md ├── docker ├── .dockerignore ├── Dockerfile ├── Dockerfile.minimal ├── Makefile ├── httpd ├── index.htm ├── index.html └── pi_armed_with_docker.jpg ├── src ├── .gitignore ├── FasmArm.inc ├── fasmarm └── httpd.fasm └── start-webservers.sh /.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hypriot/rpi-nano-httpd/ab5ab3eb3d536047ec597f08b2543bed08bc3b21/.gitignore -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Hypriot 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rpi-nano-httpd 2 | 3 | A nano sized web server packed into a Docker Nano Container 4 | 5 | ## Step 1: Compile the assembly source code 6 | 7 | Compiling the ASM source to a statically linked ARM binary for Raspberry Pi. 8 | 9 | The source can be found in `src/` folder. You need a x86/amd64 Linux machine to compile the source assembly code with FASM (can be downloaded from http://arm.flatassembler.net). You only need the statically compiled `fasmarm` binary. I've done it within a Ubuntu 14.04 (64bit) machine running as a Docker Container. 10 | 11 | Compile the thing just with `./fasmarm httpd.fasm httpd` 12 | ``` 13 | cd ./src/ 14 | docker run --rm -ti -v $(pwd):/src ubuntu:14.04 bash -c 'cd /src && ./fasmarm httpd.fasm httpd' 15 | flat assembler for ARM version 1.71.39 (16384 kilobytes memory) 16 | 3 passes, 4328 bytes. 17 | ``` 18 | Now we've got a super small httpd binary with 4kByte only 19 | ``` 20 | ls -al httpd 21 | -rwxr-xr-x 1 dieter staff 4328 Jun 28 16:27 httpd 22 | ``` 23 | And hell yeah, it's statically linked 24 | ``` 25 | file httpd 26 | httpd: ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, stripped 27 | ``` 28 | 29 | Let's copying it to `./docker/` folder 30 | ``` 31 | cp httpd ../docker/ 32 | ``` 33 | 34 | ## Step 2: Create the Docker Nano Image 35 | ``` 36 | cd ./docker/ 37 | make 38 | ``` 39 | 40 | Now we do have a ready-to-run Docker Image with a single statically linked ARM binary for use on a Raspberry Pi. 41 | ``` 42 | docker images 43 | REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE 44 | hypriot/rpi-nano-httpd 0.1.0 0fd6d79d7479 15 minutes ago 87.7 kB 45 | hypriot/rpi-nano-httpd latest 0fd6d79d7479 15 minutes ago 87.7 kB 46 | ``` 47 | Please note, the size of the Docker Image includes the payload too! Without the payload the image size would be 4kByte only! Ok, that's not `nano` anymore, that's more `pico` sized. 48 | 49 | ## Step 3: Now let's run a lot of these containers on our Raspberry Pi 50 | To start 10 web servers use the following command 51 | ``` 52 | ./start-webservers.sh 10 53 | ``` 54 | To ramp up to 100 web servers use the following command 55 | ``` 56 | ./start-webservers.sh 100 10 57 | ``` 58 | That's it, have fun. 59 | 60 | 61 | # Acknoledgements 62 | 63 | ## httpd, original source code 64 | https://www.raspberrypi.org/forums/viewtopic.php?p=320919 65 | 66 | I was so happy to find this small piece of source code after hours. It's a minimal web server or httpd written in assembly language to run on an ARM cpu. I did only a minor change and use `index.html` as the default resource to look for. 67 | As the source code is written for FASM you also need to download this tool too. 68 | 69 | ## FASMARM: Freeware ARM cross assembler for FASM 70 | http://arm.flatassembler.net 71 | 72 | Just download the package for a Linux distro and extract the tool `fasmarm` which is a statically linked binary and doesn't need any additional dependencies installed. 73 | -------------------------------------------------------------------------------- /docker/.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile* 2 | Makefile 3 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM scratch 2 | ADD . / 3 | EXPOSE 80 4 | CMD ["/httpd", "80"] 5 | -------------------------------------------------------------------------------- /docker/Dockerfile.minimal: -------------------------------------------------------------------------------- 1 | FROM scratch 2 | ADD . / 3 | -------------------------------------------------------------------------------- /docker/Makefile: -------------------------------------------------------------------------------- 1 | DOCKER_IMAGE_VERSION=0.1.0 2 | DOCKER_IMAGE_NAME=hypriot/rpi-nano-httpd 3 | DOCKER_IMAGE_TAGNAME=$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_VERSION) 4 | 5 | default: build 6 | 7 | build: 8 | docker build -t $(DOCKER_IMAGE_TAGNAME) . 9 | docker tag -f $(DOCKER_IMAGE_TAGNAME) $(DOCKER_IMAGE_NAME):latest 10 | 11 | minimal: 12 | docker build -t $(DOCKER_IMAGE_TAGNAME) -f Dockerfile.minimal . 13 | docker tag -f $(DOCKER_IMAGE_TAGNAME) $(DOCKER_IMAGE_NAME):minimal 14 | 15 | push: 16 | docker push $(DOCKER_IMAGE_NAME) 17 | 18 | run: 19 | docker run -d -p 80:80 -d hypriot/rpi-nano-httpd 20 | -------------------------------------------------------------------------------- /docker/httpd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hypriot/rpi-nano-httpd/ab5ab3eb3d536047ec597f08b2543bed08bc3b21/docker/httpd -------------------------------------------------------------------------------- /docker/index.htm: -------------------------------------------------------------------------------- 1 | 2 | Pi armed with Docker by Hypriot 3 | 4 |
5 | pi armed with docker 6 |
7 | 8 | 9 | -------------------------------------------------------------------------------- /docker/index.html: -------------------------------------------------------------------------------- 1 | 2 | Pi armed with Docker by Hypriot 3 | 4 |
5 | pi armed with docker 6 |
7 | 8 | 9 | -------------------------------------------------------------------------------- /docker/pi_armed_with_docker.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hypriot/rpi-nano-httpd/ab5ab3eb3d536047ec597f08b2543bed08bc3b21/docker/pi_armed_with_docker.jpg -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | httpd 2 | -------------------------------------------------------------------------------- /src/FasmArm.inc: -------------------------------------------------------------------------------- 1 | macro imm16 reg,immediate { 2 | mov reg,(immediate) and $FF 3 | orr reg,(immediate) and $FF00 4 | } 5 | 6 | macro imm16eq reg,immediate { 7 | moveq reg,(immediate) and $FF 8 | orreq reg,(immediate) and $FF00 9 | } 10 | 11 | macro imm16ne reg,immediate { 12 | movne reg,(immediate) and $FF 13 | orrne reg,(immediate) and $FF00 14 | } 15 | 16 | macro imm16lt reg,immediate { 17 | movlt reg,(immediate) and $FF 18 | orrlt reg,(immediate) and $FF00 19 | } 20 | 21 | macro imm16gt reg,immediate { 22 | movgt reg,(immediate) and $FF 23 | orrgt reg,(immediate) and $FF00 24 | } 25 | 26 | macro imm32 reg,immediate { 27 | mov reg,(immediate) and $FF 28 | orr reg,(immediate) and $FF00 29 | orr reg,(immediate) and $FF0000 30 | orr reg,(immediate) and $FF000000 31 | } 32 | 33 | macro imm32eq reg,immediate { 34 | moveq reg,(immediate) and $FF 35 | orreq reg,(immediate) and $FF00 36 | orreq reg,(immediate) and $FF0000 37 | orreq reg,(immediate) and $FF000000 38 | } 39 | 40 | macro imm32ne reg,immediate { 41 | movne reg,(immediate) and $FF 42 | orrne reg,(immediate) and $FF00 43 | orrne reg,(immediate) and $FF0000 44 | orrne reg,(immediate) and $FF000000 45 | } 46 | 47 | macro imm32lt reg,immediate { 48 | movlt reg,(immediate) and $FF 49 | orrlt reg,(immediate) and $FF00 50 | orrlt reg,(immediate) and $FF0000 51 | orrlt reg,(immediate) and $FF000000 52 | } 53 | 54 | macro imm32gt reg,immediate { 55 | movgt reg,(immediate) and $FF 56 | orrgt reg,(immediate) and $FF00 57 | orrgt reg,(immediate) and $FF0000 58 | orrgt reg,(immediate) and $FF000000 59 | } 60 | -------------------------------------------------------------------------------- /src/fasmarm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hypriot/rpi-nano-httpd/ab5ab3eb3d536047ec597f08b2543bed08bc3b21/src/fasmarm -------------------------------------------------------------------------------- /src/httpd.fasm: -------------------------------------------------------------------------------- 1 | ;======================================================; 2 | ; Tiny Server. ; 3 | ;------------------------------------------------------; 4 | ; By Craig Bamford (Dex) (11-03-2013) ; 5 | ;------------------------------------------------------; 6 | format ELF executable ; 7 | entry start ; 8 | ;======================================================; 9 | ; Is logging enabled ? ; 10 | ;------------------------------------------------------; 11 | LOGGING = 1 ; 12 | LOGGING2 = 0 ; 13 | ;======================================================; 14 | ; Def ; 15 | ;------------------------------------------------------; 16 | SOCK_STREAM = 1 ; 17 | AF_INET = 2 ; 18 | SYS_socketcall = 102 ; 19 | SYS_SOCKET = 1 ; 20 | SYS_CONNECT = 3 ; 21 | SYS_SEND = 9 ; 22 | SYS_RECV = 10 ; 23 | IPPROTO_TCP = 6 ; Transmission Control Protocol 24 | ;======================================================; 25 | ; Print macro ; 26 | ;------------------------------------------------------; 27 | macro print _msg,_msglen ; 28 | { common ; 29 | push (r0) ; 30 | push (r1) ; 31 | push (r2) ; 32 | push (r7) ; 33 | mov r7, 0x4 ; 34 | mov r0, 0x1 ; 35 | imm32 r1, _msg ; 36 | imm32 r2, _msglen ; 37 | swi 0x0 ; 38 | ; 39 | pop (r7) ; 40 | pop (r2) ; 41 | pop (r1) ; 42 | pop (r0) ; 43 | } ; 44 | ;======================================================; 45 | ; old tcpsend macro ; 46 | ;------------------------------------------------------; 47 | macro tcpsend _msg,_msglen ; 48 | { common ; 49 | push (r0) ; 50 | push (r1) ; 51 | push (r2) ; 52 | push (r3) ; 53 | push (r7) ; 54 | imm32 r0, clientfd ; 55 | ldr r0, [r0] ; 56 | imm32 r1, _msg ; 57 | imm32 r2, _msglen ; 58 | mov r3, 0 ; 59 | imm32 r7, 0x121 ; 60 | swi 0x0 ; 61 | pop (r7) ; 62 | pop (r3) ; 63 | pop (r2) ; 64 | pop (r1) ; 65 | pop (r0) ; 66 | } ; 67 | ;======================================================; 68 | ; old tcpsend macro ; 69 | ;------------------------------------------------------; 70 | ; r0 = size to send ; 71 | ;------------------------------------------------------; 72 | macro tcpsend_reg _msg ; 73 | { common ; 74 | push (r0) ; 75 | push (r1) ; 76 | push (r2) ; 77 | push (r3) ; 78 | push (r7) ; 79 | mov r2, r0 ; 80 | imm32 r0, clientfd ; 81 | ldr r0, [r0] ; 82 | imm32 r1, _msg ; 83 | mov r3, 0 ; 84 | imm32 r7, 0x121 ; 85 | swi 0x0 ; 86 | pop (r7) ; 87 | pop (r3) ; 88 | pop (r2) ; 89 | pop (r1) ; 90 | pop (r0) ; 91 | } ; 92 | ;======================================================; 93 | ; Print macro ; 94 | ;------------------------------------------------------; 95 | ; r0 = string size ; 96 | ;------------------------------------------------------; 97 | macro print_reg _msg,_msglen ; 98 | { common ; 99 | push (r0) ; 100 | push (r1) ; 101 | push (r2) ; 102 | push (r7) ; 103 | mov r2, r0 ; 104 | mov r7, 0x4 ; 105 | mov r0, 0x1 ; 106 | imm32 r1, _msg ; 107 | swi 0x0 ; 108 | ; 109 | pop (r7) ; 110 | pop (r2) ; 111 | pop (r1) ; 112 | pop (r0) ; 113 | } ; 114 | include 'FasmArm.inc' ; 115 | segment readable writeable executable ; 116 | ;======================================================; 117 | ; Main start ; 118 | ;------------------------------------------------------; 119 | start: ; 120 | print msg,msg.msg_size ; print start message 121 | ;======================================================; 122 | ; Check commamd line for port number ; 123 | ;------------------------------------------------------; 124 | ldmfd sp!, {r1} ; 125 | imm32 r0, argc ; 126 | str r1, [r0] ; 127 | mov r0, 80 ; default port number 128 | cmp r1, 1 ; 129 | ;======================================================; 130 | ; If no number, use default (port 80) ; 131 | ;------------------------------------------------------; 132 | beq UseDeFault ; 133 | ;======================================================; 134 | ; Convet port number ; 135 | ;------------------------------------------------------; 136 | ldmfd sp!,{r1} ; 137 | ldmfd sp!,{r3} ; 138 | mov r4, 10 ; 139 | bl StrToInt ; 140 | mov r0, r1 ; 141 | imm32 r1, portnumber ; 142 | str r0, [r1] ; 143 | align 4 ; 144 | UseDeFault: ; 145 | ;======================================================; 146 | ; Store Port ; 147 | ;------------------------------------------------------; 148 | rev16 r0, r0 ; byte order 149 | imm32 r1, sockaddr_in.sin_port ; 150 | strh r0, [r1] ; 151 | ;======================================================; 152 | ; SOCKET(2) ; 153 | ;------------------------------------------------------; 154 | ; Creates an endpoint for communication and returns ; 155 | ; a descriptor. ; 156 | ;------------------------------------------------------; ; 157 | imm32 r7, 0x119 ; socket sys number 158 | mov r0, AF_INET ; 159 | mov r1, SOCK_STREAM ; 160 | mov r2, 0 ; 161 | swi 0x0 ; 162 | teq r0, 0 ; 163 | bpl bind ; branch if not neg 164 | ; 165 | mvn r0, r0 ; 166 | add r0, 1 ; change neg to pos number 167 | imm32 r1, 4096 ; We need to check because some times address is neg 168 | cmp r0, r1 ; 169 | bl Convert2Hex ; Get error code 170 | print print_hex_string,print_hex_string_size ; 171 | print msgE1,msgE1.msgE1_size ; error message 1 172 | b fin ; branch exit 173 | align 4 ; 174 | ;======================================================; 175 | ; BIND(2) ; 176 | ;------------------------------------------------------; 177 | ; When a socket is created with socket(2), ; 178 | ; it exists in a name space (address family) but has ; 179 | ; no address assigned to it. bind() assigns the ; 180 | ; address specified to by addr to the socket referred ; 181 | ; to by the file descriptor sockfd. addrlen specifies ; 182 | ; the size, in bytes, of the address structure pointed ; 183 | ; to by addr. Traditionally, this operation is called ; 184 | ; "assigning a name to a socket". ; 185 | ;------------------------------------------------------; 186 | bind: ; Ok SOCKET(2) 187 | imm32 r1, sockfd ; 188 | str r0, [r1] ; 189 | ; 190 | imm32 r0, sockfd ; bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen); 191 | ldr r0, [r0] ; 192 | imm32 r1, sockaddr_in ; 193 | imm32 r2, dest_size ; 194 | imm32 r7, 0x11A ; BIND(2) 195 | swi 0x0 ; 196 | ; 197 | teq r0, 0 ; check for errors 198 | bpl listen ; branch if not neg 199 | ; 200 | mvn r0, r0 ; 201 | add r0, 1 ; change neg to pos number 202 | imm32 r1, 4096 ; We need to check because some times address is neg 203 | cmp r0, r1 ; 204 | bl Convert2Hex ; Get error code 205 | print print_hex_string,print_hex_string_size ; 206 | print msgE2,msgE2.msgE2_size ; error message 1 207 | b fin ; branch exit 208 | ; Ok BIND(2) 209 | ;======================================================; 210 | ; LISTEN(2) ; 211 | ;------------------------------------------------------; 212 | ; Start to listen for incomming connections, ; 213 | ; allowing for a queue size of ; 214 | ; waiting connections of 20. ; 215 | ;------------------------------------------------------; 216 | align 4 ; 217 | listen: ; 218 | imm32 r0, sockfd ; listen(int sockfd, int backlog); 219 | ldr r0, [r0] ; 220 | mov r1, 0 ; was 20 ***** 221 | imm32 r7, 0x11C ; LISTEN(2) 222 | swi 0x0 ; 223 | ; 224 | teq r0, 0 ; check for errors 225 | bpl infinity ; branch if not neg 226 | ; 227 | mvn r0, r0 ; 228 | add r0, 1 ; change neg to pos number 229 | imm32 r1, 4096 ; We need to check because some times address is neg 230 | cmp r0, r1 ; 231 | bl Convert2Hex ; Get error code 232 | print print_hex_string,print_hex_string_size ; 233 | print msgE3,msgE3.msgE3_size ; error message 1 234 | b fin ; branch exit 235 | align 4 ; 236 | infinity: ; Ok LISTEN(2) 237 | ;======================================================; 238 | ; ACCEPT(2) ; 239 | ;------------------------------------------------------; 240 | ; The accept() system call is used with ; 241 | ; connection-based socket types (SOCK_STREAM, ; 242 | ; SOCK_SEQPACKET). It extracts the first connection ; 243 | ; request on the queue of pending connections for the ; 244 | ; listening socket, sockfd, creates a new connected ; 245 | ; socket, and returns a new file descriptor referring ; 246 | ; to that socket. The newly created socket is not in ; 247 | ; the listening state. The original socket sockfd is ; 248 | ; unaffected by this call. ; 249 | ;------------------------------------------------------; 250 | accept_loop: ; 251 | imm32 r0, sockfd ; 252 | ldr r0, [r0] ; 253 | imm32 r1, client_addr_ip ; client_addr 254 | imm32 r2, sixteen ; addrlen 255 | imm32 r7, 0x11D ; ACCEPT(2) 256 | swi 0x0 ; 257 | ; 258 | teq r0, 0 ; check for errors 259 | bgt accept_ok ; 260 | if LOGGING2 ; 261 | mvn r0, r0 ; 262 | add r0, 1 ; change neg to pos number 263 | bl Convert2Hex ; Get error code 264 | print print_hex_string,print_hex_string_size ; 265 | print msgE4,msgE4.msgE4_size ; error message 1 266 | end if ; 267 | b accept_loop ; branch if neg or zero 268 | align 4 ; 269 | ;======================================================; 270 | ; RECV(2) ; 271 | ;------------------------------------------------------; 272 | ; The recv(2) and recvmsg() calls are used to receive ; 273 | ; messages from a socket, and may be used to receive ; 274 | ; data on a socket whether or not it is ; 275 | ; connection-oriented. ; 276 | ; ; 277 | ; If src_addr is not NULL, and the underlying protocol ; 278 | ; provides the source address, this source address is ; 279 | ; filled in. When src_addr is NULL, nothing is filled ; 280 | ; in, in this case, addrlen is not used, and should ; 281 | ; also be NULL. The argument addrlen is a value-result ; 282 | ; argument, which the caller should initialize before ; 283 | ; the call to the size of the buffer associated with ; 284 | ; src_addr, and modified on return to indicate the ; 285 | ; actual size of the source address. The returned ; 286 | ; address is truncated if the buffer provided is too ; 287 | ; small, ; 288 | ; in this case, addrlen will return a value ; 289 | ; greater than was supplied to the call. ; 290 | ;------------------------------------------------------; 291 | accept_ok: ; 292 | imm32 r1, clientfd ; 293 | str r0, [r1] ; 294 | recv_loop: ; 295 | imm32 r0, clientfd ; 296 | ldr r0, [r0] ; client FD 297 | imm32 r1, buffer ; buffer 298 | imm32 r2, bufferlen ; buffer size 299 | imm32 r3, 0 ; No options 300 | imm32 r7, 0x123 ; RECV(2) 301 | swi 0x0 ; 302 | ; 303 | teq r0, 0 ; check for errors 304 | blt recv_loop ; 305 | imm32 r1, recv_size ; get size of message 306 | str r0, [r1] ; store it 307 | ;======================================================; 308 | ; if logging2 then print buffer ; 309 | ;------------------------------------------------------; 310 | if LOGGING2 ; 311 | print buffer, bufferlen ; 312 | ;======================================================; 313 | ; Next line ; 314 | ;------------------------------------------------------; 315 | print NextLine, NextLine.NextLine_size ; 316 | ; 317 | end if ; 318 | ;======================================================; 319 | ; test for file ; 320 | ;------------------------------------------------------; 321 | imm32 r0, buffer ; 322 | ldr r2, [r0], 4 ; 323 | imm32 r3, 'GET ' ; 324 | cmp r2, r3 ; 325 | bne close ; 326 | eor r1, r1 ; 327 | ldrb r2, [r0], 1 ; 328 | cmp r2, '/' ; 329 | bne close ; 330 | filewantedloop: ; 331 | ldrb r2, [r0], 1 ; 332 | cmp r2, ' ' ; 333 | beq ItsSpace ; 334 | ; 335 | imm32 r3, bufferwantedfile ; 336 | strb r2, [r3,r1] ; 337 | add r1, 1 ; 338 | b filewantedloop ; 339 | ItsSpace: ; 340 | imm32 r3, bufferwantedfile ; 341 | cmp r1,0 ; 342 | bne Notjustspace ; 343 | imm32 r6,"inde" ; 344 | str r6,[r3],4 ; 345 | imm32 r6,"x.ht" ; 346 | str r6,[r3],4 ; 347 | imm32 r6,"m" ; 348 | strb r6,[r3] ; 349 | imm32 r3, bufferwantedfile ; 350 | mov r1,9 ; 351 | Notjustspace: ; 352 | mov r4, 0 ; 353 | strb r4, [r3,r1] ; 354 | imm32 r4, offset ; 355 | str r1, [r4] ; 356 | ;======================================================; 357 | ; Is logging enabled ? ; 358 | ;------------------------------------------------------; 359 | ; If logging has been enabled, itll use the function ; 360 | ; called ntop (at the bottom of this code) to convert ; 361 | ; the IP address of the person who connected to the ; 362 | ; server into a string form and print it. ; 363 | ;------------------------------------------------------; 364 | if LOGGING ; 365 | imm32 r2, client_addr_ip ; 366 | ldr r0, [r2, 4] ; mov eax, dword [peeraddr+4] 367 | imm32 r3, client_addr_ip_Converted ; mov edi, buffer+bufferlen-16 368 | bl ntop ; call ntop 369 | print_reg client_addr_ip_Converted ; 370 | end if ; 371 | ;======================================================; 372 | ; get file info ; 373 | ;------------------------------------------------------; 374 | fileisnamed: ; 375 | mov r7, 106 ; 376 | imm32 r0, bufferwantedfile ; 377 | imm32 r1, stat ; 378 | swi 0x0 ; 379 | cmp r0, 0 ; 380 | beq openfile ; 381 | ; 382 | ;======================================================; 383 | ; file not found ; 384 | ;------------------------------------------------------; 385 | tcpsend h404,h404.len ; 386 | ;======================================================; 387 | ; if logging print 404 message ; 388 | ;------------------------------------------------------; 389 | if LOGGING ; 390 | print l404,l404.len ; 391 | ;======================================================; 392 | ; Print wanted file name ; 393 | ;------------------------------------------------------; 394 | imm32 r2, offset ; 395 | ldr r2, [r2] ; 396 | mov r7, 0x4 ; 397 | mov r0, 0x1 ; 398 | imm32 r1, bufferwantedfile ; 399 | ; 400 | swi 0x0 ; 401 | ;======================================================; 402 | ; Next line ; 403 | ;------------------------------------------------------; 404 | print NextLine, NextLine.NextLine_size ; 405 | end if ; 406 | b close ; 407 | ; 408 | ;======================================================; 409 | ; Try to open file ; 410 | ;------------------------------------------------------; 411 | openfile: ; 412 | imm32 r4, stat ; 413 | ldr r0, [r4,20] ; 414 | imm32 r5, fdlen ; 415 | str r0, [r5] ; 416 | mov r7, 5 ; 417 | imm32 r0, bufferwantedfile ; 418 | mov r1 ,0 ; 419 | mov r2 ,0 ; 420 | swi 0x0 ; 421 | teq r0, 0 ; check for errors 422 | bgt fd_ok ; 423 | ;======================================================; 424 | ; Error when opeing file ; 425 | ;------------------------------------------------------; 426 | mvn r0, r0 ; 427 | add r0, 1 ; change neg to pos number 428 | bl Convert2Hex ; 429 | print print_hex_string, print_hex_string_size 430 | print fderror,fderror_len ; 431 | b close ; 432 | ;======================================================; 433 | ; file found and ready to send ; 434 | ;------------------------------------------------------; 435 | fd_ok: ; 436 | imm32 r1, fd ; 437 | str r0, [r1] ; 438 | ;======================================================; 439 | ; if logging, print 200 OK message ; 440 | ;------------------------------------------------------; 441 | if LOGGING ; 442 | print l200,l200.len ; 443 | ;======================================================; 444 | ; Print wanted file name ; 445 | ;------------------------------------------------------; 446 | imm32 r2, offset ; 447 | ldr r2, [r2] ; 448 | mov r7, 0x4 ; 449 | mov r0, 0x1 ; 450 | imm32 r1, bufferwantedfile ; 451 | ; 452 | swi 0x0 ; 453 | ;======================================================; 454 | ; Next line ; 455 | ;------------------------------------------------------; 456 | print NextLine, NextLine.NextLine_size ; 457 | end if ; 458 | ;======================================================; 459 | ; set header (eg: file size) ; 460 | ;------------------------------------------------------; 461 | imm32 r0, fdlen ; 462 | ldr r0, [r0] ; 463 | imm32 r10, c_l ; 464 | bl IntToStr ; 465 | mov r1, 13 ; 466 | strb r1, [r10],1 ; 467 | mov r1, 10 ; 468 | strb r1, [r10],1 ; 469 | mov r1, 13 ; 470 | strb r1, [r10],1 ; 471 | mov r1, 10 ; 472 | strb r1, [r10],1 ; 473 | ; 474 | imm32 r1, c_l ; 475 | sub r10, r1 ; 476 | imm32 r0, h200.len ; 477 | add r0, r10 ; 478 | ; print NextLine, NextLine.NextLine_size ; 479 | ; print_reg h200 ;, h200.len ; temp 480 | ; print NextLine, NextLine.NextLine_size ; 481 | ;======================================================; 482 | ; send header ; 483 | ;------------------------------------------------------; 484 | tcpsend_reg h200 ; 485 | ;======================================================; 486 | ; Send file ; 487 | ;------------------------------------------------------; 488 | mov r7, 187 ; 489 | imm32 r0, clientfd ; 490 | ldr r0, [r0] ; 491 | imm32 r1, fd ; 492 | ldr r1, [r1] ; 493 | mov r2, 0 ; 494 | imm32 r3, fdlen ; 495 | ldr r3, [r3] ; 496 | mov r4, 0 ; 497 | swi 0x0 ; 498 | ; 499 | align 4 ; 500 | ;======================================================; 501 | ; Close socket and wait ; 502 | ;------------------------------------------------------; 503 | close: ; 504 | mov r7, 6 ; 505 | imm32 r0, fd ; 506 | ldr r0, [r0] ; 507 | swi 0x0 ; 508 | mov r7, 6 ; CLOSE(2) 509 | imm32 r0, clientfd ; 510 | ldr r0, [r0] ; 511 | swi 0x0 ; 512 | imm32 r0,buffer ; 513 | eor r1,r1 ; 514 | imm32 r2,bufferlen ; 515 | mov r2,r2, lsr 2 ; 516 | align 4 ; 517 | ClearBuffLoop: ; 518 | str r1,[r0],4 ; 519 | subs r2,r2,1 ; 520 | bne ClearBuffLoop ; 521 | b infinity ; 522 | align 4 ; 523 | ;======================================================; 524 | ; Close server and exit ; 525 | ;------------------------------------------------------; 526 | fin: ; exit 527 | print msgclose,msgclose.msgclose_size ; 528 | mov r0, 0x0 ; 529 | mov r7, 0x1 ; 530 | swi 0x0 ; 531 | ; 532 | ;======================================================; 533 | ; Converts to hex ; 534 | ; r0 = number ( prints 8 hex digits) ; 535 | ;------------------------------------------------------; 536 | align 4 ; 537 | Convert2Hex: ; 538 | stmfd sp!, {r0-r12, lr} ; 539 | imm32 r2,hex_digits ; 540 | imm32 r3,print_hex_string ; 541 | mov r4,28 ; 542 | align 4 ; 543 | print_hex_loop: ; 544 | mov r1,r0,lsr r4 ; Get digit n 545 | and r1,r1,0x0f ; mask off lower nibble 546 | ldrb r1,[r2,r1] ; r0 now contains a hex number, 547 | ; look it up in table 548 | strb r1,[r3],1 ; 549 | subs r4,r4,4 ; 550 | bpl print_hex_loop ; 551 | ; 552 | ldmfd sp!, {r0-r12, pc} ; pop & return 553 | align 4 ; 554 | ;======================================================; 555 | ; String to int ; 556 | ; input: r3 = buffer, r4 = base (eg 10) ; 557 | ; output: r1 = number ; 558 | ;------------------------------------------------------; 559 | StrToInt: ; 560 | stmfd sp!, {r2-r3, lr} ; save regs 561 | eor r1, r1 ; zero r1 and r2 562 | eor r2, r2 ; 563 | .loop: ; 564 | ldrb r2, [r3], 1 ; load a byte 565 | cmp r2, 0 ; check for 0 566 | beq .end ; if so exit 567 | mul r1, r1, r4 ; multiply with basic 568 | sub r2, r2, '0' ; sub ASCII value 569 | cmp r2, 9 ; cmpare it to 9 570 | ble .ok ; if its let or = jump to OK 571 | sub r2, 7 ; if not sub 7 (eg: it A-F) 572 | .ok: ; 573 | add r1, r1, r2 ; save it 574 | b .loop ; do another loop .loop 575 | .end: ; 576 | ldmfd sp!, {r2-r3, pc} ; restore regs and return. 577 | align 4 ; 578 | ;======================================================; 579 | ; write number to buffer ; 580 | ;------------------------------------------------------; 581 | ; r10 = buffer address ; 582 | ;------------------------------------------------------; 583 | number2buffer: ; 584 | stmfd sp!, {lr} ; 585 | strb r0, [r10],1 ; 586 | ldmfd sp!, {pc} ; 587 | align 4 ; 588 | ;======================================================; 589 | ; Int To String ; 590 | ;------------------------------------------------------; 591 | ; input: r0 = number ; 592 | ; r10 = buffer ; 593 | ; ; 594 | ; output: r10 = buffer + offset ; 595 | ;------------------------------------------------------; ; 596 | IntToStr: ; 597 | stmfd sp!, {r0-r9, lr} ; save regs 598 | imm32 r8,prt_digits ; 599 | ; 600 | imm32 r6,0 ; current character to print 601 | mov r5,9 ; digits - 1 (max pos value 2147483647) (31 bits) 602 | imm32 r9,0 ; 603 | ; 604 | ; 605 | cmp r0,0 ; 606 | beq zeronum ; 607 | and r7,r0,$80000000 ; test bit 31 of r0 608 | cmp r7,0 ; 609 | beq posnum ; go to posnum if result equals zero (positive) 610 | ; 611 | imm32 r2,$FFFFFFFF ; 612 | eor r0,r0,r2 ; invert all bits of r0 (convert to positive) 613 | mov r2,r0 ; backup r0 value 614 | add r2,r2,1 ; add 1 to (now positive) value 615 | imm32 r0,"-" ; 616 | bl number2buffer ; print negative (-) sign 617 | mov r0,r2 ; restore r0 value 618 | ; 619 | posnum: ; 620 | imm32 r4,1000000000 ; 621 | subs r1,r0,r4 ; subtract r4 (power of 10 result) from starting value, store in r1 622 | b Printloop ; 623 | ; 624 | align 4 ; 625 | mainloop: ; 626 | imm32 r6,0 ; clear r6 627 | mov r2,r5 ; back up value of r5 628 | b pow10 ; lookup power of 10 from r5 629 | p10ret: ; 630 | mov r5,r2 ; restore r5 value 631 | subs r1,r1,r4 ; subtract r4 (power of 10 result) from starting value, store in r1 632 | ; 633 | Printloop: ; 634 | ;print "At Printloop" ; 635 | ;sync ; 636 | bpl notmatched ; go to notmatched if result (r1) is not negative 637 | add r1,r1,r4 ; add power of 10 value to r1 638 | cmp r6,0 ; 639 | blt nodigit ; if r6 is negative, reset to 0 640 | b numfix ; 641 | notmatched: ; 642 | ;-print "at notmatched" ; 643 | add r6,r6,1 ; increment current character by 1 644 | subs r1,r1,r4 ; subtract pow10 value from r1 645 | b Printloop ; 646 | numfix: ; 647 | mov r3,r6 ; update r3 from r6 648 | ldrb r3,[r8,r3] ; r3 now contains a number (ascii) 649 | mov r0,r3 ; move ascii character to r0 650 | cmp r9,0 ; if r9 does not equal zero, 651 | bne allowprt ; go to allowprt (allow printing) 652 | cmp r0,'0' ; if r0 equals ascii zero, 653 | beq noprint ; go to noprint (disable printing leading zeros) 654 | imm32 r9,1 ; set r9 to 1 (allow printing zeros) 655 | ; 656 | allowprt: ; 657 | bl number2buffer ; print character to buffer 658 | noprint: ; 659 | subs r5,r5,1 ; decrement r5 (power of 10 mult) by 1 660 | mov r0,r1 ; replace r0 with r1 661 | bpl mainloop ; go to mainloop if r5 is not negative 662 | ;sync ; 663 | ;-end ; 664 | finish: ; 665 | ;print "Done printing" ; 666 | ;sync ; 667 | ldmfd sp!, {r0-r9, pc} ; restore regs and return. 668 | zeronum: ; 669 | imm32 r0,"0" ; 670 | bl number2buffer ; print negative (-) sign 671 | b finish ; 672 | ; 673 | ; 674 | nodigit: ; 675 | imm32 r6,0 ; 676 | b numfix ; 677 | ; 678 | pow10: ; calculate 10^x (r5 value) ; 679 | ;r4 will contain result, r5 specifies power of 10 to calc 680 | imm32 r4,10 ; 681 | cmp r5,1 ; 682 | bgt p10mul ; 683 | blt p10p1 ; 684 | imm32 r4,10 ; 685 | b p10ret ; 686 | p10p1: ; 687 | imm32 r4,1 ; 688 | b p10ret ; 689 | p10mul: ; 690 | sub r5,r5,1 ; 691 | p10loop: ; 692 | imm32 r7,10 ; 693 | mul r4,r4,r7 ; 694 | sub r5,r5,1 ; 695 | cmp r5,0 ; 696 | bgt p10loop ; 697 | b p10ret ; 698 | align 4 ; 699 | ;======================================================; 700 | ; ntop ; 701 | ; r0 = network-order address, r3 = buffer ; 702 | ; returns: r10 = buffer + offset ; 703 | ;------------------------------------------------------; 704 | ntop: ; 705 | ; eax = network-order address, edi = buffer ; 706 | ; returns: ecx = length ; 707 | stmfd sp!, {r1-r12, lr} ; save regs push ebx edx ebp 708 | mov r10,r3 ; 709 | mov r6, r3 ; 710 | mov r1, r0 ; 711 | imm32 r2, 0x000000FF ; 712 | and r0, r2 ; 713 | mov r4, 2 ; 714 | ;num4 ; 715 | bl IntToStr ; 716 | imm32 r2, '.' ; 717 | strb r2, [r10],1 ; 718 | ;num3 ; 719 | mov r0, r1 ; 720 | imm32 r2, 0x0000FF00 ; 721 | and r0, r2 ; 722 | mov r0, r0, lsr 8 ; 723 | mov r4, 2 ; 724 | bl IntToStr ; 725 | imm32 r2, '.' ; 726 | strb r2, [r10],1 ; 727 | ;num2 ; 728 | mov r0, r1 ; 729 | imm32 r2, 0x00FF0000 ; 730 | and r0, r2 ; 731 | mov r0, r0, lsr 16 ; 732 | mov r4, 2 ; 733 | bl IntToStr ; 734 | imm32 r2, '.' ; 735 | strb r2, [r10],1 ; 736 | ; num1 ; 737 | mov r0, r1 ; 738 | mov r0, r0, lsr 24 ; 739 | mov r4, 2 ; 740 | bl IntToStr ; 741 | ; 742 | sub r10, r6 ; 743 | mov r0, r10 ; 744 | ldmfd sp!, {r1-r12, pc} ; restore regs and return.ret 745 | 746 | ; ---------------------------------------------------- ; 747 | ; OutChar ( print 1 character to stdout ) ; 748 | ; ---------------------------------------------------- ; 749 | ; put char = r0 ; 750 | ; ---------------------------------------------------- ; 751 | OutChar: ; 752 | stmfd sp!, {r0-r2, r7, lr} ; Store registers 753 | mov r1, sp ; r1 address 754 | mov r0, 1 ; r0 stdout 755 | mov r2, r0 ; r2 length 756 | mov r7, 4 ; 757 | swi 0 ; 758 | ldmfd sp!, {r0-r2, r7, pc} ; Restore registers and return 759 | ;======================================================; 760 | ; data ; 761 | ;------------------------------------------------------; 762 | segment readable writeable ; 763 | if LOGGING ; 764 | align 4 ; 765 | l404 db ' - 404 NF - ' ; 766 | .len = $ - l404 ; 767 | align 4 ; 768 | l200 db ' - 200 OK - ' ; 769 | .len = $ - l200 ; 770 | end if ; 771 | align 4 ; 772 | ipnumber dw 0xfefefefe ; 773 | align 4 ; 774 | prt_digits: ; 775 | db "0123456789" ; 776 | align 4 ; 777 | hex_digits: ; 778 | db "0123456789ABCDEF" ; 779 | align 4 ; 780 | print_hex_string: ; 781 | db "12345678", 0xa ; storage for 8 digit hex string, 782 | print_hex_string_size = $-print_hex_string ; 783 | align 4 ; 784 | client_addr_ip: rb 16 ; 785 | sixteen dw 16 ; 786 | align 4 ; 787 | sockaddr_in: ; 788 | .sin_family dh AF_INET ; 789 | .sin_port dh 0 ; 790 | .sin_addr dw 0 ; 791 | .sin_zero rb 8 ; 792 | dest_size = $-sockaddr_in ; 793 | align 4 ; 794 | client_addr dw sockaddr_in ; 795 | addrlen dw dest_size ; 796 | align 4 ; 797 | argc dw 0 ; 798 | portnumber dw 0 ; 799 | align 4 ; 800 | recv_size dw 0 ; 801 | sockfd dw 0 ; 802 | fd dw 0 ; 803 | fdlen dw 0 ; 804 | clientfd dw 0 ; 805 | offset dw 0 ; 806 | align 4 ; 807 | cArray dw 0 ; 808 | dw 0 ; 809 | dw 0 ; 810 | dw 0 ; 811 | dw 0 ; 812 | align 4 ; 813 | ;send 'array'. ; 814 | sArray dw 0 ; 815 | dw 0 ; 816 | dw 0 ; 817 | dw 0 ; 818 | align 4 ; 819 | indexfile db ".index.html",0 ; 820 | align 4 ; 821 | NextLine db " " ,0xa ; Next line 822 | .NextLine_size = $-NextLine ; 823 | align 4 ; 824 | msgreceved db " Server receved data" ,0xa ; Message 825 | .msgreceved_size = $-msgreceved ; 826 | align 4 ; 827 | msg db " Server started..." ,0xa ; Message 828 | .msg_size = $-msg ; 829 | align 4 ; 830 | msgclose db " Server closed", 0xa ; Close 831 | .msgclose_size = $-msgclose ; 832 | align 4 ; 833 | msgsent db " Sent file", 0xa ; Send 834 | .msgsent_size = $-msgsent ; 835 | ; 836 | msgE1 db " socket() failed" ,0xa ; Err1 837 | .msgE1_size = $-msgE1 ; 838 | align 4 ; 839 | msgE2 db " bind() failed", 0xa ; Err2 840 | .msgE2_size = $-msgE2 ; 841 | align 4 ; 842 | msgE3 db " listen() failed", 0xa ; Err3 843 | .msgE3_size = $-msgE3 ; 844 | align 4 ; 845 | msgE4 db " accept() failed", 0xa ; Err4 846 | .msgE4_size = $-msgE4 ; 847 | align 4 ; 848 | msgE5 db " send() failed", 0xa ; Err5 849 | .msgE5_size = $-msgE5 ; 850 | align 4 ; 851 | fderror db " ERROR 1", 0xa ; 852 | fderror_len = $- fderror ; 853 | align 4 ; 854 | fderror2 db " ERROR 2", 0xa ; 855 | fderror_len2 = $- fderror2 ; 856 | align 4 ; 857 | h404 db 'HTTP/1.0 404 Not Found',13,10,13,10,'404 Not Found

File Not Found

',10,13 858 | .len = $ - h404 ; 859 | align 4 ; 860 | h200 db 'HTTP/1.0 200 OK',13,10 ; 861 | db 'Server: DexServer',13,10 ; 862 | db 'Content-Length: ' ; 863 | .len = $ - h200 ; 864 | align 4 ; 865 | c_l db ' '; 866 | align 4 ; 867 | stat rb 88 ; 868 | align 4 ; 869 | buffer rb 512 ; 870 | bufferlen = $ - buffer ; 871 | align 4 ; 872 | bufferwantedfile rb 512 ; 873 | align 4 ; 874 | client_addr_ip_Converted rb 16 ; 875 | align 4 ; 876 | -------------------------------------------------------------------------------- /start-webservers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | DOCKER_IMAGE="hypriot/rpi-nano-httpd" 5 | P1=$1 6 | P2=$2 7 | MAXNR=${P1:="1"} 8 | STARTNR=${P2:="0"} 9 | 10 | COUNTER=$STARTNR 11 | while [ $COUNTER -lt $MAXNR ]; do 12 | let COUNTER=COUNTER+1 13 | let PORT=10000+COUNTER 14 | echo COUNTER=$COUNTER, PORT=$PORT 15 | docker run -d --name=WebServer-$PORT -p $PORT:80 $DOCKER_IMAGE 16 | done 17 | --------------------------------------------------------------------------------