Hello

I'm newbie Factor developer. First of all, I'd like to say "thank you
very much" for really great language. Having all the power and
simplicity of Lisp without closing ten or more damn brackets at the
end of each complex function - this is my wet dream from school days.

I have choosen Factor for my next web-related project. One part of
this project is dynamic DNS server. I think, general DNS library will
be usefull not only for me, so I'd like to share. For now, it is
pretty low-level, only DNS message parsing and generation, but with
two-way name packing. Library is not not yet tested in real-life
environment and may be buggy, but before investing more time into it,
i'd like to show my code to more experienced Factor developers. Am I
doing something wrong?

Thank you.

Maxim Savchenko.
! Copyright (C) 2008 Maxim Savchenko.
! See http://factorcode.org/license.txt for BSD license.

USING: bitfields kernel arrays combinators sequences io math namespaces
        io.binary strings assocs byte-arrays splitting tuples ;
IN: dns

<PRIVATE ! Helper stream for DNS message compression

TUPLE: dns-stream counter pointers ;

: <dns-stream> ( stream -- stream )
        0 H{ } clone dns-stream construct-boa tuck set-delegate ;

PRIVATE> ! Helper stream for DNS message compression

TUPLE: dns-message id flags qd an ns ar ;

C: <dns-message> dns-message

BITFIELD: dns-flags qr:1 opcode:4 aa:1 tc:1 rd:1 ra:1 z:3 rcode:4 ;

TUPLE: dns-question name type class ;

C: <dns-question> dns-question

TUPLE: dns-record name type class ttl resource ;

C: <dns-record> dns-record

TUPLE: rdata-soa mname rname serial refresh retry expire minimum ;

C: <rdata-soa> rdata-soa

TUPLE: rdata-wks address protocol bitmap ;

C: <rdata-wks> rdata-wks

TUPLE: rdata-minfo rmailbx emailbx ;

C: <rdata-minfo> rdata-minfo

TUPLE: rdata-mx preference exchange ;

C: <rdata-mx> rdata-mx

<PRIVATE ! Helpers for DNS parsing

: read2be ( -- int ) 2 read be> ; inline

: read4be ( -- int ) 4 read be> ; inline

M: dns-stream stream-read1
        dup delegate stream-read1 tuck
        [ dup dns-stream-counter 1+ swap set-dns-stream-counter ]
        [ drop ] if ;

M: dns-stream stream-read
        tuck delegate stream-read dup length
        pick dns-stream-counter + rot set-dns-stream-counter ;                  
     

TUPLE: rdata-istream limit ;

: <rdata-istream> ( int stream -- stream )
        swap rdata-istream construct-boa tuck set-delegate ;

M: rdata-istream stream-read1
        dup rdata-istream-limit dup 0 >
        [ 1- over set-rdata-istream-limit delegate stream-read1 ]
        [ 2drop f ] if ;

M: rdata-istream stream-read
        dup rdata-istream-limit dup roll min dup 0 >
        [ tuck - pick set-rdata-istream-limit swap delegate stream-read ]
        [ 3drop f ] if ;

: rdata-readall ( -- data )
        stdio get dup rdata-istream-limit swap stream-read ;

: (dns-name-read) ( list -- list )
        stdio get dns-stream-counter read1 {
                {
                        [ dup -6 shift BIN: 11 = ]
                        [
                                nip BIN: 111111 bitand 8 shift read1 +
                                stdio get dns-stream-pointers at
                                dup 1 roll set-nth
                        ]
                }
                {
                        [ dup dup -6 shift 0 = swap 0 > and ]
                        [
                                read >string f 2array swap dupd
                                stdio get dns-stream-pointers set-at
                                dup 1 roll set-nth (dns-name-read)
                        ]
                }
                { [ dup 0 = ] [ 2drop ] }
        } cond ;

: dns-name-read ( -- list )
        { f f } clone dup (dns-name-read) drop second ;

: dns-rdata-read ( type -- data )
        {
                { 1 [ 4 read >byte-array ] }                    ! A
                { 2 [ dns-name-read ] }                         ! NS
                { 5 [ dns-name-read ] }                         ! CNAME
                { 6 [
                        dns-name-read                           ! MNAME
                        dns-name-read                           ! RNAME
                        read4be                                 ! SERIAL
                        read4be                                 ! REFRESH
                        read4be                                 ! RETRY
                        read4be                                 ! EXPIRE
                        read4be                                 ! MINIMUM
                <rdata-soa> ] }                                 ! SOA
                { 11 [
                        4 read >byte-array                      ! ADDRESS
                        read1                                   ! PROTOCOL
                        rdata-readall >byte-array               ! <BIT MAP>
                <rdata-wks> ] }                                 ! WKS
                { 12 [ dns-name-read ] }                        ! PTR
                { 14 [
                        dns-name-read
                        dns-name-read
                <rdata-minfo> ] }                               ! MINFO
                { 15 [ read2be dns-name-read <rdata-mx> ] }     ! MX
                { 16 [ rdata-readall >string ] }                ! TXT
                [ drop f ]
        } case rdata-readall drop ; inline

: dns-question-read ( -- question )
        dns-name-read   ! QNAME
        read2be         ! QTYPE
        read2be         ! QCLASS
        <dns-question> ;

: dns-record-read ( -- record )
        dns-name-read   ! NAME
        read2be         ! TYPE
        read2be         ! CLASS
        read4be         ! TTL
        read2be         ! RDLENGTH
        >r pick r> stdio get
        <rdata-istream> [ dns-rdata-read ] with-stream* <dns-record> ;

