! Copyright (C) 2007, 2011 Slava Pestov, Doug Coleman, ! Daniel Ehrenberg. ! See https://factorcode.org/license.txt for BSD license. USING: accessors alien.c-types alien.data alien.strings arrays byte-arrays classes classes.struct combinators combinators.short-circuit continuations destructors endian fry grouping init io.backend io.encodings.ascii io.encodings.binary io.pathnames io.ports io.streams.duplex kernel locals math math.parser memoize namespaces present sequences sequences.private splitting strings summary system vocabs vocabs.parser ip-parser ip-parser.private random ; IN: io.sockets << { { [ os windows? ] [ "windows.winsock" ] } { [ os unix? ] [ "unix.ffi" ] } } cond use-vocab >> GENERIC#: with-port 1 ( addrspec port -- addrspec ) ! Addressing TUPLE: local { path string read-only } ; : ( path -- addrspec ) absolute-path local boa ; M: local present path>> "Unix domain socket: " prepend ; M: local protocol drop 0 ; SLOT: port TUPLE: ipv4 { host maybe{ string } read-only } ; : ( host -- ipv4 ) dup check-ipv4 ipv4 boa ; M: ipv4 inet-ntop drop 4 memory>byte-array join-ipv4 ; M: ipv4 inet-pton drop [ ?parse-ipv4 ] [ invalid-ipv4 ] recover ; M: ipv4 address-size drop 4 ; M: ipv4 protocol-family drop PF_INET ; M: ipv4 sockaddr-size drop sockaddr-in heap-size ; M: ipv4 empty-sockaddr drop sockaddr-in new ; : make-sockaddr-part ( inet -- sockaddr ) sockaddr-in new AF_INET >>family swap port>> 0 or htons >>port ; inline M: ipv4 make-sockaddr [ make-sockaddr-part ] [ host>> "0.0.0.0" or ] [ inet-pton uint deref >>addr ] tri ; M: ipv4 make-sockaddr-outgoing [ make-sockaddr-part ] [ host>> dup { f "0.0.0.0" } member? [ drop "127.0.0.1" ] when ] [ inet-pton uint deref >>addr ] tri ; M: ipv4 parse-sockaddr [ addr>> uint ] dip inet-ntop ; TUPLE: inet4 < ipv4 { port maybe{ integer } read-only } ; : ( host port -- inet4 ) over check-ipv4 inet4 boa ; M: ipv4 with-port [ host>> ] dip ; M: inet4 parse-sockaddr [ call-next-method ] [ drop port>> ntohs ] 2bi with-port ; M: inet4 present [ host>> ] [ port>> number>string ] bi ":" glue ; M: inet4 protocol drop 0 ; TUPLE: ipv6 { host maybe{ string } read-only } { scope-id integer read-only } ; : ( host -- ipv6 ) dup check-ipv6 0 ipv6 boa ; M: ipv6 inet-ntop drop 16 memory>byte-array 2 [ be> >hex ] map ":" join ; be ] { } map-as B{ } concat-as ; PRIVATE> M: ipv6 inet-pton drop [ parse-ipv6 ipv6-bytes ] [ invalid-ipv6 ] recover ; M: ipv6 address-size drop 16 ; M: ipv6 protocol-family drop PF_INET6 ; M: ipv6 sockaddr-size drop sockaddr-in6 heap-size ; M: ipv6 empty-sockaddr drop sockaddr-in6 new ; : make-sockaddr-in6-part ( inet -- sockaddr ) sockaddr-in6 new AF_INET6 >>family swap port>> 0 or htons >>port ; inline M: ipv6 make-sockaddr [ make-sockaddr-in6-part ] [ [ host>> "::" or ] keep inet-pton >>addr ] [ scope-id>> >>scopeid ] tri ; M: ipv6 make-sockaddr-outgoing [ make-sockaddr-in6-part ] [ [ host>> dup { f "::" } member? [ drop "::1" ] when ] keep inet-pton >>addr ] [ scope-id>> >>scopeid ] tri ; M: ipv6 parse-sockaddr [ [ addr>> ] dip inet-ntop ] [ drop scopeid>> ] 2bi ipv6 boa ; M: ipv6 present [ host>> ] [ scope-id>> ] bi [ number>string "%" glue ] unless-zero ; TUPLE: inet6 < ipv6 { port maybe{ integer } read-only } ; : ( host port -- inet6 ) [ dup check-ipv6 0 ] dip inet6 boa ; M: ipv6 with-port [ [ host>> ] [ scope-id>> ] bi ] dip inet6 boa ; M: inet6 parse-sockaddr [ call-next-method ] [ drop port>> ntohs ] 2bi with-port ; M: inet6 present [ call-next-method "[" "]" surround ] [ port>> number>string ] bi ":" glue ; M: inet6 protocol drop 0 ; ERROR: addrinfo-error n string host ; ( handle -- input-port output-port ) [ [ |dispose ] [ |dispose ] bi ] with-destructors ; SYMBOL: bind-local-address GENERIC: establish-connection ( client-out remote -- ) GENERIC: remote>handle ( remote -- handle ) GENERIC: (client) ( remote -- client-in client-out local ) M: array (client) [ (client) 3array ] attempt-all first3 ; M: object (client) [ [ remote>handle ] keep [ [ [ |dispose ] bi@ dup ] dip establish-connection ] [ get-local-address ] 2bi ] with-destructors ; TUPLE: server-port < port addr encoding ; GENERIC: (server) ( addrspec -- handle ) GENERIC: (accept) ( server addrspec -- handle sockaddr ) TUPLE: datagram-port < port addr ; HOOK: (datagram) io-backend ( addr -- datagram ) TUPLE: raw-port < port addr ; HOOK: (raw) io-backend ( addr -- raw ) HOOK: (broadcast) io-backend ( datagram -- datagram ) HOOK: (receive-unsafe) io-backend ( n buf datagram -- count addrspec ) ERROR: invalid-port object ; : check-port ( bytes addrspec port -- bytes addrspec port ) 2dup addr>> [ class-of ] bi@ assert= pick class-of byte-array assert= ; : check-connectionless-port ( port -- port ) dup { [ datagram-port? ] [ raw-port? ] } 1|| [ invalid-port ] unless ; : check-send ( bytes addrspec port -- bytes addrspec port ) check-connectionless-port check-disposed check-port ; : check-receive ( port -- port ) check-connectionless-port check-disposed ; HOOK: (send) io-backend ( bytes addrspec datagram -- ) : addrinfo>addrspec ( addrinfo -- addrspec ) [ [ addr>> ] [ family>> ] bi sockaddr-of-family ] [ family>> addrspec-of-family ] bi parse-sockaddr ; : parse-addrinfo-list ( addrinfo -- seq ) [ next>> ] follow [ addrinfo>addrspec ] map sift ; HOOK: addrinfo-error-string io-backend ( n -- string ) : prepare-addrinfo ( -- addrinfo ) addrinfo new PF_UNSPEC >>family IPPROTO_TCP >>protocol ; PRIVATE> : ( remote encoding -- stream local ) [ (client) ] dip swap [ ] dip ; SYMBOL: local-address SYMBOL: remote-address : with-client ( remote encoding quot -- ) [ [ over remote-address set local-address set ] dip with-stream ] with-scope ; inline : ( addrspec encoding -- server ) [ [ (server) ] keep [ drop server-port ] [ get-local-address ] 2bi >>addr ] dip >>encoding ; : accept ( server -- client remote ) [ dup addr>> [ (accept) ] keep parse-sockaddr swap ] [ encoding>> ] bi swap ; : ( addrspec -- datagram ) [ [ (datagram) |dispose ] keep [ drop datagram-port ] [ get-local-address ] 2bi >>addr ] with-destructors ; : ( addrspec -- datagram ) [ [ (raw) |dispose ] keep [ drop raw-port ] [ get-local-address ] 2bi >>addr ] with-destructors ; : ( addrspec -- datagram ) (broadcast) ; : receive-unsafe ( n buf datagram -- count addrspec ) check-receive [ (receive-unsafe) ] [ addr>> ] bi parse-sockaddr ; inline CONSTANT: datagram-size 65536 :: receive ( datagram -- bytes addrspec ) datagram-size (byte-array) :> buf datagram-size buf datagram receive-unsafe :> ( count addrspec ) count buf resize addrspec ; inline :: receive-into ( buf datagram -- buf-slice addrspec ) buf length :> n n buf datagram receive-unsafe :> ( count addrspec ) buf count head-slice addrspec ; inline : send ( bytes addrspec datagram -- ) check-send (send) ; inline MEMO: ipv6-supported? ( -- ? ) [ "::1" 0 binary dispose t ] [ drop f ] recover ; STARTUP-HOOK: [ \ ipv6-supported? reset-memoized ] GENERIC: resolve-host ( addrspec -- seq ) HOOK: resolve-localhost os ( -- obj ) TUPLE: hostname { host maybe{ string } read-only } ; TUPLE: inet < hostname port ; M: inet present [ host>> ] [ port>> number>string ] bi ":" glue ; C: inet M:: string resolve-host ( host -- seq ) host f prepare-addrinfo f void* [ getaddrinfo [ dup addrinfo-error-string host addrinfo-error ] unless-zero ] keep void* deref addrinfo memory>struct [ parse-addrinfo-list ] keep freeaddrinfo ; M: string with-port ; M: hostname resolve-host host>> resolve-host ; M: hostname with-port [ host>> ] dip ; M: inet resolve-host [ call-next-method ] [ port>> ] bi '[ _ with-port ] map ; M: inet4 resolve-host 1array ; M: inet6 resolve-host 1array ; M: local resolve-host 1array ; M: f resolve-host drop resolve-localhost ; M: object resolve-localhost ipv6-supported? { T{ ipv4 f "0.0.0.0" } T{ ipv6 f "::" } } { T{ ipv4 f "0.0.0.0" } } ? ; HOOK: host-name os ( -- string ) M: inet (client) resolve-host (client) ; ERROR: invalid-inet-server addrspec ; M: invalid-inet-server summary drop "Cannot use with ; use or instead" ; M: inet (server) invalid-inet-server ; ERROR: invalid-local-address addrspec ; M: invalid-local-address summary drop "Cannot use with-local-address with ; use or instead" ; : with-local-address ( addr quot -- ) [ [ ] [ inet4? ] [ inet6? ] tri or [ bind-local-address ] [ invalid-local-address ] if ] dip with-variable ; inline : protocol-port ( protocol -- port ) [ f getservbyname [ port>> htons ] [ f ] if* ] [ f ] if* ; : port-protocol ( port -- protocol ) [ htons f getservbyport [ name>> ] [ f ] if* ] [ f ] if* ; : ( -- inet4 ) f 0 ; : ( -- inet6 ) f 0 ; GENERIC: ( inet -- inet4 ) M: inet4 drop ; M: inet6 drop f 0 ; : ( inet -- datagram ) ; : ( inet -- datagram ) ; : with-any-port-local-datagram ( quot -- ) [ dup ] dip with-disposal ; inline : with-any-port-local-broadcast ( quot -- ) [ dup ] dip with-disposal ; inline : send-once ( bytes addrspec -- ) [ send ] with-any-port-local-datagram ; : broadcast-once ( bytes addrspec -- ) [ send ] with-any-port-local-broadcast ; { { [ os unix? ] [ "io.sockets.unix" require ] } { [ os windows? ] [ "io.sockets.windows" require ] } } cond