I am newbie to scapy and TCP , so it might be many mistakes in this post..
# apt-cache policy python-scapy | grep -i installed Installed: 2.2.0-1 |
client ( 192.168.10.205 ) -- web ( scapy 192.168.10.15 )
Before making simple web server with scapy , check HTTP pcap data with scapy.
It’s useful to learn how to build data stream with scapy.
The following is pcap date capture on web server.
( at this moment , I used Apache as an web server )
client ( 192.168.10.205 ) --- Apache ( 192.168.10.15 )
client # wget http://192.168.10.15 -O - |
# tshark -nr wget.pcap 1 0.000000 192.168.10.205 -> 192.168.10.15 TCP 74 39202 > 80 [SYN] Seq=0 Win=14600 Len=0 MSS=1460 SACK_PERM=1 TSval=85784530 TSecr=0 WS=32 2 0.001248 192.168.10.15 -> 192.168.10.205 TCP 74 80 > 39202 [SYN, ACK] Seq=0 Ack=1 Win=14480 Len=0 MSS=1460 SACK_PERM=1 TSval=155546502 TSecr=85784530 WS=128 3 0.001302 192.168.10.205 -> 192.168.10.15 TCP 66 39202 > 80 [ACK] Seq=1 Ack=1 Win=14624 Len=0 TSval=85784530 TSecr=155546502 4 0.001616 192.168.10.205 -> 192.168.10.15 HTTP 179 GET / HTTP/1.1 5 0.004509 192.168.10.15 -> 192.168.10.205 TCP 66 80 > 39202 [ACK] Seq=1 Ack=114 Win=14592 Len=0 TSval=155546503 TSecr=85784530 6 0.004529 192.168.10.15 -> 192.168.10.205 HTTP 384 HTTP/1.1 200 OK (text/html) 7 0.004540 192.168.10.205 -> 192.168.10.15 TCP 66 39202 > 80 [ACK] Seq=114 Ack=319 Win=15680 Len=0 TSval=85784531 TSecr=155546503 8 0.017647 192.168.10.205 -> 192.168.10.15 TCP 66 39202 > 80 [FIN, ACK] Seq=114 Ack=319 Win=15680 Len=0 TSval=85784534 TSecr=155546503 9 0.017944 192.168.10.15 -> 192.168.10.205 TCP 66 80 > 39202 [FIN, ACK] Seq=319 Ack=115 Win=14592 Len=0 TSval=155546507 TSecr=85784534 10 0.017967 192.168.10.205 -> 192.168.10.15 TCP 66 39202 > 80 [ACK] Seq=115 Ack=320 Win=15680 Len=0 TSval=85784534 TSecr=155546507 |
read pcap with scapy.
If you have not installed scapy , you can install it with “apt-get install python-scapy”
10 packets ( a[0] , a[1] … a[9] )
# scapy >>> a = rdpcap("wget.pcap") >>> a <wget.pcap: TCP:10 UDP:0 ICMP:0 Other:0> >>> a.show() 0000 Ether / IP / TCP 192.168.10.205:39202 > 192.168.10.15:http S <- a[0] 0001 Ether / IP / TCP 192.168.10.15:http > 192.168.10.205:39202 SA <- a[1] 0002 Ether / IP / TCP 192.168.10.205:39202 > 192.168.10.15:http A 0003 Ether / IP / TCP 192.168.10.205:39202 > 192.168.10.15:http PA / Raw 0004 Ether / IP / TCP 192.168.10.15:http > 192.168.10.205:39202 A 0005 Ether / IP / TCP 192.168.10.15:http > 192.168.10.205:39202 PA / Raw 0006 Ether / IP / TCP 192.168.10.205:39202 > 192.168.10.15:http A 0007 Ether / IP / TCP 192.168.10.205:39202 > 192.168.10.15:http FA 0008 Ether / IP / TCP 192.168.10.15:http > 192.168.10.205:39202 FA 0009 Ether / IP / TCP 192.168.10.205:39202 > 192.168.10.15:http A <- a[9] |
a[0] : client -> web : syn
a[1] : web -> client ; syn,ack
a[2] : client -> web : ack
a[3] : client -> web : ack , HTTP GET
a[4] : web -> client : ack
a[5] : web -> client : ack , HTTP Response
a[6] : client -> web : ack
a[7] : client -> web : fin , ack
a[8] : web -> client : fin , ack
a[9] : client -> web : fin
[ syn , syn ack , ack ]
a[0] :syn packet ( client -> web )
>>> a[0].show <bound method Ether.show of <Ether dst=00:26:55:e1:ee:ee src=52:54:00:7b:72:35 type=IPv4 |<IP version=4L ihl=5L tos=0x0 len=60 id=29010 flags=DF frag=0L ttl=64 proto=tcp chksum=0x333d src=192.168.10.205 dst=192.168.10.15 options=[] |<TCP sport=39202 dport=http seq=1395951232 ack=0 dataofs=10L reserved=0L flags=S window=14600 chksum=0x965b urgptr=0 options=[('MSS', 1460), ('SAckOK', ''), ('Timestamp', (85784530, 0)), ('NOP', None), ('WScale', 5)] |>>>> |
a[1] : syn , ack packet ( web -> client )
>>> a[1].show <bound method Ether.show of <Ether dst=52:54:00:7b:72:35 src=00:26:55:e1:ee:ee type=IPv4 |<IP version=4L ihl=5L tos=0x0 len=60 id=0 flags=DF frag=0L ttl=64 proto=tcp chksum=0xa48f src=192.168.10.15 dst=192.168.10.205 options=[] |<TCP sport=http dport=39202 seq=245303587 ack=1395951233 dataofs=10L reserved=0L flags=SA window=14480 chksum=0x965b urgptr=0 options=[('MSS', 1460), ('SAckOK', ''), ('Timestamp', (155546502, 85784530)), ('NOP', None), ('WScale', 7)] |>>>> |
a[2] : ack packet ( client -> web )
>>> a[2].show <bound method Ether.show of <Ether dst=00:26:55:e1:ee:ee src=52:54:00:7b:72:35 type=IPv4 |<IP version=4L ihl=5L tos=0x0 len=52 id=29011 flags=DF frag=0L ttl=64 proto=tcp chksum=0x3344 src=192.168.10.205 dst=192.168.10.15 options=[] |<TCP sport=39202 dport=http seq=1395951233 ack=245303588 dataofs=8L reserved=0L flags=A window=457 chksum=0x9653 urgptr=0 options=[('NOP', None), ('NOP', None), ('Timestamp', (85784530, 155546502))] |>>>> |
about ack # of a[2] ( web -> client )
ack # of a[2] ( web -> client ) must be client’s seq # ( a[0] ) plus 1
Let’s check pcap file.
the following is seq # of syn packet ( client -> web )
>>> a[0].seq 1395951232 |
and the following is ack # of syn , ack packet ( web -> client )
>>> a[1].ack 1395951233 <- ack # of syn,ack is client’s seq # plus 1 >>> a[1].ack == a[0].seq + 1 True |
Here’s a sample script of syn , syn ack , ack part is like this :
# cat -n http_simple_server.py 1 #!/usr/bin/env python 2 from scapy.all import * 3 4 # sniff one packet from 192.168.10.205 ( tcp syn packet ) 5 a=sniff(count=1,filter="tcp and host 192.168.10.205 and port 80") 6 7 # get client's TCP Sport , Dport , seq number from syn packet 8 sport_num=a[0].sport 9 dport_num=a[0].dport 10 seq_num=a[0].seq 11 ack_num=a[0].seq+1 12 13 # build IP layer 14 ip=IP(src="192.168.10.15",dst="192.168.10.205") 15 16 # build TCP layer 17 tcp_synack=TCP(sport=dport_num, dport=sport_num,flags="SA", seq=seq_num, ack=ack_num, options=[('MSS', 1460), ('SAckOK', ''), ('NOP', None), ('WScale', 7)]) 18 19 # send SYN ACK packet to client and get ack packet from client 20 answer=sr1(ip/tcp_synack) |
[ http get and that response ]
Next , we need to handle HTTP GET ( client -> web ) , ack ( web -> client ) and HTTP response ( web -> client )
a[3] : HTTP get request from client ( client -> web )
>>> a[3] <Ether dst=00:26:55:e1:ee:ee src=52:54:00:7b:72:35 type=IPv4 |<IP version=4L ihl=5L tos=0x0 len=165 id=29012 flags=DF frag=0L ttl=64 proto=tcp chksum=0x32d2 src=192.168.10.205 dst=192.168.10.15 options=[] |<TCP sport=39202 dport=http seq=1395951233 ack=245303588 dataofs=8L reserved=0L flags=PA window=457 chksum=0x96c4 urgptr=0 options=[('NOP', None), ('NOP', None), ('Timestamp', (85784530, 155546502))] |<Raw load='GET / HTTP/1.1\r\nUser-Agent: Wget/1.13.4 (linux-gnu)\r\nAccept: */*\r\nHost: 192.168.10.15\r\nConnection: Keep-Alive\r\n\r\n' |>>>> |
a[4] : ack to client ( web -> client )
>>> a[4] <Ether dst=52:54:00:7b:72:35 src=00:26:55:e1:ee:ee type=IPv4 |<IP version=4L ihl=5L tos=0x0 len=52 id=31395 flags=DF frag=0L ttl=64 proto=tcp chksum=0x29f4 src=192.168.10.15 dst=192.168.10.205 options=[] |<TCP sport=http dport=39202 seq=245303588 ack=1395951346 dataofs=8L reserved=0L flags=A window=114 chksum=0x9653 urgptr=0 optio |
a[5] : HTTP response to client ( web -> client )
>>> a[5] <Ether dst=52:54:00:7b:72:35 src=00:26:55:e1:ee:ee type=IPv4 |<IP version=4L ihl=5L tos=0x0 len=370 id=31396 flags=DF frag=0L ttl=64 proto=tcp chksum=0x28b5 src=192.168.10.15 dst=192.168.10.205 options=[] |<TCP sport=http dport=39202 seq=245303588 ack=1395951346 dataofs=8L reserved=0L flags=PA window=114 chksum=0x9791 urgptr=0 options=[('NOP', None), ('NOP', None), ('Timestamp', (155546503, 85784530))] |<Raw load='HTTP/1.1 200 OK\r\nDate: Mon, 10 Dec 2012 06:33:53 GMT\r\nServer: Apache/2.2.22 (Ubuntu)\r\nLast-Modified: Mon, 10 Dec 2012 06:18:23 GMT\r\nETag: "1062407-6-4d079884fb4f2"\r\nAccept-Ranges: bytes\r\nContent-Length: 6\r\nVary: Accept-Encoding\r\nKeep-Alive: timeout=5, max=100\r\nConnection: Keep-Alive\r\nContent-Type: text/html\r\n\r\nhello\n' |>>>> |
a[4] : ack to client ( web -> client )
a[4]’s ack # must be a[3]’s seq + length of a[3]’ http payload.
let’s calculate to check whether or not this formula is correct.
>>> a[4].ack 1395951346 >>> a[3].seq 1395951233 >>> a[3].load 'GET / HTTP/1.1\r\nUser-Agent: Wget/1.13.4 (linux-gnu)\r\nAccept: */*\r\nHost: 192.168.10.15\r\nConnection: Keep-Alive\r\n\r\n' >>> len(a[3].load) 113 >>> a[3].seq+len(a[3].load) 1395951346 >>> a[4].ack == a[3].seq+len(a[3].load) True |
a[4]’s seq # must be a[1]’s seq # + 1
>>> a[1].seq+1 == a[4].seq True |
a[1] is syn ack to client ( web -> client )
a[5] : HTTP response ( web -> client )
>>> a[5] <Ether dst=52:54:00:7b:72:35 src=00:26:55:e1:ee:ee type=IPv4 |<IP version=4L ihl=5L tos=0x0 len=370 id=31396 flags=DF frag=0L ttl=64 proto=tcp chksum=0x28b5 src=192.168.10.15 dst=192.168.10.205 options=[] |<TCP sport=http dport=39202 seq=245303588 ack=1395951346 dataofs=8L reserved=0L flags=PA window=114 chksum=0x9791 urgptr=0 options=[('NOP', None), ('NOP', None), ('Timestamp', (155546503, 85784530))] |<Raw load='HTTP/1.1 200 OK\r\nDate: Mon, 10 Dec 2012 06:33:53 GMT\r\nServer: Apache/2.2.22 (Ubuntu)\r\nLast-Modified: Mon, 10 Dec 2012 06:18:23 GMT\r\nETag: "1062407-6-4d079884fb4f2"\r\nAccept-Ranges: bytes\r\nContent-Length: 6\r\nVary: Accept-Encoding\r\nKeep-Alive: timeout=5, max=100\r\nConnection: Keep-Alive\r\nContent-Type: text/html\r\n\r\nhello\n' |>>>> |
a[5]’s ack # equals a[4]’s ack #
>>> a[4].ack == a[5].ack True |
a[5]’s seq # equals a[4]’s seq #
>>> a[5].seq == a[4].seq True |
[ close connections ]
a[6] , a[7] , a[8] , a[9]
0006 Ether / IP / TCP 192.168.10.205:39202 > 192.168.10.15:http A <- a[6] 0007 Ether / IP / TCP 192.168.10.205:39202 > 192.168.10.15:http FA <- a[7] 0008 Ether / IP / TCP 192.168.10.15:http > 192.168.10.205:39202 FA <- a[8] 0009 Ether / IP / TCP 192.168.10.205:39202 > 192.168.10.15:http A <- a[9] |
a[6] : ack : client -> web
ack [6]’s ack # is a[5] seq + len(a[5].load)
>>> a[5].load 'HTTP/1.1 200 OK\r\nDate: Mon, 10 Dec 2012 06:33:53 GMT\r\nServer: Apache/2.2.22 (Ubuntu)\r\nLast-Modified: Mon, 10 Dec 2012 06:18:23 GMT\r\nETag: "1062407-6-4d079884fb4f2"\r\nAccept-Ranges: bytes\r\nContent-Length: 6\r\nVary: Accept-Encoding\r\nKeep-Alive: timeout=5, max=100\r\nConnection: Keep-Alive\r\nContent-Type: text/html\r\n\r\nhello\n' >>> len(a[5].load) 318 >>> a[5].seq + len(a[5].load) 245303906 >>> a[6].ack 245303906 >>> >>> a[6].ack == a[5].seq + len(a[5].load) True |
a[6]’s seq # is a[3].seq + len(a[3].load)
>>> a[3].load 'GET / HTTP/1.1\r\nUser-Agent: Wget/1.13.4 (linux-gnu)\r\nAccept: */*\r\nHost: 192.168.10.15\r\nConnection: Keep-Alive\r\n\r\n' >>> len(a[3].load) 113 >>> a[3].seq + len(a[3].load) 1395951346 >>> >>> a[3].seq 1395951233 >>> a[6].seq 1395951346 >>> >>> a[6].seq == a[3].seq + len(a[3].load) True |
a[7] : fin , ack ( web -> client )
seq # of a[7] equals a[6]’s seq #
>>> a[7].seq == a[6].seq True |
ack # of a[7] equals a[6]’s ack #
>>> a[7].ack == a[6].ack True |
a[8] : fin , ack ( web -> client )
seq # of a[8] equals a[7]’s ack
>>> a[8].seq == a[7].ack True |
ack # of a[8] equals a[7]’s ack + plus 1
>>> a[8].ack == a[7].seq+1 True |
a[9] : ack ( client -> web )
a[9]’s seq # equals a[8]’s ack
>>> a[9].seq == a[8].ack True |
a[9]’s ack # equals
>>> a[9].ack == a[8].seq+1 True |
Let’s make simple web server with scapy.
# cat -n http_server_scapy.py 1 #!/usr/bin/env python 2 from scapy.all import * 3 import os 4 5 # a[0] sniff one packet ( syn packet ) from 192.168.10.205 6 a=sniff(count=1,filter="tcp and port 80 and host 192.168.10.205") 7 8 # prepare for a[1] : web -> client syn , ack 9 Sport_num=a[0].sport 10 11 Seq_num1=a[0].seq # for a[1] 12 Ack_num1=a[0].seq+1 # for a[1] 13 14 # a[1] build IP layer 15 ip=IP(src="192.168.10.15",dst="192.168.10.205") 16 17 # a[1] build TCP layer ( syn,ack : web -> client ) 18 tcp_synack1=TCP(sport=80, dport=Sport_num,flags="SA", seq=Seq_num1, ack=Ack_num1, options=[('MSS', 1460), ('SAckOK', ''), ('NOP', None), ('WScale', 7)]) 19 20 # a[1] : SYN ACK packet (sr1) to client and put client's response into answer2 21 answer2=sr1(ip/tcp_synack1) 22 23 # a[3] recieve the HTTP GET ( client -> web ) 24 GEThttp = sniff(filter="tcp and port 80 and host 192.168.10.205",count=1,prn=lambda x:x.sprintf("{IP:%IP.src%: %TCP.dport%}")) 25 26 # a[4] ack , seq number ( web -> client ) 27 Ack_num4=Ack_num1+len(GEThttp[0].load) 28 Seq_num4=Seq_num1+1 29 30 # a[4] build TCP layer 31 tcp_ack4=TCP(sport=80, dport=Sport_num, flags="AP", seq=Seq_num4, ack=Ack_num4, options=[('MSS', 1460), ('SAckOK', ''), ('NOP', None), ('WScale', 7)]) 32 33 # a[5] send ack to client 34 sr1(ip/tcp_ack4) 35 36 # a[6] build HTTP response ( web -> client ) 37 html1='HTTP/1.1 200 OK\r\nDate: Mon, 10 Dec 2012 06:33:53 GMT\r\nServer: Apache/2.2.22 (Ubuntu)\r\nLast-Modified: Mon, 10 Dec 2012 06:18:23 GMT\r\nETag: "1062407-6-4d079884fb4f2"\r\nAccept-Ranges: bytes\r\nContent-Length: 6\r\nVary: Accept-Encoding\r\nKeep-Alive: timeout=5, max=100\r\nConnection: Keep-Alive\r\nContent-Type: text/html\r\n\r\nhello\n' 38 39 # a[5] build TCP layer 40 tcp5=TCP(sport=80, dport=Sport_num, flags="PA", seq=Seq_num4, ack=Ack_num4, options=[('MSS', 1460), ('SAckOK', ''), ('NOP', None), ('WScale', 7)]) 41 42 # a[5] send HTTP response ( web -> client ) 43 answer6=sr1(ip/tcp5/html1) 44 45 # close connection ( web -> client ) 46 tcp6=TCP(sport=80, dport=Sport_num, flags="FA",seq=Seq_num4, ack=Ack_num4,options=[('MSS', 1460), ('SAckOK', ''), ('NOP', None), ('WScale', 7)]) 47 48 # close connection ( web -> client ) 49 send(ip/tcp5) |
Let’s test the script
client ( 192.168.10.205 ) --- scapy web server ( 192.168.10.15 )
Before running the script , add iptables rule to avoid the scapy web server from sending RST to the client.
on the scapy web server
# iptables –F # iptables -A OUTPUT -p tcp --tcp-flags RST RST -j DROP |
run the script on the scapy web server
# ./http_server_scapy.py WARNING: No route found for IPv6 destination :: (no default route?) |
wget from the client.
Okay.
# wget -d http://192.168.10.15 -O - Setting --output-document (outputdocument) to - DEBUG output created by Wget 1.13.4 on linux-gnu. URI encoding = `ANSI_X3.4-1968' --2012-12-11 17:46:52-- http://192.168.10.15/ Connecting to 192.168.10.15:80... connected. Created socket 3. Releasing 0x0000000001cf5650 (new refcount 0). Deleting unused 0x0000000001cf5650. ---request begin--- GET / HTTP/1.1 User-Agent: Wget/1.13.4 (linux-gnu) Accept: */* Host: 192.168.10.15 Connection: Keep-Alive ---request end--- HTTP request sent, awaiting response... ---response begin--- HTTP/1.1 200 OK Date: Mon, 10 Dec 2012 01:33:53 GMT Server: Apache/2.2.22 (Ubuntu) Last-Modified: Mon, 10 Dec 2012 06:18:23 GMT ETag: "1062407-6-4d079884fb4f2" Accept-Ranges: bytes Content-Length: 6 Vary: Accept-Encoding Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Content-Type: text/html ---response end--- 200 OK Registered socket 3 for persistent reuse. Length: 6 [text/html] Saving to: `STDOUT' 0% [ ] 0 --.-K/s hello 100%[======================================>] 6 --.-K/s in 0s 2012-12-11 23:46:53 (454 KB/s) - written to stdout [6/6] |
on the scapy web server
# ./http_server_scapy.py WARNING: No route found for IPv6 destination :: (no default route?) Begin emission: ......Finished to send 1 packets. * Received 7 packets, got 1 answers, remaining 0 packets Begin emission: .Finished to send 1 packets. .* Received 3 packets, got 1 answers, remaining 0 packets Begin emission: .Finished to send 1 packets. * Received 2 packets, got 1 answers, remaining 0 packets . Sent 1 packets. |
cap data on the client.
nn , it seems that clients and server could not close connections..
anyway , I was able to establish TCP connection and send/receive HTTP data with scapy.
# tshark –nr foo.pcap tshark: Lua: Error during loading: [string "/usr/share/wireshark/init.lua"]:45: dofile has been disabled Running as user "root" and group "root". This could be dangerous. 1 0.000000 192.168.10.205 -> 192.168.10.15 TCP 74 54804 > 80 [SYN] Seq=0 Win=14600 Len=0 MSS=1460 SACK_PERM=1 TSval=366365 TSecr=0 WS=32 2 0.115611 192.168.10.15 -> 192.168.10.205 TCP 66 80 > 54804 [SYN, ACK] Seq=0 Ack=1 Win=8192 Len=0 MSS=1460 SACK_PERM=1 WS=128 3 0.115657 192.168.10.205 -> 192.168.10.15 TCP 54 54804 > 80 [ACK] Seq=1 Ack=1 Win=14624 Len=0 4 0.121249 192.168.10.205 -> 192.168.10.15 HTTP 167 GET / HTTP/1.1 5 0.467028 192.168.10.205 -> 192.168.10.15 HTTP 167 [TCP Retransmission] GET / HTTP/1.1 6 0.489140 192.168.10.15 -> 192.168.10.205 TCP 66 80 > 54804 [PSH, ACK] Seq=1 Ack=7 Win=1048576 Len=0 MSS=1460 SACK_PERM=1 WS=128 7 0.669716 192.168.10.15 -> 192.168.10.205 HTTP 384 HTTP/1.1 200 OK (text/html) 8 0.669755 192.168.10.205 -> 192.168.10.15 TCP 54 54804 > 80 [ACK] Seq=114 Ack=319 Win=15680 Len=0 9 0.682858 192.168.10.205 -> 192.168.10.15 TCP 54 54804 > 80 [FIN, ACK] Seq=114 Ack=319 Win=15680 Len=0 10 0.752542 192.168.10.15 -> 192.168.10.205 TCP 66 80 > 54804 [PSH, ACK] Seq=1 Ack=7 Win=1048576 Len=0 MSS=1460 SACK_PERM=1 WS=128 11 0.752569 192.168.10.205 -> 192.168.10.15 TCP 54 [TCP Dup ACK 9#1] 54804 > 80 [ACK] Seq=115 Ack=319 Win=15680 Len=0 12 1.162999 192.168.10.205 -> 192.168.10.15 HTTP 161 [TCP Retransmission] HTTP/1.1 13 2.559061 192.168.10.205 -> 192.168.10.15 HTTP 161 [TCP Retransmission] HTTP/1.1 |
When I run the script , I sometimes face the following error.
# ./http_server_scapy.py WARNING: No route found for IPv6 destination :: (no default route?) Traceback (most recent call last): File "./http_server_scapy.py", line 11, in <module> Seq_num1=a[0].seq # for a[1] File "/usr/lib/python2.7/dist-packages/scapy/packet.py", line 176, in __getattr__ fld,v = self.getfield_and_val(attr) File "/usr/lib/python2.7/dist-packages/scapy/packet.py", line 172, in getfield_and_val return self.payload.getfield_and_val(attr) File "/usr/lib/python2.7/dist-packages/scapy/packet.py", line 172, in getfield_and_val return self.payload.getfield_and_val(attr) File "/usr/lib/python2.7/dist-packages/scapy/packet.py", line 172, in getfield_and_val return self.payload.getfield_and_val(attr) File "/usr/lib/python2.7/dist-packages/scapy/packet.py", line 172, in getfield_and_val return self.payload.getfield_and_val(attr) File "/usr/lib/python2.7/dist-packages/scapy/packet.py", line 1057, in getfield_and_val raise AttributeError(attr) AttributeError: seq # |
If you see similar errors , please run the script until you succeed.
# ./http_server_scapy.py WARNING: No route found for IPv6 destination :: (no default route?) |
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.