0

The title is explicit.

Reproductible steps

* Launch an Ubuntu instance on AWS with HTTP rule on port 80

* Install sbcl

sudo apt install sbcl -y

* Install usocket library for root

See next step to understand why I am doing this as root

curl -O https://beta.quicklisp.org/quicklisp.lisp
sudo sbcl --load quicklisp.lisp

The next lines need to be typed into sbcl REPL

(quicklisp-quickstart:install)
(ql:add-to-init-file)
(ql:quickload "usocket")

* Use Land of Lisp example (http://landoflisp.com/)

In a file named server.lisp

(require 'usocket)

(defun http-char (c1 c2 &optional (default #\Space)) (let ((code (parse-integer (coerce (list c1 c2) 'string) :radix 16 :junk-allowed t))) (if code (code-char code) default)))

(defun decode-param (s) (labels ((f (lst) (when lst (case (car lst) (#% (cons (http-char (cadr lst) (caddr lst)) (f (cdddr lst)))) (#+ (cons #\space (f (cdr lst)))) (otherwise (cons (car lst) (f (cdr lst)))))))) (coerce (f (coerce s 'list)) 'string)))

(defun parse-params (s) (let* ((i1 (position #= s)) (i2 (position #& s))) (cond (i1 (cons (cons (intern (string-upcase (subseq s 0 i1))) (decode-param (subseq s (1+ i1) i2))) (and i2 (parse-params (subseq s (1+ i2)))))) ((equal s "") nil) (t s))))

(defun parse-url (s) (let* ((url (subseq s (+ 2 (position #\space s)) (position #\space s :from-end t))) (x (position #? url))) (if x (cons (subseq url 0 x) (parse-params (subseq url (1+ x)))) (cons url '()))))

(defun get-header (stream) (let* ((s (read-line stream)) (h (let ((i (position #: s))) (when i (cons (intern (string-upcase (subseq s 0 i))) (subseq s (+ i 2))))))) (when h (cons h (get-header stream)))))

(defun get-content-params (stream header) (let ((length (cdr (assoc 'content-length header)))) (when length (let ((content (make-string (parse-integer length)))) (read-sequence content stream) (parse-params content)))))

(defun serve (request-handler) (let ((socket (usocket:socket-listen #(127 0 0 1) 80))) (unwind-protect (loop (with-open-stream (stream (usocket:socket-stream (usocket:socket-accept socket))) (let* ((url (parse-url (read-line stream))) (path (car url)) (header (get-header stream)) (params (append (cdr url) (get-content-params stream header))) (standard-output stream)) (funcall request-handler path header params)))) (usocket:socket-close socket))))

(defun hello-request-handler (path header params) (if (equal path "greeting") (let ((name (assoc 'name params))) (if (not name) (princ "<html><form>What is your name?<input name='name'/></form></html>") (format t "<html>Nice to meet you, ~a!</html>" (cdr name)))) (princ "Sorry... I don't know that page")))

(serve #'hello-request-handler)

Then you launch the server as root :

sudo sbcl --load "server.lisp"

I am using root because I cannot get rid of the following error message with a normal user

The condition Socket error in "bind": 13 (Permission denied) occurred with errno :0.

Then everything seems to be right but I cannot access the server from a standard browser using :

http://IPv4.Public.IP:80

* Supplementary diagnostics :

AWS Security group / inboud rules

╔══════╦══════════╦════════════╦═══════════╗
║ Type ║ Protocol ║ Port Range ║  Source   ║
╠══════╬══════════╬════════════╬═══════════╣
║ HTTP ║ TCP      ║         80 ║ 0.0.0.0/0 ║
║ HTTP ║ TCP      ║         80 ║ ::/0      ║
║ SSH  ║ TCP      ║         22 ║ 0.0.0.0/0 ║
╚══════╩══════════╩════════════╩═══════════╝

iptables

sudo iptables -L -v
Chain INPUT (policy ACCEPT 346 packets, 23760 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination

Chain OUTPUT (policy ACCEPT 244 packets, 32428 bytes) pkts bytes target prot opt in out source destination

sudo iptables -t nat -L -v
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination

Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination

curl on server

curl 127.0.0.1
Sorry... I don't know that page

as expected !

ping from distant machine

Need to add Custom ICMP rule to inbound security group policy (now I know that ping is using ICMP ...)

ping 35.180.138.87
64 bytes from 35.180.138.87: icmp_seq=1 ttl=49 time=173 ms
64 bytes from 35.180.138.87: icmp_seq=2 ttl=49 time=32.2 ms
^C
--- 35.180.138.87 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1005ms
rtt min/avg/max/mdev = 32.248/102.884/173.520/70.636 ms

curl from distant machine

curl 35.180.138.87
curl: (7) Failed to connect to 35.180.138.87 port 80: Connection refused

netstat

netstat -nlp
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
tcp        0      0 127.0.0.1:80            0.0.0.0:*               LISTEN      -

This line with port 80 only appears when my server is running.

2 Answers2

0

Try pinging the public IPV4 to make sure you reach it.

If you do indeed reach it, check iptables/firewalld rules for port 80 (es. iptables -L ...)

If the firewall is OK, check that the server is binding on all network interfaces and not just on loopback (127.0.0.1) with netstat -nlp. Also check if other programs are binding to port 80.

You may also test curl 127.0.0.1 to check if the server is actually serving the page and you're not reaching it, or if the server is broken overall.

Also make sure that the HTTP rule on port 80 you set on AWS is in a security group that is associated to your instance.

Vi Pau
  • 268
0

The issue was in the ip address used to open the socket i.e 127.0.0.1

I have tried to use the IPv4 address given by AWS but this was not the solution

Instead one have to search for the ip address from the host server :

sudo ip addr

The answer is for example 111.111.111.111

And then use the found address inside the lisp code

(let ((socket (usocket:socket-listen #(111 111 111 111) 80)))