PageRenderTime 53ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/socket-options.scm

https://bitbucket.org/ursetto/rfc2553
Scheme | 394 lines | 238 code | 52 blank | 104 comment | 0 complexity | 6d45be6bdaa5e8b90104a4018e654782 MD5 | raw file
  1. ;;; Local macros
  2. ;;(require-library srfi-13) ;;?
  3. (import-for-syntax srfi-13)
  4. ;; ;; (local 'ip/multicast-ttl) => '_ip_multicast_ttl
  5. ;; (define-for-syntax (local s)
  6. ;; (string->symbol
  7. ;; (string-append "_" (string-translate (symbol->string s) "/-" #\_))))
  8. ;; (local 'ip/multicast-ttl) => 'IP_MULTICAST_TTL
  9. (define-for-syntax (local sym)
  10. (string->symbol
  11. (string-translate (string-upcase (symbol->string sym)) "/-" #\_)))
  12. ;; (c-name 'ip/multicast-ttl) => "IP_MULTICAST_TTL"
  13. (define-for-syntax (c-name sym)
  14. (string-translate (string-upcase (symbol->string sym)) "/-" #\_))
  15. ;; (define-socket-int so/reuseaddr) =>
  16. ;; (begin (define-foreign-variable SO_REUSEADDR "SO_REUSEADDR")
  17. ;; (define so/reuseaddr (if (= SO_REUSEADDR -1) #f SO_REUSEADDR)))
  18. (define-syntax define-socket-int
  19. (er-macro-transformer
  20. (lambda (e r c)
  21. (let ((sym (cadr e))
  22. (str (cddr e)))
  23. (let ((str (if (pair? str) (car str) (c-name sym)))
  24. (lname (local sym)))
  25. `(,(r 'begin)
  26. (,(r 'define-foreign-variable) ,lname ,(r 'int) ,str)
  27. (,(r 'define) ,sym ,lname)))))))
  28. (define-syntax define-socket-ints
  29. (er-macro-transformer
  30. (lambda (e r c)
  31. `(,(r 'begin)
  32. ,@(map (lambda (sym)
  33. (if (pair? sym)
  34. `(,(r 'define-socket-int) ,(car sym) ,(cadr sym))
  35. `(,(r 'define-socket-int) ,sym)))
  36. (cdr e))))))
  37. ;; (define-optional-socket-int so/reuseaddr)
  38. ;; => (cond-expand (SO_REUSEADDR
  39. ;; (define-foreign-variable SO_REUSEADDR int "SO_REUSEADDR")
  40. ;; (define so/reuseaddr SO_REUSEADDR))
  41. ;; (else
  42. ;; (define so/reuseaddr #f))))
  43. (define-syntax define-optional-socket-int
  44. (er-macro-transformer
  45. (lambda (e r c)
  46. (let ((sym (cadr e))
  47. (str (cddr e)))
  48. (let ((str (if (pair? str) (->string (car str)) (c-name sym)))
  49. (lname (local sym)))
  50. (let ((feat (string->symbol str)))
  51. `(,(r 'cond-expand)
  52. (,feat
  53. (,(r 'define-foreign-variable) ,lname ,(r 'int) ,str)
  54. (,(r 'define) ,sym ,lname))
  55. (,(r 'else)
  56. (,(r 'define) ,sym #f)))))))))
  57. (define-syntax define-optional-socket-ints
  58. (er-macro-transformer
  59. (lambda (e r c)
  60. `(,(r 'begin)
  61. ,@(map (lambda (sym)
  62. (if (pair? sym)
  63. `(,(r 'define-optional-socket-int) ,(car sym) ,(cadr sym))
  64. `(,(r 'define-optional-socket-int) ,sym)))
  65. (cdr e))))))
  66. ;; (define-socket-option ipv6-v6-only? ipproto/ipv6 ipv6/v6only set-boolean-option get-boolean-option) =>
  67. ;; (begin
  68. ;; (define ipv6-v6-only?
  69. ;; (if (or (= _ipproto_ipv6 -1) (= _ipv6_v6only -1))
  70. ;; (getter-with-setter
  71. ;; (lambda (s)
  72. ;; (unsupported-error 'ipv6-v6-only? "socket option or level unsupported"))
  73. ;; (lambda (s v)
  74. ;; (unsupported-error 'ipv6-v6-only? "socket option or level unsupported")))
  75. ;; (getter-with-setter
  76. ;; (lambda (s) (get-boolean-option s _ipproto_ipv6 _ipv6_v6only))
  77. ;; (lambda (s v) (set-boolean-option s _ipproto_ipv6 _ipv6_v6only v))))))
  78. ;; When option or level undefined, define the procedure to simply
  79. ;; return a nice error. We could pass an invalid option or level
  80. ;; (such as -1) through to get/setsockopt, but this is more meaningful
  81. ;; and safer. (Note this does use the foreign-vars instead of
  82. ;; the constants, so it needs to test for -1 instead of #f.)
  83. (define (unsupported-socket-option name)
  84. ;; Deduplicates code in define-socket-option. More savings could
  85. ;; be achieved by not printing "name".
  86. (unsupported-error name "socket option unavailable on this platform"))
  87. (define-syntax define-socket-option
  88. (er-macro-transformer
  89. (lambda (e r c)
  90. (let ((name (cadr e))
  91. (level (caddr e))
  92. (optname (cadddr e))
  93. (set (car (cddddr e)))
  94. (get (cadr (cddddr e))))
  95. `(,(r 'define) ,name
  96. (,(r 'getter-with-setter)
  97. (,(r 'lambda) (s) (,get ',name s ,(local level) ,(local optname)))
  98. (,(r 'lambda) (s v) (,set ',name s ,(local level) ,(local optname) v))))))))
  99. (define-syntax define-boolean-option
  100. (syntax-rules ()
  101. ((_ name level optname)
  102. (define-socket-option name level optname set-boolean-option get-boolean-option))))
  103. (define-syntax define-integer-option
  104. (syntax-rules ()
  105. ((_ name level optname)
  106. (define-socket-option name level optname set-integer-option get-integer-option))))
  107. ;; Like define-socket-option, but performs a feature test on the level and optname,
  108. ;; choosing whether option is supported at compile time instead of runtime.
  109. ;; Assumes foreign variables have been declared by define-optional-socket-ints.
  110. (define-syntax define-optional-socket-option
  111. (er-macro-transformer
  112. (lambda (e r c)
  113. (define (feature-name x) (string->symbol (c-name x)))
  114. (let ((name (cadr e))
  115. (level (caddr e))
  116. (optname (cadddr e))
  117. (set (car (cddddr e)))
  118. (get (cadr (cddddr e)))
  119. (%unsup (gensym)))
  120. `(,(r 'define) ,name
  121. (,(r 'cond-expand)
  122. ((,(r 'and) ,(feature-name level) ,(feature-name optname))
  123. (,(r 'getter-with-setter)
  124. (,(r 'lambda) (s) (,get ',name s ,(local level) ,(local optname)))
  125. (,(r 'lambda) (s v) (,set ',name s ,(local level) ,(local optname) v))))
  126. (,(r 'else)
  127. (,(r 'let) ((,%unsup
  128. (,(r 'lambda) _
  129. (,(r 'unsupported-socket-option) ',name))))
  130. (,(r 'getter-with-setter) ,%unsup ,%unsup)))))))))
  131. (define-syntax define-optional-boolean-option
  132. (syntax-rules ()
  133. ((_ name level optname)
  134. (define-optional-socket-option name level optname
  135. set-boolean-option get-boolean-option))))
  136. (define-syntax define-optional-integer-option
  137. (syntax-rules ()
  138. ((_ name level optname)
  139. (define-optional-socket-option name level optname
  140. set-integer-option get-integer-option))))
  141. ;;; FFI
  142. (define setsockopt (foreign-lambda int "setsockopt" int int int scheme-pointer int))
  143. (define getsockopt (foreign-lambda int "typecorrect_getsockopt" int int int scheme-pointer c-pointer))
  144. (define setsockopt/int
  145. (foreign-lambda* int ((int sock) (int level) (int name) (int val))
  146. "return(setsockopt(sock, level, name, (const void *)&val, sizeof(val)));"))
  147. (define getsockopt/int
  148. (foreign-lambda* int ((int sock) (int level) (int name) ((c-pointer int) ret))
  149. "socklen_t sz = sizeof(*ret);"
  150. "return(typecorrect_getsockopt(sock, level, name, ret, &sz));"))
  151. ;;; getters and setters
  152. (define-inline (check-boolean where x)
  153. (unless (boolean? x)
  154. (type-error where "bad argument type: not a boolean" x)))
  155. (define-inline (check-error where err)
  156. (let ((no errno))
  157. (when (fx= -1 err)
  158. (if (or (fx= no _enoprotoopt) ;; False + on Win for e.g. sock/dgram when stream expected
  159. (fx= no _einval)) ;; Maybe incorrect level; but false + on dgram when stream expected
  160. (unsupported-error where (strerror no))
  161. (begin
  162. (##sys#update-errno)
  163. (##sys#signal-hook #:network-error where (strerror no)))))))
  164. (define (set-integer-option where s level name val)
  165. (##sys#check-exact val where)
  166. (let ((s (if (socket? s) (socket-fileno s) s)))
  167. (let ((err (setsockopt/int s level name val)))
  168. (check-error where err)
  169. (void))))
  170. (define (set-boolean-option where s level name val)
  171. (check-boolean where val)
  172. (set-integer-option where s level name (if val 1 0)))
  173. (define (get-boolean-option where s level name)
  174. (not (= 0 (get-integer-option where s level name))))
  175. (define (get-integer-option where s level name)
  176. (let ((s (if (socket? s) (socket-fileno s) s)))
  177. (let-location ((val int))
  178. (let ((err (getsockopt/int s level name (location val))))
  179. (check-error where err)
  180. val))))
  181. (define (set-readonly-option where s level name val)
  182. (network-error where "socket option is read-only"))
  183. ;;; generic lowlevel interface
  184. ;; This interface is likely to change or go away completely. Complex manipulation
  185. ;; might be easier done in C.
  186. ;; (set-socket-option S ipproto/tcp tcp/nodelay 1)
  187. ;; (set-socket-option S ipproto/tcp tcp/nodelay (make-string 4 #\x0))
  188. ;; (set-socket-option S sol/socket so/rcvlowat (u32vector->blob/shared (u32vector #x01020304)))
  189. ;; (get-socket-option S ipproto/tcp tcp/nodelay)
  190. ;; complex example
  191. #|
  192. (define (make-linger-storage)
  193. (make-blob (foreign-value "sizeof(struct linger)" int)))
  194. (define (encode-linger-option state time)
  195. (let ((blob (make-linger-storage)))
  196. ((foreign-lambda* void ((scheme-pointer ptr) (int onoff) (int linger))
  197. "struct linger *p = ptr;"
  198. "p->l_onoff = onoff; p->l_linger = linger;")
  199. blob state time)
  200. blob))
  201. (define (decode-linger-option blob)
  202. ; sanity checking recommended here
  203. (list ((foreign-lambda* int ((scheme-pointer p)) "return(((struct linger *)p)->l_onoff);") blob)
  204. ((foreign-lambda* int ((scheme-pointer p)) "return(((struct linger *)p)->l_linger);") blob)))
  205. ;; (set-socket-option S sol/socket so/linger (encode-linger-option 1 100))
  206. ;; (decode-linger-option (get-socket-option S sol/socket so/linger (make-linger-storage)))
  207. |#
  208. (define (set-socket-option s level name val)
  209. (cond ((not level)
  210. (unsupported-error 'set-socket-option "socket option level not supported"))
  211. ((not name)
  212. (unsupported-error 'set-socket-option "socket option not supported"))
  213. (else
  214. (let ((s (if (socket? s) (socket-fileno s) s)))
  215. (cond ((boolean? val)
  216. (set-boolean-option 'set-socket-option s level name val))
  217. ((fixnum? val)
  218. (set-integer-option 'set-socket-option s level name val))
  219. ((blob? val)
  220. (check-error 'set-socket-option
  221. (setsockopt s level name val (blob-size val))))
  222. ((string? val)
  223. (check-error 'set-socket-option
  224. (setsockopt s level name val (string-length val))))
  225. (else
  226. (##sys#signal-hook #:type-error
  227. 'set-socket-option
  228. "bad option value" val)))))))
  229. ;; Get socket option on socket S at socket level LEVEL with option name NAME.
  230. ;; If len is #f (the default) it assumes the option is an integer value.
  231. ;; Otherwise allocates temporary space of LEN bytes and copies the result into
  232. ;; a fresh blob of the length returned by the getsockopt() call; returns the blob.
  233. ;; If you know the correct length ahead of time, no copy is done.
  234. ;; (get-socket-option s sol/socket so/reuseaddr 1024) => #${04000000}
  235. ;; (get-socket-option s sol/socket so/reuseaddr) => 4
  236. (define (get-socket-option s level name #!optional len)
  237. (cond ((not level)
  238. (unsupported-error 'get-socket-option "socket option level not supported"))
  239. ((not name)
  240. (unsupported-error 'get-socket-option "socket option not supported"))
  241. ((not len)
  242. (get-integer-option 'get-socket-option s level name))
  243. (else
  244. (let ((buf (make-blob len)))
  245. (let-location ((sz int len))
  246. (let ((s (if (socket? s) (socket-fileno s) s)))
  247. ;; FIXME: Report unsupported error correctly
  248. (check-error 'get-socket-option (getsockopt s level name buf (location sz))))
  249. (if (= sz len)
  250. buf
  251. (let ((retbuf (make-blob sz)))
  252. ((foreign-lambda void C_memcpy scheme-pointer scheme-pointer int)
  253. retbuf buf sz)
  254. retbuf)))))))
  255. ;;; socket integers
  256. ;; Optional socket ints must be defined as foreign features.
  257. (define-optional-socket-ints
  258. so/useloopback so/reuseport so/timestamp so/exclusiveaddruse
  259. tcp/maxseg tcp/nopush tcp/noopt tcp/keepalive
  260. ip/mtu ip/mtu-discover
  261. ip/pktinfo ip/recverr ip/recvtos ip/recvttl ip/router-alert
  262. ip/recvopts ip/recvretopts ip/retopts ip/recvdstaddr
  263. ;; NB There's probably a subset of IPv6 options these that we can require
  264. ;; when IPv6 is enabled (i.e. error out on if undefined).
  265. ipv6/v6only ipv6/addrform ipv6/mtu
  266. ipv6/mtu-discover ipv6/multicast-hops ipv6/multicast-if ipv6/multicast-loop ipv6/pktinfo
  267. ipv6/rthdr ipv6/authhdr ipv6/dstopts ipv6/hopopts ipv6/flowinfo ipv6/hoplimit
  268. ipv6/recverr ipv6/router-alert ipv6/unicast-hops ipv6/nexthop
  269. ipv6/port-range ipv6/join-group ipv6/leave-group ipv6/checksum
  270. ;; ipv6/add-membership ipv6/drop-membership ;; OBSOLETE synonyms for JOIN/LEAVE_GROUP
  271. ;; ipv6/options ipv6/recvopts ipv6/recvretopts ipv6/retopts ipv6/recvdstaddr ;; DEPRECATED
  272. ipproto/ipv6)
  273. (define-socket-ints
  274. ;; socket options
  275. so/reuseaddr so/debug so/acceptconn so/keepalive so/dontroute
  276. so/broadcast so/linger so/oobinline so/sndbuf so/rcvbuf
  277. so/sndlowat so/rcvlowat so/sndtimeo so/rcvtimeo so/error so/type
  278. ;; tcp options
  279. tcp/nodelay
  280. ;; ip options
  281. ip/options ip/hdrincl ip/tos ip/ttl
  282. ip/multicast-if ip/multicast-ttl ip/multicast-loop
  283. ip/add-membership ip/drop-membership
  284. ;; ipv6 options
  285. ;; socket levels
  286. sol/socket ipproto/ip ipproto/icmp
  287. ; ipproto/tcp ipproto/udp ;; already provided in socket.scm
  288. )
  289. ;;; socket-level options
  290. (cond-expand
  291. ((and windows SO_EXCLUSIVEADDRUSE)
  292. ;; Windows semantics of so/reuseaddr are basically nonsense,
  293. ;; so use so/exclusiveaddruse for correct semantics. However,
  294. ;; this may fail without admin privs on WinXP<SP3 and Win2k<SP4,
  295. ;; so on failure fall back to so/reuseaddr (better than nothing).
  296. ;; Also, so/exclusiveaddruse may not be available, so we explicitly feature
  297. ;; test for it; define-socket-option expects the foreign var to be defined,
  298. ;; and define-optional-socket-option won't fall back to so/reuseaddr.
  299. (define (set-reuse-option where s level name val)
  300. (handle-exceptions exn
  301. (set-boolean-option where s level so/reuseaddr val)
  302. (set-boolean-option where s level name val)))
  303. (define (get-reuse-option where s level name)
  304. (handle-exceptions exn
  305. (get-boolean-option where s level so/reuseaddr)
  306. (get-boolean-option where s level name)))
  307. (define-socket-option so-reuse-address? sol/socket so/exclusiveaddruse
  308. set-reuse-option get-reuse-option))
  309. (else
  310. (define-boolean-option so-reuse-address? sol/socket so/reuseaddr)))
  311. (define-boolean-option so-debug? sol/socket so/debug)
  312. (define-socket-option so-accept-connections? sol/socket so/acceptconn set-readonly-option get-boolean-option)
  313. (define-boolean-option so-keep-alive? sol/socket so/keepalive)
  314. (define-boolean-option so-dont-route? sol/socket so/dontroute)
  315. (define-boolean-option so-broadcast? sol/socket so/broadcast)
  316. ;(define-socket-option so-linger sol/socket so/linger set-linger-option get-linger-option)
  317. (define-boolean-option so-oob-inline? sol/socket so/oobinline)
  318. (define-integer-option so-send-buffer sol/socket so/sndbuf)
  319. (define-integer-option so-receive-buffer sol/socket so/rcvbuf)
  320. (define-integer-option so-send-low-water sol/socket so/sndlowat)
  321. (define-integer-option so-receive-low-water sol/socket so/rcvlowat)
  322. ;(define-socket-option so-receive-timeout sol/socket so/rcvtimeo set-timeval-option get-timeval-option)
  323. ;(define-socket-option so-send-timeout sol/socket so/sndtimeo set-timeval-option get-timeval-option)
  324. (define-socket-option so-error sol/socket so/error set-readonly-option get-integer-option)
  325. (define-socket-option so-type sol/socket so/type set-readonly-option get-integer-option)
  326. ;;; TCP options
  327. (define-boolean-option tcp-no-delay? ipproto/tcp tcp/nodelay)
  328. (define-optional-integer-option tcp-max-segment-size ipproto/tcp tcp/maxseg)
  329. (define-optional-boolean-option tcp-no-push? ipproto/tcp tcp/nopush)
  330. (define-optional-boolean-option tcp-no-options? ipproto/tcp tcp/noopt)
  331. (define-optional-integer-option tcp-keep-alive ipproto/tcp tcp/keepalive)
  332. ;;; IP options
  333. ;; Most of the IP option interface is currently unimplemented as it
  334. ;; seems to differ widely between systems.
  335. ;; TODO Multicast should be implemented if present.
  336. (define-boolean-option ip-header-included? ipproto/ip ip/hdrincl)
  337. (define-integer-option ip-type-of-service ipproto/ip ip/tos)
  338. (define-integer-option ip-time-to-live ipproto/ip ip/ttl)
  339. (define-optional-socket-option ipv6-v6-only? ipproto/ipv6 ipv6/v6only
  340. set-boolean-option get-boolean-option)
  341. ;;(define-boolean-option ipv6-v6-only? ipproto/ipv6 ipv6/v6only)