diff options
-rw-r--r-- | lisplib.c | 3 | ||||
-rw-r--r-- | share/txr/stdlib/socket.tl | 114 | ||||
-rw-r--r-- | tests/014/in6addr-str.expected | 0 | ||||
-rw-r--r-- | tests/014/in6addr-str.tl | 120 | ||||
-rw-r--r-- | tests/014/inaddr-str.expected | 0 | ||||
-rw-r--r-- | tests/014/inaddr-str.tl | 78 | ||||
-rw-r--r-- | txr.1 | 122 |
7 files changed, 435 insertions, 2 deletions
@@ -343,6 +343,7 @@ static val sock_set_entries(val dlt, val fun) lit("ai-numericserv"), lit("str-inaddr"), lit("str-in6addr"), lit("str-inaddr-net"), lit("str-in6addr-net"), + lit("inaddr-str"), lit("in6addr-str"), lit("shut-rd"), lit("shut-wr"), lit("shut-rdwr"), lit("open-socket"), lit("open-socket-pair"), lit("sock-bind"), lit("sock-connect"), lit("sock-listen"), @@ -352,7 +353,7 @@ static val sock_set_entries(val dlt, val fun) }; val name_noload[] = { lit("family"), lit("addr"), lit("port"), lit("flow-info"), - lit("scope-id"), lit("path"), lit("flags"), lit("socktype"), + lit("scope-id"), lit("prefix"), lit("path"), lit("flags"), lit("socktype"), lit("protocol"), lit("canonname"), nil }; set_dlt_entries(dlt, name, fun); diff --git a/share/txr/stdlib/socket.tl b/share/txr/stdlib/socket.tl index 3236460c..54df9758 100644 --- a/share/txr/stdlib/socket.tl +++ b/share/txr/stdlib/socket.tl @@ -29,10 +29,12 @@ (defstruct sockaddr-in sockaddr (addr 0) (port 0) + (prefix 32) (:static family af-inet)) (defstruct sockaddr-in6 sockaddr (addr 0) (port 0) (flow-info 0) (scope-id 0) + (prefix 128) (:static family af-inet6)) (defstruct sockaddr-un sockaddr @@ -152,6 +154,118 @@ (prefix (if (search cand-prefix '(0 0)) pieces cand-prefix))) `@(sys:in6addr-condensed-text prefix)/@(or width w)`)))) +(defun inaddr-str (str) + (labels ((invalid () + (error "~s: invalid address ~s" 'inaddr-str str)) + (mkaddr (octets port) + (unless [all octets (op <= 0 @1 255)] + (invalid)) + (unless (<= 0 port 65535) + (invalid)) + (new sockaddr-in + addr (+ (ash (pop octets) 24) + (ash (pop octets) 16) + (ash (pop octets) 8) + (car octets)) + port port)) + (mkaddr-pf (octets prefix port) + (unless [all octets (op <= 0 @1 255)] + (invalid)) + (unless (<= 0 prefix 32) + (invalid)) + (unless (<= 0 port 65535) + (invalid)) + (let* ((addr (+ (ash (or (pop octets) 0) 24) + (ash (or (pop octets) 0) 16) + (ash (or (pop octets) 0) 8) + (or (car octets) 0)))) + (new sockaddr-in + addr (logand addr (ash -1 (- 32 prefix))) + port port + prefix prefix)))) + (cond + ((r^$ #/\d+\.\d+\.\d+\.\d+:\d+/ str) + (tree-bind (addr port) (split* str (rpos #\: str)) + (mkaddr [mapcar toint (spl #\. addr)] (toint port)))) + ((r^$ #/\d+\.\d+\.\d+\.\d+(:\d+)?/ str) + (mkaddr [mapcar toint (spl #\. str)] 0)) + ((r^$ #/\d+(\.\d+(\.\d+(\.\d+)?)?)?\/\d+/ str) + (tree-bind (addr prefix) (spl #\/ str) + (mkaddr-pf [mapcar toint (spl #\. addr)] (toint prefix) 0))) + ((r^$ #/\d+(\.\d+(\.\d+(\.\d+)?)?)?\/\d+:\d+/ str) + (tree-bind (addr prefix port) (split-str-set str ":/") + (mkaddr-pf [mapcar toint (spl #\. addr)] (toint prefix) (toint port)))) + (t (invalid))))) + +(defun in6addr-str (str) + (labels ((invalid () + (error "~s: invalid address ~s" 'in6addr-str str)) + (mkaddr-full (pieces) + (unless [all pieces (op <= 0 @1 #xffff)] + (invalid)) + (unless (eql 8 (length pieces)) + (invalid)) + (new sockaddr-in6 + addr (reduce-left (op + @2 (ash @1 16)) pieces))) + (mkaddr-brev (pieces-x pieces-y) + (let ((len-x (len pieces-x)) + (len-y (len pieces-y))) + (unless (<= (+ len-x len-y) 7) + (invalid)) + (let* ((val-x (reduce-left (op + @2 (ash @1 16)) pieces-x 0)) + (val-y (reduce-left (op + @2 (ash @1 16)) pieces-y 0)) + (addr (cond + ((null pieces-x) val-y) + ((null pieces-y) (ash val-x (* 16 (- 8 len-x)))) + (t (+ val-y + (ash val-x (* 16 (- 8 len-x)))))))) + (new sockaddr-in6 + addr addr)))) + (str-to-pieces (str) + (unless (empty str) + [mapcar (lop toint 16) (spl #\: str)])) + (octets-to-pieces (octets) + (unless [all octets (op <= 0 @1 255)] + (invalid)) + (list (+ (ash (pop octets) 8) + (pop octets)) + (+ (ash (pop octets) 8) + (pop octets))))) + (cond + ((r^$ #/\[.*\]:\d+/ str) + (tree-bind (addr-str port-str) (split* str (rpos #\: str)) + (let ((addr (in6addr-str [addr-str 1..-1])) + (port (toint port-str))) + (unless (<= 0 port 65535) + (invalid)) + (set addr.port port) + addr))) + ((r^$ #/[^\/]+\/\d+/ str) + (tree-bind (addr-str prefix-str) (split* str (rpos #\/ str)) + (let ((addr (in6addr-str addr-str)) + (prefix (toint prefix-str))) + (unless (<= 0 prefix 128) + (invalid)) + (upd addr.addr (logand (ash -1 (- 128 prefix)))) + (set addr.prefix prefix) + addr))) + ((r^$ #/[\da-fA-F]*(:[\da-fA-F]*)*/ str) + (upd str (regsub #/::/ "@")) + (let* ((str-splat (regsub #/::/ "@" str)) + (maj-pieces (spl #\@ str-splat))) + (caseql (len maj-pieces) + (1 (mkaddr-full (str-to-pieces (car maj-pieces)))) + (2 (mkaddr-brev (str-to-pieces (car maj-pieces)) + (str-to-pieces (cadr maj-pieces)))) + (t (invalid))))) + ((r^$ #/::0*[fF][fF][fF][fF]:\d+\.\d+\.\d+\.\d+/ str) + (let* ((bigsplit (split* str (rpos #\: str))) + (4part (cadr bigsplit)) + (octets [mapcar toint (spl #\. 4part)]) + (pieces (cons #xffff (octets-to-pieces octets)))) + (mkaddr-brev nil pieces))) + (t (invalid))))) + (defplace (sock-peer sock) body (getter setter ^(macrolet ((,getter () ^(sock-peer ',',sock)) diff --git a/tests/014/in6addr-str.expected b/tests/014/in6addr-str.expected new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/014/in6addr-str.expected diff --git a/tests/014/in6addr-str.tl b/tests/014/in6addr-str.tl new file mode 100644 index 00000000..a85da416 --- /dev/null +++ b/tests/014/in6addr-str.tl @@ -0,0 +1,120 @@ +(load "../common.tl") + +(test (in6addr-str "junk") :error) + +(test (in6addr-str "0:0:0:0:0:0:0:0:0") :error) +(test (in6addr-str "0:0:0:0:0:0") :error) +(test (in6addr-str "0:0:0:0") :error) +(test (in6addr-str "") :error) + +(test (in6addr-str "0:0:0:0:0:0:0:x:0") :error) + +(test (in6addr-str ":0:0:0:0:0:0:0:0:0") :error) +(test (in6addr-str "0:0:0:0:0:0:0:0:0:") :error) + +(test (in6addr-str "0:0:0:0:0:0:0:0:FFFFF") :error) +(test (in6addr-str "0:0:0:0:0:0:0:FFFFF:0") :error) +(test (in6addr-str "0:0:0:0:0:0:FFFFF:0:0") :error) +(test (in6addr-str "0:0:0:0:0:FFFFF:0:0:0") :error) +(test (in6addr-str "0:0:0:0:FFFFF:0:0:0:0") :error) +(test (in6addr-str "0:0:0:FFFFF:0:0:0:0:0") :error) +(test (in6addr-str "0:0:FFFFF:0:0:0:0:0:0") :error) +(test (in6addr-str "0:FFFFF:0:0:0:0:0:0:0") :error) +(test (in6addr-str "FFFFF:0:0:0:0:0:0:0:0") :error) + +(test (in6addr-str "0:0:0:0:0:0:0:0/") :error) +(test (in6addr-str "0:0:0:0:0:0:0:0/129") :error) +(test (in6addr-str "[0:0:0:0:0:0:0:0]:") :error) +(test (in6addr-str "[0:0:0:0:0:0:0:0]:65536") :error) + +(test (in6addr-str "0:0:0:0:0:0:0:0") + #S(sockaddr-in6 addr 0 + port 0 flow-info 0 scope-id 0 prefix 128)) +(test (in6addr-str "1111:2222:3333:4444:5555:6666:7777:8888") + #S(sockaddr-in6 addr 22685837286468424649968941046919825544 + port 0 flow-info 0 scope-id 0 prefix 128)) +(test (in6addr-str "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF") + #S(sockaddr-in6 addr 340282366920938463463374607431768211455 + port 0 flow-info 0 scope-id 0 prefix 128)) + +(test (in6addr-str "[0:0:0:0:0:0:0:0]:42") + #S(sockaddr-in6 addr 0 + port 42 flow-info 0 scope-id 0 prefix 128)) +(test (in6addr-str "[1111:2222:3333:4444:5555:6666:7777:8888]:42") + #S(sockaddr-in6 addr 22685837286468424649968941046919825544 + port 42 flow-info 0 scope-id 0 prefix 128)) +(test (in6addr-str "[FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:42") + #S(sockaddr-in6 addr 340282366920938463463374607431768211455 + port 42 flow-info 0 scope-id 0 prefix 128)) + +(test (in6addr-str "0:0:0:0:0:0:0:0/16") + #S(sockaddr-in6 addr 0 + port 0 flow-info 0 scope-id 0 prefix 16)) +(test (in6addr-str "1111:2222:3333:4444:5555:6666:7777:8888/16") + #S(sockaddr-in6 addr 22685144974938661909049738462362599424 + port 0 flow-info 0 scope-id 0 prefix 16)) +(test (in6addr-str "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF/16") + #S(sockaddr-in6 addr 340277174624079928635746076935438991360 + port 0 flow-info 0 scope-id 0 prefix 16)) +(test (in6addr-str "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/16") + #S(sockaddr-in6 addr 340277174624079928635746076935438991360 + port 0 flow-info 0 scope-id 0 prefix 16)) + +(test (in6addr-str "[0:0:0:0:0:0:0:0/16]:42") + #S(sockaddr-in6 addr 0 + port 42 flow-info 0 scope-id 0 prefix 16)) +(test (in6addr-str "[1111:2222:3333:4444:5555:6666:7777:8888/16]:42") + #S(sockaddr-in6 addr 22685144974938661909049738462362599424 + port 42 flow-info 0 scope-id 0 prefix 16)) +(test (in6addr-str "[FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF/16]:42") + #S(sockaddr-in6 addr 340277174624079928635746076935438991360 + port 42 flow-info 0 scope-id 0 prefix 16)) + +(test (in6addr-str "1:2:3:4:5:6::7:8:9") :error) +(test (in6addr-str "1:2:3:4:5:6::7:8:9") :error) +(test (in6addr-str "1:2:3:4:5:6::7:8") :error) + +(test (in6addr-str "::1") + #S(sockaddr-in6 addr 1 + port 0 flow-info 0 scope-id 0 prefix 128)) + +(test (in6addr-str "1::") + #S(sockaddr-in6 addr 5192296858534827628530496329220096 + port 0 flow-info 0 scope-id 0 prefix 128)) + +(test (in6addr-str "1::1") + #S(sockaddr-in6 addr 5192296858534827628530496329220097 + port 0 flow-info 0 scope-id 0 prefix 128)) + +(test (in6addr-str "1:2::3:4") + #S(sockaddr-in6 addr 5192455314859856157205683417317380 + port 0 flow-info 0 scope-id 0 prefix 128)) + +(test (in6addr-str "::ffff:1.2.3.4.5") :error) +(test (in6addr-str "::ffff:1.2.3.4:5") :error) +(test (in6addr-str "::ffff:1.2.3") :error) +(test (in6addr-str "::ffff:1.2.3:4") :error) + +(test (in6addr-str "::ffff:1.2.3.4") + #S(sockaddr-in6 addr 281470698652420 + port 0 flow-info 0 scope-id 0 prefix 128)) + +(test (in6addr-str "::FFFF:1.2.3.4") + #S(sockaddr-in6 addr 281470698652420 + port 0 flow-info 0 scope-id 0 prefix 128)) + +(test (in6addr-str "::FfFf:1.2.3.4") + #S(sockaddr-in6 addr 281470698652420 + port 0 flow-info 0 scope-id 0 prefix 128)) + +(test (in6addr-str "::FFFF:1.2.3.4/96") + #S(sockaddr-in6 addr 281470681743360 + port 0 flow-info 0 scope-id 0 prefix 96)) + +(test (in6addr-str "[::ffff:1.2.3.4]:42") + #S(sockaddr-in6 addr 281470698652420 + port 42 flow-info 0 scope-id 0 prefix 128)) + +(test (in6addr-str "[::FFFF:1.2.3.4/96]:42") + #S(sockaddr-in6 addr 281470681743360 + port 42 flow-info 0 scope-id 0 prefix 96)) diff --git a/tests/014/inaddr-str.expected b/tests/014/inaddr-str.expected new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/014/inaddr-str.expected diff --git a/tests/014/inaddr-str.tl b/tests/014/inaddr-str.tl new file mode 100644 index 00000000..67283188 --- /dev/null +++ b/tests/014/inaddr-str.tl @@ -0,0 +1,78 @@ +(load "../common.tl") + +(test (inaddr-str "junk") :error) +(test (inaddr-str "0.0.0.0.0") :error) +(test (inaddr-str "0.0.0") :error) +(test (inaddr-str "0") :error) +(test (inaddr-str "") :error) +(test (inaddr-str "0.0.0.nnn") :error) +(test (inaddr-str "0.0.0.256") :error) +(test (inaddr-str "0.0.256.0") :error) +(test (inaddr-str "0.256.0.0") :error) +(test (inaddr-str "256.0.0.0") :error) +(test (inaddr-str "0.0.0.0:65537") :error) +(test (inaddr-str "0.0.0.0/33") :error) +(test (inaddr-str "0.0.0.0/32:65537") :error) +(test (inaddr-str "0.0.0.0/33:0") :error) +(test (inaddr-str "0.0.0.0:0/0") :error) +(test (inaddr-str "0.0.0.") :error) +(test (inaddr-str "0.0..0") :error) +(test (inaddr-str "0..0.0") :error) +(test (inaddr-str ".0.0.0") :error) +(test (inaddr-str "0.0.0.0:") :error) + +(test (inaddr-str "0.0.0.0") + #S(sockaddr-in addr 0 port 0 prefix 32)) +(test (inaddr-str "1.2.3.4") + #S(sockaddr-in addr 16909060 port 0 prefix 32)) +(test (inaddr-str "255.255.255.255") + #S(sockaddr-in addr 4294967295 port 0 prefix 32)) + +(test (inaddr-str "0.0.0.0:0") + #S(sockaddr-in addr 0 port 0 prefix 32)) +(test (inaddr-str "1.2.3.4:5") + #S(sockaddr-in addr 16909060 port 5 prefix 32)) +(test (inaddr-str "255.255.255.255:65535") + #S(sockaddr-in addr 4294967295 port 65535 prefix 32)) + +(test (inaddr-str "0.0.0.0/0") + #S(sockaddr-in addr 0 port 0 prefix 0)) +(test (inaddr-str "1.2.3.4/8") + #S(sockaddr-in addr 16777216 port 0 prefix 8)) +(test (inaddr-str "255.255.255.255/24") + #S(sockaddr-in addr 4294967040 port 0 prefix 24)) + +(test (inaddr-str "0.0.0/0") + #S(sockaddr-in addr 0 port 0 prefix 0)) +(test (inaddr-str "0.0/0") + #S(sockaddr-in addr 0 port 0 prefix 0)) +(test (inaddr-str "0/0") + #S(sockaddr-in addr 0 port 0 prefix 0)) + +(test (inaddr-str "1.2.3/8") + #S(sockaddr-in addr 16777216 port 0 prefix 8)) +(test (inaddr-str "1.2/8") + #S(sockaddr-in addr 16777216 port 0 prefix 8)) +(test (inaddr-str "1/8") + #S(sockaddr-in addr 16777216 port 0 prefix 8)) + +(test (inaddr-str "0.0.0.0/0:1234") + #S(sockaddr-in addr 0 port 1234 prefix 0)) +(test (inaddr-str "1.2.3.4/8:1234") + #S(sockaddr-in addr 16777216 port 1234 prefix 8)) +(test (inaddr-str "255.255.255.255/24:1234") + #S(sockaddr-in addr 4294967040 port 1234 prefix 24)) + +(test (inaddr-str "0.0.0/0:1234") + #S(sockaddr-in addr 0 port 1234 prefix 0)) +(test (inaddr-str "0.0/0:1234") + #S(sockaddr-in addr 0 port 1234 prefix 0)) +(test (inaddr-str "0/0:1234") + #S(sockaddr-in addr 0 port 1234 prefix 0)) + +(test (inaddr-str "1.2.3/8:1234") + #S(sockaddr-in addr 16777216 port 1234 prefix 8)) +(test (inaddr-str "1.2/8:1234") + #S(sockaddr-in addr 16777216 port 1234 prefix 8)) +(test (inaddr-str "1/8:1234") + #S(sockaddr-in addr 16777216 port 1234 prefix 8)) @@ -63412,7 +63412,7 @@ which is static, and initialized to .coNP Structure @ sockaddr-in .synb .mets (defstruct sockaddr-in sockaddr -.mets \ \ (addr 0) (port 0) +.mets \ \ (addr 0) (port 0) (prefix 32) .mets \ \ (:static family af-inet)) .syne .desc @@ -63446,6 +63446,12 @@ function is used with the aim of looking up the address of a host, without caring about the port number. The +.code prefix +field is set by the function +.codn inaddr-str , +when it recognizes and parses a prefix field in the textual representation. + +The .code family static slot holds the value .codn af-inet . @@ -63454,6 +63460,7 @@ static slot holds the value .synb .mets (defstruct sockaddr-in6 sockaddr .mets \ \ (addr 0) (port 0) (flow-info 0) (scope-id 0) +.mets \ \ (prefix 128) .mets \ \ (:static family af-inet6)) .syne .desc @@ -63488,6 +63495,12 @@ slots of the C language structure. Their meaning and use are beyond the scope of this document. The +.code prefix +field is set by the function +.codn in6addr-str , +when it recognizes and parses a prefix field in the textual representation. + +The .code family static slot holds the value .codn af-inet6 . @@ -64161,6 +64174,113 @@ excluding the contiguous all-zero bits in the least significant position: how many times the address can be shifted to the right before a 1 appears in the least significant bit. +.coNP Functions @ inaddr-str and @ in6addr-str +.synb +.mets (inaddr-str << string ) +.mets (in6addr-str << string ) +.syne +.desc +The +.code inaddr-str +and +.code in6addr-str +functions recover an IPv4 or IPv6 address from a textual representation. +If the parse is successful, the address is returned as, respectively, a +.code sockaddr-in +or +.code sockaddr-in6 +structure. + +If +.meta string +is a malformed address, due to any issue such as invalid syntax or +a numeric value being out of range, an exception is thrown. + +The +.code inaddr-str +function recognizes the dot notation consisting of four decimal numbers +separated by period characters. The numbers must be in the range 0 to 255. +Note: superfluous leading zeros are permitted, though this is a nonstandard +extension; not all implementations of this notations support this. + +A prefix may be specified in the notation as a slash followed by a decimal +number, in the range 0 to 32. In this case, the integer value of the +prefix appears as the +.code prefix +member of the returned +.code sockaddr-in +structure. Furthermore, the address is masked, so that any bits not +included in the prefix are zero. For instance, the address +.str 255.255.255.255/1 +is equivalent to +.strn 128.0.0.0 , +except that the +.code prefix +if the returned structure is 1 rather than 32. +When a prefix is not specified, the +.code prefix +member of the structure retains its default value of 32. +When the prefix is specified, the address part need not contain all four +octets; it may contain between one and four octets. Thus, +.str 192.168/16 +is a valid address, equivalent to +.strn 192.168.0.0/16 . + +A port number may be specified in the notation as a colon, followed by a +decimal number in the range 0 to 65535. The integer value of this port +number appears as the +.code port +member of the returned structure. An example of this notation is +.strn 127.0.0.1:23 . + +A prefix and port number may both be specified; in this case the prefix must +appear first, followed by the port number. For example, +.strn "127/8:23" . + +The +.code in6addr-str +function recognizes the IPv6 notation consisting of 16-bit hexadecimal pieces +separated by colons. If the operation is successful, it returns a +.code sockaddr-in6 +structure. Each piece must be a value in the range 0 to FFFF. +The hexadecimal digits may be any mixture of upper and lower case. Leading +zeros are permitted. +Up to eight such pieces must be specified. If fewer pieces are specified, +then the token +.code :: +(double colon) +must appear in the address exactly once. That token denotes the condensation of +a sufficient number of zero-valued pieces to make eight pieces. +The token must be in one of three positions: it may be the leftmost element of +the address, immediately followed by a hexadecimal piece; it may be the rightmost element +of the address, preceded by a hexadecimal piece; or else, it may be in the +middle of the address, flanked on both sides by hexadecimal pieces. + +The +.code in6addr-str +also recognizes the special notation for IPv6-mapped IPv4 addresses. This +notation consists of the address string +.str ::FFFF +which may appear in any upper/lower case mixture, possibly with leading +zeros, followed by an IPv4 address given in the four-octet dot notation. +For example, +.strn ::FFFF:127.0.0.1 . + +A prefix may be specified using a slash, followed by a decimal number in the +range 0 to 128. The handling of the prefix is similar to that of +.code inaddr-str +except that pieces of the address may not be omitted. Condensing the +pieces of the IPv6 address is always done by means of the +.code :: +token, whether or not a prefix is present. Furthermore, the octets specified in +the IPv6-mapped IPv4 notation must all be present, regardless of the prefix. + +A port number may be specified in the notation as follows: the entire address, +including any slash-separated prefix, must appear surrounded in square +brackets. The closing square bracket must be followed by a colon and one or +more digits denoting a decimal number in the range 0 to 65535. For instance +.strn "[1:2:3::4/64]:1234". + .SS* Unix Terminal Control \*(TX provides access to the terminal control "termios" interfaces defined by |