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.

3
  • You probably haven't configured your security groups to allow public access, maybe you haven't added an internet gateway to your VPC, or maybe things aren't routed properly in your route tables. You need to do basic network diagnostics, like a ping out, and a curl in to something like a basic Apache / Nginx install. If that doesn't work please edit your question to add everything you've done, and screenshots of your VPC / networking setup.
    – Tim
    Commented Aug 13, 2019 at 17:37
  • I have added some diags
    – pietrodito
    Commented Aug 13, 2019 at 19:21
  • Suggest you install nginx and run it on port 80, then try the remote curl again. I still suspect firewall is the problem, but we don't have quite enough to say that for sure yet.
    – Tim
    Commented Aug 13, 2019 at 19:51

2 Answers 2

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.

2
  • The curl on localhost works. And the HTTP rule has been checked (I test it with an Apache server and it works). I do not understand properly the output of iptables and netstat. I do not how to check if other programs are binding to port 80 (Apache is now uninstalled).
    – pietrodito
    Commented Aug 13, 2019 at 15:45
  • I have added some diags
    – pietrodito
    Commented Aug 13, 2019 at 19:21
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)))

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .