PageRenderTime 39ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/src/sockets/make-socket.lisp

https://bitbucket.org/sionescu/iolib
Lisp | 423 lines | 355 code | 48 blank | 20 comment | 0 complexity | a52fda4edeac8f322ad7d5c04996f9e6 MD5 | raw file
  1. ;;;; -*- Mode: Lisp; indent-tabs-mode: nil -*-
  2. ;;;
  3. ;;; --- Socket creation.
  4. ;;;
  5. (in-package :iolib/sockets)
  6. (eval-when (:compile-toplevel :load-toplevel :execute)
  7. (defparameter *socket-type-map*
  8. '(((:ipv4 :stream :active) . socket-stream-internet-active)
  9. ((:ipv6 :stream :active) . socket-stream-internet-active)
  10. ((:ipv4 :stream :passive) . socket-stream-internet-passive)
  11. ((:ipv6 :stream :passive) . socket-stream-internet-passive)
  12. ((:local :stream :active) . socket-stream-local-active)
  13. ((:local :stream :passive) . socket-stream-local-passive)
  14. ((:local :datagram nil) . socket-datagram-local)
  15. ((:ipv4 :datagram nil) . socket-datagram-internet)
  16. ((:ipv6 :datagram nil) . socket-datagram-internet)
  17. ((:ipv4 :raw nil) . socket-raw-internet)
  18. #+linux
  19. ((:netlink :raw nil) . socket-raw-netlink)))
  20. (defun select-socket-class (address-family type connect)
  21. (or (loop :for ((sock-family sock-type sock-connect) . class)
  22. :in *socket-type-map*
  23. :when (and (eql sock-family address-family)
  24. (eql sock-type type)
  25. (if sock-connect (eql sock-connect connect) t))
  26. :return class)
  27. (error "No socket class found !!"))))
  28. (defun create-socket (family type protocol
  29. &rest args &key connect fd &allow-other-keys)
  30. (apply #'make-instance (select-socket-class family type connect)
  31. :address-family family
  32. :protocol protocol
  33. :file-descriptor fd
  34. (remove-from-plist args :connect)))
  35. (define-compiler-macro create-socket (&whole form &environment env
  36. family type protocol
  37. &rest args &key connect fd &allow-other-keys)
  38. (cond
  39. ((and (constantp family env) (constantp type env) (constantp connect env))
  40. `(make-instance ',(select-socket-class family type connect)
  41. :file-descriptor ,fd
  42. :address-family ,family
  43. :protocol ,protocol
  44. ,@(remove-from-plist args :connect)))
  45. (t form)))
  46. (defmacro with-close-on-error ((var value) &body body)
  47. "Bind `VAR' to `VALUE' and execute `BODY' as implicit PROGN.
  48. If a non-local exit occurs during the execution of `BODY',
  49. call CLOSE with :ABORT T on `VAR'."
  50. `(let ((,var ,value))
  51. (unwind-protect-case () ,@body
  52. (:abort (close ,var :abort t)))))
  53. (defmacro %create-internet-socket (family &rest args)
  54. `(case ,family
  55. (:ipv4 (create-socket :ipv4 ,@args))
  56. (:ipv6 (create-socket :ipv6 ,@args))))
  57. (eval-when (:compile-toplevel :load-toplevel :execute)
  58. (defun make-first-level-name (family type connect)
  59. (if (eql :stream type)
  60. (format-symbol :iolib/sockets "%~A/~A-~A-~A" :make-socket family type connect)
  61. (format-symbol :iolib/sockets "%~A/~A-~A" :make-socket family type))))
  62. (defmacro define-socket-creator ((socket-family socket-type &optional socket-connect)
  63. (family protocol key &rest parameters) &body body)
  64. (assert (eql '&key key))
  65. (flet ((maybe-quote-default-value (arg)
  66. (cond ((symbolp arg) arg)
  67. ((consp arg) (list (first arg) `(quote ,(second arg))))))
  68. (arg-name (arg)
  69. (car (ensure-list arg)))
  70. (quotify (form)
  71. `(list (quote ,(car form)) ,@(cdr form))))
  72. (let* ((parameter-names (mapcar #'arg-name parameters))
  73. (first-level-function (make-first-level-name socket-family socket-type socket-connect))
  74. (second-level-function (format-symbol t "%~A" first-level-function)))
  75. (flet ((make-first-level-body (family protocol)
  76. `(,second-level-function ,family ,protocol ,@parameter-names)))
  77. `(progn
  78. (declaim (inline ,second-level-function))
  79. (defun ,second-level-function (,family ,protocol ,@parameter-names) ,@body)
  80. (defun ,first-level-function (arguments family protocol)
  81. (destructuring-bind (&key ,@parameters)
  82. arguments
  83. ,(make-first-level-body family protocol)))
  84. (define-compiler-macro ,first-level-function (&whole form arguments family protocol)
  85. ;; Must quote default values in order for them not to be evaluated
  86. ;; in the compilation environment
  87. (if (listp arguments)
  88. (destructuring-bind (&key ,@(mapcar #'maybe-quote-default-value parameters))
  89. (cdr arguments)
  90. ,(quotify (make-first-level-body family protocol)))
  91. form)))))))
  92. ;;; Internet Stream Active Socket creation
  93. (defun %%init-socket/internet-stream-active (socket keepalive nodelay reuse-address
  94. local-host local-port remote-host remote-port)
  95. (setf (socket-option socket :no-sigpipe :if-does-not-exist nil) t)
  96. (when keepalive (setf (socket-option socket :keep-alive) t))
  97. (when nodelay (setf (socket-option socket :tcp-nodelay) t))
  98. (when local-host
  99. (bind-address socket (ensure-hostname local-host)
  100. :port local-port
  101. :reuse-address reuse-address))
  102. (when remote-host
  103. (connect socket (ensure-hostname remote-host)
  104. :port remote-port))
  105. (values socket))
  106. (define-socket-creator (:internet :stream :active)
  107. (family protocol &key external-format
  108. keepalive nodelay (reuse-address t)
  109. local-host local-port remote-host remote-port
  110. input-buffer-size output-buffer-size)
  111. (with-close-on-error (socket (%create-internet-socket family :stream protocol
  112. :connect :active
  113. :external-format external-format
  114. :input-buffer-size input-buffer-size
  115. :output-buffer-size output-buffer-size))
  116. (%%init-socket/internet-stream-active socket keepalive nodelay reuse-address
  117. local-host (or local-port 0) remote-host remote-port)))
  118. ;;; Internet Stream Passive Socket creation
  119. (defun %%init-socket/internet-stream-passive (socket interface reuse-address
  120. local-host local-port backlog)
  121. (when local-host
  122. (when interface
  123. (setf (socket-option socket :bind-to-device) interface))
  124. (bind-address socket (ensure-hostname local-host)
  125. :port local-port
  126. :reuse-address reuse-address)
  127. (listen-on socket :backlog backlog))
  128. (values socket))
  129. (define-socket-creator (:internet :stream :passive)
  130. (family protocol &key external-format
  131. interface (reuse-address t)
  132. local-host local-port backlog)
  133. (with-close-on-error (socket (%create-internet-socket family :stream protocol
  134. :connect :passive
  135. :external-format external-format))
  136. (%%init-socket/internet-stream-passive socket interface reuse-address
  137. local-host (or local-port 0)
  138. (or backlog *default-backlog-size*))))
  139. ;;; Local Stream Active Socket creation
  140. (defun %%init-socket/local-stream-active (socket local-filename remote-filename)
  141. (setf (socket-option socket :no-sigpipe :if-does-not-exist nil) t)
  142. (when local-filename
  143. (bind-address socket (ensure-address local-filename :family :local)))
  144. (when remote-filename
  145. (connect socket (ensure-address remote-filename :family :local)))
  146. (values socket))
  147. (define-socket-creator (:local :stream :active)
  148. (family protocol &key external-format local-filename remote-filename
  149. input-buffer-size output-buffer-size)
  150. (with-close-on-error (socket (create-socket family :stream protocol
  151. :connect :active
  152. :external-format external-format
  153. :input-buffer-size input-buffer-size
  154. :output-buffer-size output-buffer-size))
  155. (%%init-socket/local-stream-active socket local-filename remote-filename)))
  156. ;;; Local Stream Passive Socket creation
  157. (defun %%init-socket/local-stream-passive (socket local-filename reuse-address backlog)
  158. (when local-filename
  159. (bind-address socket (ensure-address local-filename :family :local)
  160. :reuse-address reuse-address)
  161. (listen-on socket :backlog backlog))
  162. (values socket))
  163. (define-socket-creator (:local :stream :passive)
  164. (family protocol &key external-format local-filename (reuse-address t) backlog)
  165. (with-close-on-error (socket (create-socket family :stream protocol
  166. :connect :passive
  167. :external-format external-format))
  168. (%%init-socket/local-stream-passive socket local-filename reuse-address
  169. (or backlog *default-backlog-size*))))
  170. ;;; Internet Datagram Socket creation
  171. (defun %%init-socket/internet-datagram (socket broadcast interface reuse-address
  172. local-host local-port remote-host remote-port)
  173. (setf (socket-option socket :no-sigpipe :if-does-not-exist nil) t)
  174. (when broadcast (setf (socket-option socket :broadcast) t))
  175. (when local-host
  176. (bind-address socket (ensure-hostname local-host)
  177. :port local-port
  178. :reuse-address reuse-address)
  179. (when interface
  180. (setf (socket-option socket :bind-to-device) interface)))
  181. (when remote-host
  182. (connect socket (ensure-hostname remote-host)
  183. :port remote-port))
  184. (values socket))
  185. (define-socket-creator (:internet :datagram)
  186. (family protocol &key broadcast interface (reuse-address t)
  187. local-host local-port remote-host remote-port)
  188. (with-close-on-error (socket (%create-internet-socket family :datagram protocol))
  189. (%%init-socket/internet-datagram socket broadcast interface reuse-address
  190. local-host (or local-port 0)
  191. remote-host (or remote-port 0))))
  192. ;;; Local Datagram Socket creation
  193. (defun %%init-socket/local-datagram (socket local-filename remote-filename)
  194. (setf (socket-option socket :no-sigpipe :if-does-not-exist nil) t)
  195. (when local-filename
  196. (bind-address socket (ensure-address local-filename :family :local)))
  197. (when remote-filename
  198. (connect socket (ensure-address remote-filename :family :local)))
  199. (values socket))
  200. (define-socket-creator (:local :datagram)
  201. (family protocol &key local-filename remote-filename)
  202. (with-close-on-error (socket (create-socket family :datagram protocol))
  203. (%%init-socket/local-datagram socket local-filename remote-filename)))
  204. ;;; Raw Socket creation
  205. (defun %%init-socket/internet-raw (socket include-headers)
  206. (setf (socket-option socket :no-sigpipe :if-does-not-exist nil) t)
  207. (setf (socket-option socket :ip-header-include) include-headers)
  208. (values socket))
  209. (define-socket-creator (:internet :raw)
  210. (family protocol &key include-headers)
  211. (with-close-on-error (socket (create-socket family :raw protocol))
  212. (%%init-socket/internet-raw socket include-headers)))
  213. ;;; Netlink Socket creation
  214. #+linux
  215. (defun %%init-socket/netlink-raw (socket local-port multicast-groups)
  216. (when local-port
  217. (bind-address socket
  218. (make-instance 'netlink-address
  219. :multicast-groups multicast-groups)
  220. :port local-port))
  221. (values socket))
  222. #+linux
  223. (define-socket-creator (:netlink :raw)
  224. (family protocol &key (local-port 0) (multicast-groups 0))
  225. (with-close-on-error (socket (create-socket family :raw protocol))
  226. (%%init-socket/netlink-raw socket local-port multicast-groups)))
  227. #-linux
  228. (define-socket-creator (:netlink :raw)
  229. (family protocol &key local-port multicast-groups)
  230. (declare (ignore family protocol local-port multicast-groups))
  231. (error 'socket-address-family-not-supported-error))
  232. ;;; MAKE-SOCKET
  233. (defmethod make-socket (&rest args &key (address-family :internet) (type :stream) (protocol :default)
  234. (connect :active) (ipv6 *ipv6*) &allow-other-keys)
  235. (when (eql :file address-family) (setf address-family :local))
  236. (check-type address-family (member :internet :local :ipv4 :ipv6 :netlink)
  237. "one of :INTERNET, :LOCAL(or :FILE), :IPV4, :IPV6 or :NETLINK")
  238. (check-type type (member :stream :datagram :raw) "either :STREAM, :DATAGRAM or :RAW")
  239. (check-type connect (member :active :passive) "either :ACTIVE or :PASSIVE")
  240. (let ((args (remove-from-plist args :address-family :type :protocol :connect :ipv6)))
  241. (when (eql :ipv4 address-family) (setf ipv6 nil))
  242. (let ((*ipv6* ipv6))
  243. (when (eql :internet address-family) (setf address-family +default-inet-address-family+))
  244. (multiple-value-case ((address-family type connect))
  245. ((:ipv4 :stream :active)
  246. (%make-socket/internet-stream-active args :ipv4 :default))
  247. ((:ipv6 :stream :active)
  248. (%make-socket/internet-stream-active args :ipv6 :default))
  249. ((:ipv4 :stream :passive)
  250. (%make-socket/internet-stream-passive args :ipv4 :default))
  251. ((:ipv6 :stream :passive)
  252. (%make-socket/internet-stream-passive args :ipv6 :default))
  253. ((:local :stream :active)
  254. (%make-socket/local-stream-active args :local :default))
  255. ((:local :stream :passive)
  256. (%make-socket/local-stream-passive args :local :default))
  257. ((:ipv4 :datagram)
  258. (%make-socket/internet-datagram args :ipv4 :default))
  259. ((:ipv6 :datagram)
  260. (%make-socket/internet-datagram args :ipv6 :default))
  261. ((:local :datagram)
  262. (%make-socket/local-datagram args :local :default))
  263. ((:ipv4 :raw)
  264. (%make-socket/internet-raw args :ipv4 protocol))
  265. ((:netlink :raw)
  266. (%make-socket/netlink-raw args :netlink protocol))))))
  267. (define-compiler-macro make-socket (&whole form &environment env &rest args
  268. &key (address-family :internet) (type :stream) (protocol :default)
  269. (connect :active) (ipv6 '*ipv6* ipv6p) &allow-other-keys)
  270. (when (eql :file address-family) (setf address-family :local))
  271. (cond
  272. ((and (constantp address-family env) (constantp type env) (constantp connect env))
  273. (check-type address-family (member :internet :local :ipv4 :ipv6 :netlink)
  274. "one of :INTERNET, :LOCAL(or :FILE), :IPV4, :IPV6 or :NETLINK")
  275. (check-type type (member :stream :datagram :raw) "either :STREAM, :DATAGRAM or :RAW")
  276. (check-type connect (member :active :passive) "either :ACTIVE or :PASSIVE")
  277. (let* ((family (if (member address-family '(:ipv4 :ipv6)) :internet address-family))
  278. (lower-function (make-first-level-name family type connect))
  279. (args (remove-from-plist args :address-family :type :protocol :connect :ipv6)))
  280. (case address-family
  281. (:internet (setf address-family '+default-inet-address-family+))
  282. (:ipv4 (setf ipv6 nil ipv6p t)))
  283. (let ((expansion `(,lower-function (list ,@args) ,address-family ,protocol)))
  284. (if ipv6p `(let ((*ipv6* ,ipv6)) ,expansion) expansion))))
  285. (t form)))
  286. (defmacro with-open-socket ((var &rest args) &body body)
  287. "Bind VAR to a socket created by passing ARGS to MAKE-SOCKET and execute BODY as implicit PROGN.
  288. The socket is automatically closed upon exit."
  289. `(with-open-stream (,var (make-socket ,@args)) ,@body))
  290. (defmacro with-accept-connection ((var passive-socket &rest args) &body body)
  291. "Bind VAR to a socket created by passing PASSIVE-SOCKET and ARGS to ACCEPT-CONNECTION and execute BODY as implicit PROGN.
  292. The socket is automatically closed upon exit."
  293. `(with-open-stream (,var (accept-connection ,passive-socket ,@args)) ,@body))
  294. ;;; MAKE-SOCKET-FROM-FD
  295. ;;; FIXME: must come up with a way to find out
  296. ;;; whether a socket is active or passive
  297. (defmethod make-socket-from-fd ((fd integer) &key (dup t) (connect :active) (external-format :default)
  298. input-buffer-size output-buffer-size)
  299. (flet ((%get-address-family (fd)
  300. (with-sockaddr-storage-and-socklen (ss size)
  301. (%getsockname fd ss size)
  302. (eswitch ((foreign-slot-value ss 'sockaddr-storage 'family) :test #'=)
  303. (af-inet :ipv4)
  304. (af-inet6 :ipv6)
  305. (af-local :local)
  306. #+linux
  307. (af-netlink :netlink))))
  308. (%get-type (fd)
  309. (eswitch ((get-socket-option-int fd sol-socket so-type) :test #'=)
  310. (sock-stream :stream)
  311. (sock-dgram :datagram)
  312. (sock-raw :raw))))
  313. (create-socket (%get-address-family fd)
  314. (%get-type fd)
  315. :default
  316. :connect connect
  317. :fd fd
  318. :dup dup
  319. :external-format external-format
  320. :input-buffer-size input-buffer-size
  321. :output-buffer-size output-buffer-size)))
  322. ;;; MAKE-SOCKET-PAIR
  323. (defmethod make-socket-pair (&key (type :stream) (protocol :default) (external-format :default)
  324. input-buffer-size output-buffer-size)
  325. (flet ((%make-socket-pair (fd)
  326. (make-socket-from-fd fd :dup nil
  327. :external-format external-format
  328. :input-buffer-size input-buffer-size
  329. :output-buffer-size output-buffer-size)))
  330. (multiple-value-bind (fd1 fd2)
  331. (multiple-value-call #'%socketpair
  332. (translate-make-socket-keywords-to-constants :local type protocol))
  333. (values (%make-socket-pair fd1)
  334. (%make-socket-pair fd2)))))
  335. ;;; SEND/RECEIVE-FILE-DESCRIPTOR
  336. (defun call-with-buffers-for-fd-passing (fn)
  337. (with-foreign-object (msg 'msghdr)
  338. (isys:bzero msg (isys:sizeof 'msghdr))
  339. (with-foreign-pointer (buffer #.(isys:cmsg.space (isys:sizeof :int))
  340. buffer-size)
  341. (isys:bzero buffer buffer-size)
  342. (with-foreign-slots ((control controllen) msg msghdr)
  343. (setf control buffer
  344. controllen buffer-size)
  345. (let ((cmsg (isys:cmsg.firsthdr msg)))
  346. (with-foreign-slots ((len level type) cmsg cmsghdr)
  347. (setf len (isys:cmsg.len (isys:sizeof :int))
  348. level sol-socket
  349. type scm-rights)
  350. (funcall fn msg cmsg)))))))
  351. (defmacro with-buffers-for-fd-passing ((msg-var cmsg-var) &body body)
  352. `(call-with-buffers-for-fd-passing (lambda (,msg-var ,cmsg-var) ,@body)))
  353. (defmethod send-file-descriptor ((socket local-socket) file-descriptor)
  354. (with-buffers-for-fd-passing (msg cmsg)
  355. (let ((data (isys:cmsg.data cmsg)))
  356. (setf (mem-aref data :int) file-descriptor)
  357. (%sendmsg (fd-of socket) msg 0)
  358. (values))))
  359. (defmethod receive-file-descriptor ((socket local-socket))
  360. (with-buffers-for-fd-passing (msg cmsg)
  361. (let ((data (isys:cmsg.data cmsg)))
  362. (%recvmsg (fd-of socket) msg 0)
  363. (mem-aref data :int))))