: (domain-name>string) ( list -- )
        [ dup first % CHAR: . , second (domain-name>string) ] when* ;

PRIVATE> ! Helpers for DNS parsing

: dns-read ( stream -- message )
        <dns-stream> [
                read2be                                 ! ID
                read2be                                 ! flags
                read2be                                 ! QDCOUNT
                read2be                                 ! ANCOUNT
                read2be                                 ! NSCOUNT
                read2be                                 ! ARCOUNT
                roll [ drop dns-question-read ] map     ! questions
                roll [ drop dns-record-read   ] map     ! answers
                roll [ drop dns-record-read   ] map     ! autorities
                roll [ drop dns-record-read   ] map     ! additional
        ] with-stream* <dns-message> ;

: domain-name>string ( list -- string )
        [ (domain-name>string) ] "" make ;

<PRIVATE ! Helpers for DNS generation

: write2be ( int -- ) 2 >be write ; inline

: write4be ( int -- ) 4 >be write ; inline

: tuple-each ( tuple seq -- )
        >r tuple-slots r> [ call ] 2each ; inline

M: dns-stream stream-write1
        dup dns-stream-counter 1+ over set-dns-stream-counter
        delegate stream-write1 ;

M: dns-stream stream-write
        2dup dns-stream-counter swap length + over set-dns-stream-counter
        delegate stream-write ;

TUPLE: rdata-ostream buffer length ;

: <rdata-ostream> ( stream -- stream )
        V{ } clone 0 rdata-ostream construct-boa tuck set-delegate ;

M: rdata-ostream stream-write1
        tuck rdata-ostream-buffer push
        dup rdata-ostream-length 1+ swap set-rdata-ostream-length ;

M: rdata-ostream stream-write
        2dup rdata-ostream-buffer push
        tuck rdata-ostream-length swap length + swap set-rdata-ostream-length ;

M: rdata-ostream stream-close
        dup rdata-ostream-length 2 >be over delegate tuck stream-write
        >r rdata-ostream-buffer r>
        [ over integer? [ stream-write1 ] [ stream-write ] if ] curry each ;

: dns-name-write ( list -- )
        dup not [ drop 0 write1 ] [
                stdio get 2dup dns-stream-pointers at
                [ 2nip BIN: 11 14 shift bitor write2be ] [
                        dup dns-stream-counter pick rot
                        dns-stream-pointers set-at dup first dup
                        length write1 write second dns-name-write
                ] if*
        ] if ;

: dns-rdata-write ( rdata type -- )
        {
                { 1 [ write ] }                 ! A
                { 2 [ dns-name-write ] }        ! NS
                { 5 [ dns-name-write ] }        ! CNAME
                { 6 [ {
                        [ dns-name-write ]      ! MNAME
                        [ dns-name-write ]      ! RNAME
                        [ write4be ]            ! SERIAL
                        [ write4be ]            ! REFRESH
                        [ write4be ]            ! RETRY
                        [ write4be ]            ! EXPIRE
                        [ write4be ]            ! MINIMUM
                } tuple-each ] }                ! SOA
                { 11 [ {
                        [ write ]               ! ADDRESS
                        [ write1 ]              ! PROTOCOL
                        [ write ]               ! <BIT MAP>
                } tuple-each ] }                ! WKS
                { 12 [ dns-name-write ] }       ! PTR
                { 14 [ {
                        [ dns-name-write ]
                        [ dns-name-write ]
                } tuple-each ] }                ! MINFO
                { 15 [ {
                        [ write2be ]
                        [ dns-name-write ]
                } tuple-each ] }                ! MX
                { 16 [ write ] }                ! TXT
                [ 2drop ]
        } case ; inline

: dns-question-write ( question -- )
        {
                [ dns-name-write ]      ! QNAME
                [ write2be ]            ! QTYPE
                [ write2be ]            ! QCLASS
        } tuple-each ;

: dns-record-write ( record -- )
        dup {
                [ dns-name-write ]      ! NAME
                [ write2be ]            ! TYPE
                [ write2be ]            ! CLASS
                [ write4be ]            ! TTL
                [ drop ]
        } tuple-each
        dup dns-record-resource swap dns-record-type
        stdio get <rdata-ostream> [ dns-rdata-write ] with-stream ;

: (string>domain-name) ( list name -- list name )
        dup empty?
        [ "." split1 >r f 2array tuck 1 rot set-nth r> (string>domain-name) ]
        unless ;

PRIVATE> ! Helpers for DNS generation

: dns-write ( message stream -- )
        <dns-stream> [
                dup {
                        [ write2be ]                    ! ID
                        [ write2be ]                    ! flags
                        [ length write2be ]             ! QDCOUNT
                        [ length write2be ]             ! ANCOUNT
                        [ length write2be ]             ! NSCOUNT
                        [ length write2be ]             ! ARCOUNT
                } tuple-each {
                        [ drop ]
                        [ drop ]
                        [ [ dns-question-write ] each ] ! questions
                        [ [ dns-record-write ] each ]   ! answers
                        [ [ dns-record-write ] each ]   ! autorities
                        [ [ dns-record-write ] each ]   ! additional
                } tuple-each
        ] with-stream* ;

: string>domain-name ( string -- list )
        { f f } clone swap dupd (string>domain-name) 2drop second ;

-------------------------------------------------------------------------
Check out the new SourceForge.net Marketplace.
It's the best place to buy or sell services for
just about anything Open Source.
http://ad.doubleclick.net/clk;164216239;13503038;w?http://sf.net/marketplace
_______________________________________________
Factor-talk mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/factor-talk

Reply via email to