Import Debian changes 20180207-1
[hcoop/debian/mlton.git] / basis-library / net / socket.sml
CommitLineData
7f918cf1
CE
1(* Copyright (C) 2012,2013,2015,2017 Matthew Fluet.
2 * Copyright (C) 2002-2008 Henry Cejtin, Matthew Fluet, Suresh
3 * Jagannathan, and Stephen Weeks.
4 *
5 * MLton is released under a BSD-style license.
6 * See the file MLton-LICENSE for details.
7 *)
8
9structure Socket :> SOCKET_EXTRA =
10struct
11
12structure Prim = PrimitiveFFI.Socket
13structure Error = Posix.Error
14structure Syscall = Error.SysCall
15structure FileSys = Posix.FileSys
16
17structure Sock = Net.Sock
18type sock = Sock.t
19val fromRep = Sock.fromRep
20val toRep = Sock.toRep
21val sockToWord = C_Sock.castToSysWord o Sock.toRep
22val wordToSock = Sock.fromRep o C_Sock.castFromSysWord
23val sockToFD = PrePosix.FileDesc.fromRep o Sock.toRep
24val fdToSock = Sock.fromRep o PrePosix.FileDesc.toRep
25
26type pre_sock_addr = Word8.word array
27datatype sock_addr = SA of Word8.word vector
28fun unpackSockAddr (SA sa) = sa
29fun newSockAddr (): (pre_sock_addr * C_Socklen.t ref * (unit -> sock_addr)) =
30 let
31 val salen = C_Size.toInt Prim.sockAddrStorageLen
32 val sa = Array.array (salen, 0wx0: Word8.word)
33 val salenRef = ref (C_Socklen.fromInt salen)
34 fun finish () =
35 SA (ArraySlice.vector
36 (ArraySlice.slice (sa, 0, SOME (C_Socklen.toInt (!salenRef)))))
37 in
38 (sa, salenRef, finish)
39 end
40datatype dgram = DGRAM (* phantom *)
41datatype stream = MODE (* phantom *)
42datatype passive = PASSIVE (* phantom *)
43datatype active = ACTIVE (* phantom *)
44
45structure AddrFamily = Net.AddrFamily
46structure AF =
47 struct
48 type addr_family = AddrFamily.t
49 val names : (string * addr_family) list =
50 ("UNIX", AddrFamily.fromRep Prim.AF.UNIX) ::
51 ("INET", AddrFamily.fromRep Prim.AF.INET) ::
52 ("INET6", AddrFamily.fromRep Prim.AF.INET6) ::
53 ("UNSPEC", AddrFamily.fromRep Prim.AF.UNSPEC) ::
54 nil
55 fun list () = names
56 fun toString af' =
57 case List.find (fn (_, af) => af = af') names of
58 SOME (name, _) => name
59 | NONE => raise (Fail "Internal error: bogus addr_family")
60 fun fromString name' =
61 case List.find (fn (name, _) => name = name') names of
62 SOME (_, af) => SOME af
63 | NONE => NONE
64 end
65
66structure SockType = Net.SockType
67structure SOCK =
68 struct
69 type sock_type = SockType.t
70 val toRep = SockType.toRep
71 val fromRep = SockType.fromRep
72 val stream = SockType.fromRep Prim.SOCK.STREAM
73 val dgram = SockType.fromRep Prim.SOCK.DGRAM
74 val names : (string * sock_type) list =
75 ("STREAM", stream) ::
76 ("DGRAM", dgram) ::
77 nil
78 fun list () = names
79 fun toString st' =
80 case List.find (fn (_, st) => st = st') names of
81 SOME (name, _) => name
82 | NONE => raise (Fail "Internal error: bogus sock_type")
83 fun fromString name' =
84 case List.find (fn (name, _) => name = name') names of
85 SOME (_, st) => SOME st
86 | NONE => NONE
87 end
88structure SOCKExtra = SOCK
89
90structure CtlExtra =
91 struct
92 type level = C_Int.t
93 type optname = C_Int.t
94 type request = C_Int.t
95
96 fun getSockOptC_Int (level: level, optname: optname) s : C_Int.t =
97 let
98 val optval = ref (C_Int.fromInt 0)
99 val () =
100 Syscall.simple
101 (fn () => Prim.Ctl.getSockOptC_Int (Sock.toRep s, level, optname, optval))
102 in
103 ! optval
104 end
105 fun setSockOptC_Int (level: level, optname: optname) (s, optval: C_Int.t) : unit =
106 let
107 val () =
108 Syscall.simple
109 (fn () => Prim.Ctl.setSockOptC_Int (Sock.toRep s, level, optname, optval))
110 in
111 ()
112 end
113
114 fun getSockOptBool (level: level, optname: optname) s : bool =
115 if getSockOptC_Int (level, optname) s = 0 then false else true
116 fun setSockOptBool (level: level, optname: optname) (s, optval: bool) : unit =
117 setSockOptC_Int (level, optname) (s, if optval then C_Int.fromInt 1 else C_Int.fromInt 0)
118 fun gsSockOptBool (level: level, optname: optname) =
119 (getSockOptBool (level, optname), setSockOptBool (level, optname))
120
121 fun getSockOptInt (level: level, optname: optname) s : int =
122 C_Int.toInt (getSockOptC_Int (level, optname) s)
123 fun setSockOptInt (level: level, optname: optname) (s, optval: int) : unit =
124 setSockOptC_Int (level, optname) (s, C_Int.fromInt optval)
125 fun gsSockOptInt (level: level, optname: optname) =
126 (getSockOptInt (level, optname), setSockOptInt (level, optname))
127
128 fun getSockOptTimeOption (level: level, optname: optname) s : Time.time option =
129 let
130 val optval_l_onoff = ref (C_Int.fromInt 0)
131 val optval_l_linger = ref (C_Int.fromInt 0)
132 val () =
133 Syscall.simple
134 (fn () => Prim.Ctl.getSockOptC_Linger (Sock.toRep s, level, optname,
135 optval_l_onoff, optval_l_linger))
136
137 in
138 if ! optval_l_onoff = 0
139 then NONE
140 else SOME (Time.fromSeconds (C_Int.toLarge (! optval_l_linger)))
141 end
142 fun setSockOptTimeOption (level: level, optname: optname) (s, optval: Time.time option) : unit =
143 let
144 val (optval_l_onoff, optval_l_linger) =
145 case optval of
146 NONE => (C_Int.fromInt 0, C_Int.fromInt 0)
147 | SOME t => (C_Int.fromInt 1, C_Int.fromLarge (Time.toSeconds t))
148 val () =
149 Syscall.simple
150 (fn () => Prim.Ctl.setSockOptC_Linger (Sock.toRep s, level, optname,
151 optval_l_onoff, optval_l_linger))
152
153 in
154 ()
155 end
156
157 val (getDEBUG, setDEBUG) = gsSockOptBool (Prim.Ctl.SOL_SOCKET, Prim.Ctl.SO_DEBUG)
158 val (getREUSEADDR, setREUSEADDR) = gsSockOptBool (Prim.Ctl.SOL_SOCKET, Prim.Ctl.SO_REUSEADDR)
159 val (getKEEPALIVE, setKEEPALIVE) = gsSockOptBool (Prim.Ctl.SOL_SOCKET, Prim.Ctl.SO_KEEPALIVE)
160 val (getDONTROUTE, setDONTROUTE) = gsSockOptBool (Prim.Ctl.SOL_SOCKET, Prim.Ctl.SO_DONTROUTE)
161 val getLINGER = getSockOptTimeOption (Prim.Ctl.SOL_SOCKET, Prim.Ctl.SO_LINGER)
162 val setLINGER = setSockOptTimeOption (Prim.Ctl.SOL_SOCKET, Prim.Ctl.SO_LINGER)
163 val (getBROADCAST, setBROADCAST) = gsSockOptBool (Prim.Ctl.SOL_SOCKET, Prim.Ctl.SO_BROADCAST)
164 val (getOOBINLINE, setOOBINLINE) = gsSockOptBool (Prim.Ctl.SOL_SOCKET, Prim.Ctl.SO_OOBINLINE)
165 val (getSNDBUF, setSNDBUF) = gsSockOptInt (Prim.Ctl.SOL_SOCKET, Prim.Ctl.SO_SNDBUF)
166 val (getRCVBUF, setRCVBUF) = gsSockOptInt (Prim.Ctl.SOL_SOCKET, Prim.Ctl.SO_RCVBUF)
167 fun getTYPE s = SOCK.fromRep (getSockOptC_Int (Prim.Ctl.SOL_SOCKET, Prim.Ctl.SO_TYPE) s)
168 fun getERROR s =
169 let
170 val se = getSockOptC_Int (Prim.Ctl.SOL_SOCKET, Prim.Ctl.SO_ERROR) s
171 val se = PrePosix.SysError.fromRep se
172 in
173 if PosixError.cleared = se
174 then NONE
175 else SOME (Error.errorMsg se, SOME se)
176 end handle Error.SysErr z => SOME z
177
178 local
179 fun getName (s, f: C_Sock.t * pre_sock_addr * C_Socklen.t ref -> C_Int.int C_Errno.t) =
180 let
181 val (sa, salen, finish) = newSockAddr ()
182 val () = Syscall.simple (fn () => f (Sock.toRep s, sa, salen))
183 in
184 finish ()
185 end
186 in
187 fun getPeerName s = getName (s, Prim.Ctl.getPeerName)
188 fun getSockName s = getName (s, Prim.Ctl.getSockName)
189 end
190 fun getNREAD s =
191 let
192 val argp = ref (C_Int.fromInt ~1)
193 val () = Syscall.simple (fn () => Prim.Ctl.getNREAD (Sock.toRep s, argp))
194 in
195 C_Int.toInt (!argp)
196 end
197 fun getATMARK s =
198 let
199 val argp = ref (C_Int.fromInt ~1)
200 val () = Syscall.simple (fn () => Prim.Ctl.getATMARK (Sock.toRep s, argp))
201 in
202 if C_Int.toInt (!argp) = 0 then false else true
203 end
204 end
205
206structure Ctl =
207 struct
208 open CtlExtra
209
210 val getERROR = isSome o CtlExtra.getERROR
211 end
212
213fun sameAddr (SA sa1, SA sa2) = sa1 = sa2
214
215fun familyOfAddr (SA sa) = AddrFamily.fromRep (Prim.familyOfAddr sa)
216
217fun bind (s, SA sa) =
218 Syscall.simple (fn () => Prim.bind (Sock.toRep s, sa, C_Socklen.fromInt (Vector.length sa)))
219
220fun listen (s, n) =
221 Syscall.simple (fn () => Prim.listen (Sock.toRep s, C_Int.fromInt n))
222
223fun nonBlock' ({restart: bool},
224 errVal : ''a, f : unit -> ''a C_Errno.t, post : ''a -> 'b, again, no : 'b) =
225 Syscall.syscallErr
226 ({clear = false, restart = restart, errVal = errVal}, fn () =>
227 {return = f (),
228 post = post,
229 handlers = [(again, fn () => no)]})
230
231fun nonBlock (errVal, f, post, no) =
232 nonBlock' ({restart = true}, errVal, f, post, Error.again, no)
233
234local
235 structure PIO = PrimitiveFFI.Posix.IO
236 structure OS = Primitive.MLton.Platform.OS
237 structure MinGW = PrimitiveFFI.MinGW
238
239 fun withNonBlockNormal (s, f: unit -> 'a) =
240 let
241 val fd = Sock.toRep s
242 val flags =
243 Syscall.simpleResultRestart (fn () => PIO.fcntl2 (fd, PIO.F_GETFL))
244 val () =
245 Syscall.simpleRestart
246 (fn () =>
247 PIO.fcntl3 (fd, PIO.F_SETFL,
248 C_Int.orb (flags, PrimitiveFFI.Posix.FileSys.O.NONBLOCK)))
249 in
250 DynamicWind.wind
251 (f, fn () =>
252 Syscall.simpleRestart (fn () => PIO.fcntl3 (fd, PIO.F_SETFL, flags)))
253 end
254
255 fun withNonBlockMinGW (s, f: unit -> 'a) =
256 let
257 val fd = Sock.toRep s
258 val () = MinGW.setNonBlock fd
259 in
260 DynamicWind.wind
261 (f, fn () => MinGW.clearNonBlock fd)
262 end
263in
264 val withNonBlock = fn x =>
265 case OS.host of
266 OS.MinGW => withNonBlockMinGW x
267 | _ => withNonBlockNormal x
268end
269
270fun connect (s, SA sa) =
271 Syscall.simple (fn () => Prim.connect (Sock.toRep s, sa, C_Socklen.fromInt (Vector.length sa)))
272
273fun connectNB (s, SA sa) =
274 nonBlock'
275 ({restart = false}, C_Int.fromInt ~1, fn () =>
276 withNonBlock (s, fn () => Prim.connect (Sock.toRep s, sa, C_Socklen.fromInt (Vector.length sa))),
277 fn _ => true,
278 Error.inprogress, false)
279
280fun accept s =
281 let
282 val (sa, salen, finish) = newSockAddr ()
283 val s = Syscall.simpleResultRestart (fn () => Prim.accept (Sock.toRep s, sa, salen))
284 in
285 (Sock.fromRep s, finish ())
286 end
287
288fun acceptNB s =
289 let
290 val (sa, salen, finish) = newSockAddr ()
291 in
292 nonBlock
293 (C_Int.fromInt ~1,
294 fn () => withNonBlock (s, fn () => Prim.accept (Sock.toRep s, sa, salen)),
295 fn s => SOME (Sock.fromRep s, finish ()),
296 NONE)
297 end
298
299fun close s = Syscall.simple (fn () => Prim.close (Sock.toRep s))
300
301datatype shutdown_mode = NO_RECVS | NO_SENDS | NO_RECVS_OR_SENDS
302
303fun shutdownModeToHow m =
304 case m of
305 NO_RECVS => Prim.SHUT_RD
306 | NO_SENDS => Prim.SHUT_WR
307 | NO_RECVS_OR_SENDS => Prim.SHUT_RDWR
308
309fun shutdown (s, m) =
310 let val m = shutdownModeToHow m
311 in Syscall.simple (fn () => Prim.shutdown (Sock.toRep s, m))
312 end
313
314type sock_desc = FileSys.file_desc
315
316fun sockDesc sock = sockToFD sock
317
318fun sameDesc (desc1, desc2) = desc1 = desc2
319
320fun select {rds: sock_desc list,
321 wrs: sock_desc list,
322 exs: sock_desc list,
323 timeout: Time.time option} =
324 let
325 local
326 fun mk l =
327 let
328 val vec = Vector.fromList l
329 val arr = Array.array (Vector.length vec, 0: C_Int.t)
330 in
331 (PrePosix.FileDesc.vectorToRep vec, arr)
332 end
333 in
334 val (read_vec, read_arr) = mk rds
335 val (write_vec, write_arr) = mk wrs
336 val (except_vec, except_arr) = mk exs
337 end
338 val setTimeout =
339 case timeout of
340 NONE => Prim.setTimeoutNull
341 | SOME t =>
342 if Time.< (t, Time.zeroTime)
343 then Error.raiseSys Error.inval
344 else let
345 val q = LargeInt.quot (Time.toMicroseconds t, 1000000)
346 val q = C_Time.fromLargeInt q
347 val r = LargeInt.rem (Time.toMicroseconds t, 1000000)
348 val r = C_SUSeconds.fromLargeInt r
349 in
350 fn () => Prim.setTimeout (q, r)
351 end handle Overflow => Error.raiseSys Error.inval
352 val res =
353 Syscall.simpleResult
354 (fn () =>
355 (setTimeout ()
356 ; Prim.select (read_vec, write_vec, except_vec,
357 read_arr, write_arr, except_arr)))
358 val (rds, wrs, exs) =
359 if res = 0
360 then ([],[],[])
361 else
362 let
363 fun mk (l, arr) =
364 (List.rev o #1)
365 (List.foldl (fn (sd, (l, i)) =>
366 (if Array.sub (arr, i) <> (0: C_Int.t) then sd::l else l, i + 1))
367 ([], 0)
368 l)
369 in
370 (mk (rds, read_arr),
371 mk (wrs, write_arr),
372 mk (exs, except_arr))
373 end
374 in
375 {rds = rds,
376 wrs = wrs,
377 exs = exs}
378 end
379
380val ioDesc = FileSys.fdToIOD o sockDesc
381
382type out_flags = {don't_route: bool, oob: bool}
383
384val no_out_flags = {don't_route = false, oob = false}
385
386fun mk_out_flags {don't_route, oob} =
387 C_Int.orb (if don't_route then Prim.MSG_DONTROUTE else 0x0,
388 C_Int.orb (if oob then Prim.MSG_OOB else 0x0,
389 0x0))
390
391local
392 fun make (toPoly, base, primSend, primSendTo) =
393 let
394 val base = fn sl => let val (buf, i, sz) = base sl
395 in (toPoly buf, i, sz)
396 end
397 fun send' (s, sl, out_flags) =
398 let
399 val (buf, i, sz) = base sl
400 in
401 (C_SSize.toInt o Syscall.simpleResultRestart')
402 ({errVal = C_SSize.castFromFixedInt ~1}, fn () =>
403 primSend (Sock.toRep s, buf, C_Int.fromInt i, C_Size.fromInt sz,
404 mk_out_flags out_flags))
405 end
406 fun send (sock, buf) = send' (sock, buf, no_out_flags)
407 fun sendNB' (s, sl, out_flags) =
408 let
409 val (buf, i, sz) = base sl
410 in
411 nonBlock
412 (C_SSize.castFromFixedInt ~1,
413 fn () =>
414 primSend (Sock.toRep s, buf, C_Int.fromInt i, C_Size.fromInt sz,
415 C_Int.orb (Prim.MSG_DONTWAIT, mk_out_flags out_flags)),
416 SOME o C_SSize.toInt,
417 NONE)
418 end
419 fun sendNB (sock, sl) = sendNB' (sock, sl, no_out_flags)
420 fun sendTo' (s, SA sa, sl, out_flags) =
421 let
422 val (buf, i, sz) = base sl
423 in
424 Syscall.simpleRestart'
425 ({errVal = C_SSize.castFromFixedInt ~1}, fn () =>
426 primSendTo (Sock.toRep s, buf, C_Int.fromInt i, C_Size.fromInt sz,
427 mk_out_flags out_flags,
428 sa, C_Socklen.fromInt (Vector.length sa)))
429 end
430 fun sendTo (sock, sock_addr, sl) =
431 sendTo' (sock, sock_addr, sl, no_out_flags)
432 fun sendToNB' (s, SA sa, sl, out_flags) =
433 let
434 val (buf, i, sz) = base sl
435 in
436 nonBlock
437 (C_SSize.castFromFixedInt ~1,
438 fn () =>
439 primSendTo (Sock.toRep s, buf, C_Int.fromInt i, C_Size.fromInt sz,
440 C_Int.orb (Prim.MSG_DONTWAIT, mk_out_flags out_flags),
441 sa, C_Socklen.fromInt (Vector.length sa)),
442 fn _ => true,
443 false)
444 end
445 fun sendToNB (sock, sa, sl) =
446 sendToNB' (sock, sa, sl, no_out_flags)
447 in
448 (send, send', sendNB, sendNB', sendTo, sendTo', sendToNB, sendToNB')
449 end
450in
451 val (sendArr, sendArr', sendArrNB, sendArrNB',
452 sendArrTo, sendArrTo', sendArrToNB, sendArrToNB') =
453 make (Word8Array.toPoly, Word8ArraySlice.base, Prim.sendArr, Prim.sendArrTo)
454 val (sendVec, sendVec', sendVecNB, sendVecNB',
455 sendVecTo, sendVecTo', sendVecToNB, sendVecToNB') =
456 make (Word8Vector.toPoly, Word8VectorSlice.base, Prim.sendVec, Prim.sendVecTo)
457end
458
459type in_flags = {peek: bool, oob: bool}
460
461val no_in_flags = {peek = false, oob = false}
462
463fun mk_in_flags {peek, oob} =
464 C_Int.orb (if peek then Prim.MSG_PEEK else 0x0,
465 C_Int.orb (if oob then Prim.MSG_OOB else 0x0,
466 0x0))
467
468fun recvArr' (s, sl, in_flags) =
469 let
470 val (buf, i, sz) = Word8ArraySlice.base sl
471 in
472 (C_SSize.toInt o Syscall.simpleResultRestart')
473 ({errVal = C_SSize.castFromFixedInt ~1}, fn () =>
474 Prim.recv (Sock.toRep s, Word8Array.toPoly buf, C_Int.fromInt i, C_Size.fromInt sz,
475 mk_in_flags in_flags))
476 end
477
478fun getVec (a, n, bytesRead) =
479 if n = bytesRead
480 then Word8Vector.unsafeFromArray a
481 else Word8ArraySlice.vector (Word8ArraySlice.slice (a, 0, SOME bytesRead))
482
483fun recvVec' (sock, n, in_flags) =
484 let
485 val a = Word8Array.alloc n
486 val bytesRead =
487 recvArr' (sock, Word8ArraySlice.full a, in_flags)
488 in
489 getVec (a, n, bytesRead)
490 end
491
492fun recvArr (sock, sl) = recvArr' (sock, sl, no_in_flags)
493
494fun recvVec (sock, n) = recvVec' (sock, n, no_in_flags)
495
496fun recvArrFrom' (s, sl, in_flags) =
497 let
498 val (buf, i, sz) = Word8ArraySlice.base sl
499 val (sa, salen, finish) = newSockAddr ()
500 val n =
501 (C_SSize.toInt o Syscall.simpleResultRestart')
502 ({errVal = C_SSize.castFromFixedInt ~1}, fn () =>
503 Prim.recvFrom (Sock.toRep s, Word8Array.toPoly buf, C_Int.fromInt i, C_Size.fromInt sz,
504 mk_in_flags in_flags,
505 sa, salen))
506 in
507 (n, finish ())
508 end
509
510fun recvVecFrom' (sock, n, in_flags) =
511 let
512 val a = Word8Array.alloc n
513 val (bytesRead, sock_addr) =
514 recvArrFrom' (sock, Word8ArraySlice.full a, in_flags)
515 in
516 (getVec (a, n, bytesRead), sock_addr)
517 end
518
519fun recvArrFrom (sock, sl) = recvArrFrom' (sock, sl, no_in_flags)
520
521fun recvVecFrom (sock, n) = recvVecFrom' (sock, n, no_in_flags)
522
523fun mk_in_flagsNB in_flags = C_Int.orb (mk_in_flags in_flags, Prim.MSG_DONTWAIT)
524
525fun recvArrNB' (s, sl, in_flags) =
526 let
527 val (buf, i, sz) = Word8ArraySlice.base sl
528 in
529 nonBlock
530 (C_SSize.castFromFixedInt ~1,
531 fn () => Prim.recv (Sock.toRep s, Word8Array.toPoly buf, C_Int.fromInt i, C_Size.fromInt sz,
532 mk_in_flagsNB in_flags),
533 SOME o C_SSize.toInt,
534 NONE)
535 end
536
537fun recvVecNB' (s, n, in_flags) =
538 let
539 val a = Word8Array.alloc n
540 in
541 nonBlock
542 (C_SSize.castFromFixedInt ~1,
543 fn () => Prim.recv (Sock.toRep s, Word8Array.toPoly a, 0, C_Size.fromInt n,
544 mk_in_flagsNB in_flags),
545 fn bytesRead => SOME (getVec (a, n, C_SSize.toInt bytesRead)),
546 NONE)
547 end
548
549fun recvArrNB (sock, sl) = recvArrNB' (sock, sl, no_in_flags)
550
551fun recvVecNB (sock, n) = recvVecNB' (sock, n, no_in_flags)
552
553fun recvArrFromNB' (s, sl, in_flags) =
554 let
555 val (buf, i, sz) = Word8ArraySlice.base sl
556 val (sa, salen, finish) = newSockAddr ()
557 in
558 nonBlock
559 (C_SSize.castFromFixedInt ~1,
560 fn () => Prim.recvFrom (Sock.toRep s, Word8Array.toPoly buf, C_Int.fromInt i, C_Size.fromInt sz,
561 mk_in_flagsNB in_flags, sa, salen),
562 fn n => SOME (C_SSize.toInt n, finish ()),
563 NONE)
564 end
565
566fun recvVecFromNB' (s, n, in_flags) =
567 let
568 val a = Word8Array.alloc n
569 val (sa, salen, finish) = newSockAddr ()
570 in
571 nonBlock
572 (C_SSize.castFromFixedInt ~1,
573 fn () => Prim.recvFrom (Sock.toRep s, Word8Array.toPoly a, 0, C_Size.fromInt n,
574 mk_in_flagsNB in_flags, sa, salen),
575 fn bytesRead => SOME (getVec (a, n, C_SSize.toInt bytesRead), finish ()),
576 NONE)
577 end
578
579fun recvArrFromNB (sock, sl) = recvArrFromNB' (sock, sl, no_in_flags)
580
581fun recvVecFromNB (sock, n) = recvVecFromNB' (sock, n, no_in_flags)
582
583(* Phantom type. *)
584type ('af, 'sock_type) sock = sock
585
586type 'af sock_addr = sock_addr
587
588type 'mode stream = stream
589
590end