r11492: add pretty-data-ut
[kmrcl.git] / sockets.lisp
1 ;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10; Package: modlisp -*-
2 ;;;; *************************************************************************
3 ;;;; FILE IDENTIFICATION
4 ;;;;
5 ;;;; Name:          sockets.lisp
6 ;;;; Purpose:       Socket functions
7 ;;;; Programmer:    Kevin M. Rosenberg with excerpts from portableaserve
8 ;;;; Date Started:  Jun 2003
9 ;;;;
10 ;;;; $Id$
11 ;;;; *************************************************************************
12
13 (in-package #:kmrcl)
14
15 (eval-when (:compile-toplevel :load-toplevel :execute)
16   #+sbcl (require :sb-bsd-sockets)
17   #+lispworks (require "comm")
18   #+allegro (require :socket))
19
20
21 #+sbcl
22 (defun listen-to-inet-port (&key (port 0) (kind :stream) (reuse nil))
23   "Create, bind and listen to an inet socket on *:PORT.
24 setsockopt SO_REUSEADDR if :reuse is not nil"
25   (let ((socket (make-instance 'sb-bsd-sockets:inet-socket
26                                :type :stream
27                                :protocol :tcp)))
28     (if reuse
29         (setf (sb-bsd-sockets:sockopt-reuse-address socket) t))
30     (sb-bsd-sockets:socket-bind
31      socket (sb-bsd-sockets:make-inet-address "0.0.0.0") port)
32     (sb-bsd-sockets:socket-listen socket 15)
33     socket))
34
35 (defun create-inet-listener (port &key (format :text) (reuse-address t))
36   #+cmu (declare (ignore format reuse-address))
37   #+cmu (ext:create-inet-listener port)
38   #+allegro
39   (socket:make-socket :connect :passive :local-port port :format format
40                       :address-family
41                       (if (stringp port)
42                           :file
43                         (if (or (null port) (integerp port))
44                             :internet
45                           (error "illegal value for port: ~s" port)))
46                       :reuse-address reuse-address)
47   #+sbcl (declare (ignore format))
48   #+sbcl (listen-to-inet-port :port port :reuse reuse-address)
49   #+clisp (declare (ignore format reuse-address))
50   #+clisp (ext:socket-server port)
51   #+openmcl
52   (declare (ignore format))
53   #+openmcl
54   (ccl:make-socket :connect :passive :local-port port
55                    :reuse-address reuse-address)
56   #-(or allegro clisp cmu sbcl openmcl)
57   (warn "create-inet-listener not supported on this implementation")
58   )
59
60 (defun make-fd-stream (socket &key input output element-type)
61   #+cmu
62   (sys:make-fd-stream socket :input input :output output
63                       :element-type element-type)
64   #+sbcl
65   (sb-bsd-sockets:socket-make-stream socket :input input :output output
66                                      :element-type element-type)
67   #-(or cmu sbcl) (declare (ignore input output element-type))
68   #-(or cmu sbcl) socket
69   )
70
71
72 (defun accept-tcp-connection (listener)
73   "Returns (VALUES stream socket)"
74   #+allegro
75   (let ((sock (socket:accept-connection listener)))
76     (values sock sock))
77   #+clisp
78   (let ((sock (ext:socket-accept listener)))
79     (values sock sock))
80   #+cmu
81   (progn
82     (mp:process-wait-until-fd-usable listener :input)
83     (let ((sock (nth-value 0 (ext:accept-tcp-connection listener))))
84       (values (sys:make-fd-stream sock :input t :output t) sock)))
85   #+sbcl
86   (when (sb-sys:wait-until-fd-usable
87          (sb-bsd-sockets:socket-file-descriptor listener) :input)
88     (let ((sock (sb-bsd-sockets:socket-accept listener)))
89       (values
90        (sb-bsd-sockets:socket-make-stream
91         sock :element-type :default :input t :output t)
92        sock)))
93   #+openmcl
94   (let ((sock (ccl:accept-connection listener :wait t)))
95     (values sock sock))
96   #-(or allegro clisp cmu sbcl openmcl)
97   (warn "accept-tcp-connection not supported on this implementation")
98   )
99
100
101 (defmacro errorset (form display)
102   `(handler-case
103     ,form
104     (error (e)
105      (declare (ignorable e))
106      (when ,display
107        (format t "~&Error: ~A~%" e)))))
108
109 (defun close-passive-socket (socket)
110   #+allegro (close socket)
111   #+clisp (ext:socket-server-close socket)
112   #+cmu (unix:unix-close socket)
113   #+sbcl (sb-unix:unix-close
114           (sb-bsd-sockets:socket-file-descriptor socket))
115   #+openmcl (close socket)
116   #-(or allegro clisp cmu sbcl openmcl)
117   (warn "close-passive-socket not supported on this implementation")
118   )
119
120
121 (defun close-active-socket (socket)
122   #+sbcl (sb-bsd-sockets:socket-close socket)
123   #-sbcl (close socket))
124
125 (defun ipaddr-to-dotted (ipaddr &key values)
126   "Convert from 32-bit integer to dotted string."
127   (declare (type (unsigned-byte 32) ipaddr))
128   (let ((a (logand #xff (ash ipaddr -24)))
129         (b (logand #xff (ash ipaddr -16)))
130         (c (logand #xff (ash ipaddr -8)))
131         (d (logand #xff ipaddr)))
132     (if values
133         (values a b c d)
134       (format nil "~d.~d.~d.~d" a b c d))))
135
136 (defun dotted-to-ipaddr (dotted &key (errorp t))
137   "Convert from dotted string to 32-bit integer."
138   (declare (string dotted))
139   (if errorp
140       (let ((ll (delimited-string-to-list dotted #\.)))
141         (+ (ash (parse-integer (first ll)) 24)
142            (ash (parse-integer (second ll)) 16)
143            (ash (parse-integer (third ll)) 8)
144            (parse-integer (fourth ll))))
145     (ignore-errors
146       (let ((ll (delimited-string-to-list dotted #\.)))
147         (+ (ash (parse-integer (first ll)) 24)
148            (ash (parse-integer (second ll)) 16)
149            (ash (parse-integer (third ll)) 8)
150            (parse-integer (fourth ll)))))))
151
152 #+sbcl
153 (defun ipaddr-to-hostname (ipaddr &key ignore-cache)
154   (when ignore-cache
155     (warn ":IGNORE-CACHE keyword in IPADDR-TO-HOSTNAME not supported."))
156   (sb-bsd-sockets:host-ent-name
157    (sb-bsd-sockets:get-host-by-address
158     (sb-bsd-sockets:make-inet-address ipaddr))))
159
160 #+sbcl
161 (defun lookup-hostname (host &key ignore-cache)
162   (when ignore-cache
163     (warn ":IGNORE-CACHE keyword in LOOKUP-HOSTNAME not supported."))
164   (if (stringp host)
165       (sb-bsd-sockets:host-ent-address
166        (sb-bsd-sockets:get-host-by-name host))
167       (dotted-to-ipaddr (ipaddr-to-dotted host))))
168
169
170 (defun make-active-socket (server port)
171   "Returns (VALUES STREAM SOCKET)"
172   #+allegro
173   (let ((sock (socket:make-socket :remote-host server
174                                   :remote-port port)))
175     (values sock sock))
176   #+lispworks
177   (let ((sock (comm:open-tcp-stream server port)))
178     (values sock sock))
179   #+sbcl
180   (let ((sock (make-instance 'sb-bsd-sockets:inet-socket
181                              :type :stream
182                              :protocol :tcp)))
183     (sb-bsd-sockets:socket-connect sock (lookup-hostname server) port)
184     (values
185      (sb-bsd-sockets:socket-make-stream
186       sock :input t :output t :element-type :default)
187      sock))
188   #+cmu
189   (let ((sock (ext:connect-to-inet-socket server port)))
190     (values
191      (sys:make-fd-stream sock :input t :output t :element-type 'base-char)
192      sock))
193   #+clisp
194   (let ((sock (ext:socket-connect port server)))
195     (values sock sock))
196   #+openmcl
197   (let ((sock (ccl:make-socket :remote-host server :remote-port port )))
198     (values sock sock))
199   )
200
201 (defun ipaddr-array-to-dotted (array)
202   (format nil "~{~D~^.~}" (coerce array 'list))
203   #+ignore
204   (format nil "~D.~D.~D.~D"
205           (aref 0 array) (aref 1 array) (aref 2 array) (array 3 array)))
206
207 (defun remote-host (socket)
208   #+allegro (socket:ipaddr-to-dotted (socket:remote-host socket))
209   #+lispworks (nth-value 0 (comm:get-socket-peer-address socket))
210   #+sbcl (ipaddr-array-to-dotted
211           (nth-value 0 (sb-bsd-sockets:socket-peername socket)))
212   #+cmu (nth-value 0 (ext:get-peer-host-and-port socket))
213   #+clisp (let* ((peer (ext:socket-stream-peer socket t))
214                 (stop (position #\Space peer)))
215            ;; 2.37-2.39 had do-not-resolve-p backwards
216            (if stop (subseq peer 0 stop) peer))
217   #+openmcl (ccl:remote-host socket)
218   )
219