bamed.org | chown -R bamed. ~/base
So, I have moved to the Employee Development department and I work specifically with the monitoring department at HG. That means I spend most of my time focused on training people. Primarily dealing with system performance and uptime, but also deal with Network issues and DOS/DDOS attacks. I thought as a treat to all you people out there in Internet world, I’d go over some of the non-proprietary stuff I teach. My favorite stuff to go over is networking and attacks. I briefly covered some stuff about SYN floods that I’ve worked with and now I thought I’d go over the anatomy of a TCP/IP packet, specifically an HTTP GET request for bamed.org :D.
I love tcpdump. When working in Windows (when I absolutely have to) I’ve been known to use Wireshark, but since I’m in Linux all day, working remotely over ssh, I’ve become close friends with tcpdump. My favorite thing about tcpdump is that I can view the entirety of a packet in HEX/ASCII. Below is an HTTP GET request for bamed.org from tcpdump using -XX which prints the full packet, including link layer headers, in both HEX and ASCII:
The following is a diagram of the IP and TCP headers in a TCP packet:
We will be using the above diagram to break apart a real packet dumped from tcpdump in HEX & ASCII. You can view a packet in HEX & ASCII with tcpdump with the -X flag, -XX to view the full packet including the link layer headers. The following is an HTTP GET request in HEX & ASCII:
# tcpdump -nn -XX -s 0 -i eth1 host 220.127.116.11 and port 80
07:52:44.995738 IP 18.104.22.168.48300 > 22.214.171.124.80: P 1:113(112) ack 1 win 46
0x0000: a4ba db54 44f7 0008 e3ff fd90 0800 4500 ...TD.........E.
0x0010: 00a4 6879 4000 3a06 f85d 3217 2fce b8ad ..hy@.:..]2./...
0x0020: c4ea bcac 0050 ae2e 354f 3859 9344 8018 .....P..5O8Y.D..
0x0030: 002e 1c3d 0000 0101 080a 0815 5d41 239e ...=........]A#.
0x0040: 8fc4 4745 5420 2f20 4854 5450 2f31 2e31 ..GET./.HTTP/1.1
0x0050: 0d0a 5445 3a20 6465 666c 6174 652c 677a ..TE:.deflate,gz
0x0060: 6970 3b71 3d30 2e33 0d0a 436f 6e6e 6563 ip;q=0.3..Connec
0x0070: 7469 6f6e 3a20 5445 2c20 636c 6f73 650d tion:.TE,.close.
0x0080: 0a48 6f73 743a 2062 616d 6564 2e6f 7267 .Host:.bamed.org
0x0090: 0d0a 5573 6572 2d41 6765 6e74 3a20 6c77 ..User-Agent:.lw
0x00a0: 702d 7265 7175 6573 742f 322e 3037 0d0a p-request/2.07..
Each 2 HEX characters represents one byte of data, and remember there are 8 bits in a byte. So, the first section “a4b4″ represents 2 bytes or 16 bits. The diagram shows us the packet headers broken up into bits, so keep that in mind as we go through the entire packet.
Though our diagram above does not show it, the first 14 bytes of the above packet are the link layer header, Ethernet header in this example. These are “a4ba db54 44f7 0008 e3ff fd90 0800″. The first 6 bytes are the destination MAC address, the next 6 bytes are the source MAC address, and the last 2 bytes give us the EtherType.
Server’s MAC Address (Destination): A4:Ba:DB:54:44:F7
Default Gateway’s MAC Address (Source): 00:08:e3:ff:fd:90
EtherType : 0800 (Ipv4)
Other Common EtherTypes
0×0806 – ARP
0x96DD – IPv6
The next 20 bytes make up the IP header, “4500 00a4 6879 4000 3a06 f85d 3217 2fce b8ad c4ea”. The first 2 bytes, or 16 bits, represent the IP Version, Internet Header Length (IHL), and DSCP information. DSCP + ECN is not relevant to what we do, but for more information you can go tohttp://en.wikipedia.org/wiki/DSCP. The IHL is a number that represents the number of 32-bit words in the IP header. So an IHL of 5 means there are 5 32-bit words, or that the header is 20 bytes long. In hex this is 4500 and in Binary, 0100010100000000
Version: First 4 bits (0100) = 4 (Ipv4)
IHL (Internet Header Length): Next 4 bits (0101) = 5 32-bit words
Was TOS, Now DSCP (6 bits) + ECN (2 bits): Next 8 bits (00000000) – nothing set
The next 16 bits, or 2 bytes, give us the Total Length of the packet. This is the total length without the link layer headers, so its the IP header + TCP header + Data. In the above packet this is 00a4 in hex, which converts to 164 bytes. If you count the bytes in the above packet, excluding the Ethernet header, you will find that it is 164 bytes long.
The next 16 bits give us an Identification, followed by 16 bits more that set the fragmentation flags and fragmentation offset. The identification (6879 above) is a unique number randomly generated and used along with the fragmentation flags and offset in order to put fragmented packets back together. The fragmentation flags and offset (4000 above) are broken up into 3 bits for the flags and 13 bits for the offset.
Identification (Next 16 bits) = 6879 = Decimal 26745 – Packet ID, used to identify fragments
Flags & Fragmentation Offset ( Next 16 bits) = 4000 = Binary 010 0000000000000
Flags (first 3 bits) 010 (Reserved, Don’t Fragment, More Fragments)
Fragmentation offset (13 bits) – 0′s (it’s not fragmented)
The next 16 bits (3a06) give us the TTL and the Protocol:
TTL:Protocol(next 16 bits) = 3a06 – Binary 00111010 00000110
TTL: First 8 bits – 00111010 = 58
Protocol : Next 8 bit – 00000110 = 6 = TCP
Then we have the checksum, f85d. This is a checksum for the IP header only. It is used to verify the integrity of the IP header. Then the next 32 bits give us the source IP address, and the 32 bits after that the destination IP address:
Source IP (next 32 bits): Decimal 126.96.36.199 = Hex 3217 2fce
Destination IP (next 32 bits): Decimal 188.8.131.52 = Hex b8ad c4ea
If there were any IP options they would then follow; however, we do not have any options in the packet and you would rarely see any on our farm.
This brings us to the TCP header. We were able to determine the length of the IP header based on the IHL. This lets us know where the TCP header begins. In this instance the TCP header is “bcac 0050 ae2e 354f 3859 9344 8018 002e 1c3d 0000 0101 080a 0815 5d41 239e 8fc4″.
The TCP header begins with 16 bits for the source port and 16 bits for the destination port:
Source Port (Next 16 bits) = Hex bcac = Decimal 48300
Destination Port (Next 16 bits) = Hex 0050 = Decimal 80
This is followed by a 32 bit Sequence number and a 32 bit Acknowledgement number. The Sequence and Acknowledgement numbers are used to keep track of data as it is sent back and forth between client and server. The initial SYN packet includes a randomly generated Sequence number. The SYN+ACK that acknowledges this SYN packet uses the Sequence number from the SYN packet as the Acknowledgment number in the SYN+ACK packet. Also, the server will randomly generate a Sequence number to use in the SYN+ACK packet that the client will then use as its Acknowledgement number in the ACK packet it send back. Basically, these two numbers swap between client and server, and at the completion of each transmission and subsequent acknowledgement these number increment.
Seq Number (Next 32 bits) = Hex ae2e 354f
Ack Number ( Next 32 bits) = Hex 3859 9344
The Seq and Ack are used to keep track of packets
Ack # is the expected Seq # in next Ack packet incremented by 1 as it goes
The next 16 bits include both the Data Offset as well as the TCP flags. In the above example these 16 bits are 8018 in HEX or 1000000000011000 in Binary. The first 4 bits represent the Data Offset, binary 1000 or Decimal 8. This represents the number of 32-bit words from the beginning of the TCP packet to the Data. Essentially it is the length of the TCP header. So with a Data offset of 8, the TCP packet is 8 32-bit words long or 32 bytes long.
After the 4-bit data offset there are 3 Reserved bits, always 0, and then the TCP flags.
The TCP flags are each individual Binary flags used for various functions of TCP. Most of these we have already discussed. These 9 bits are in the order (NS|CWR|ECE|URG|ACK|PSH|RST|SYN|FIN). You can easily see how this works if we take the data in binary, 000011000 and place it in a table:
NS CWR ECE URG ACK PSH RST SYN FIN
0 0 0 0 1 1 0 0 0
This shows us that the above packet has both the ACK and the PSH TCP flags set. Below is a brief explanation of these flags:
NS (1 bit) – ECN-nonce concealment protection
CWR (1 bit) – Congestion Window Reduced (CWR)
ECE (1 bit) – ECN-Echo indicates
URG (1 bit) – Urgent pointer field is significant
ACK (1 bit) – Acknowledgment field is significant. All packets after the initial SYN packet sent by the client should have this flag set.
PSH (1 bit) – Push function. Asks to push the buffered data to the receiving application.
RST (1 bit) – Reset the connection
SYN (1 bit) – Synchronize sequence numbers
FIN (1 bit) – No more data from sender
The next 16 bits are the Window scale option (002e). From wikipedia: “The window scale option is used only during the TCP 3-way handshake. The window scale value represents the number of bits to left-shift the 16-bit window size field. The window scale value can be set from 0 (no shift) to 14 for each direction independently. Both sides must send the option in their SYN segments to enable window scaling in either direction.”
Next there’s another 16 bit checksum (1c3d). This is a checksum for the TCP header. Then we have the 16-bit Urgent pointer (0000). If the URG TCP flag was set this data would be read. The Urgent pointer isn’t typically used in modern protocols.
Finally, we have our TCP options:
Options = the rest
0101 080a 0815 5d41 239e 8fc4
Option kind (1byte)
Option size (1byte)
Option data (variable)
01 = Noop (filler)
08 = TCP timestamp and echo of previous timestamp
080a 0815 5d41 239e 8fc4
8,10(0x0a),TTTT,EEEE (80 bits)
Now we get to the data. In this instance it's an HTTP GET request. Let's look at the data in HEX and ASCII:
4745 5420 2f20 4854 5450 2f31 2e31 GET./.HTTP/1.1
0d0a 5445 3a20 6465 666c 6174 652c 677a ..TE:.deflate,gz
6970 3b71 3d30 2e33 0d0a 436f 6e6e 6563 ip;q=0.3..Connec
7469 6f6e 3a20 5445 2c20 636c 6f73 650d tion:.TE,.close.
0a48 6f73 743a 2062 616d 6564 2e6f 7267 .Host:.bamed.org
0d0a 5573 6572 2d41 6765 6e74 3a20 6c77 ..User-Agent:.lw
702d 7265 7175 6573 742f 322e 3037 0d0a p-request/2.07..
The HEX value 0d0a is a newline character in ASCII, so if we take the ASCII above and replace every HEX instance of 0d0a with a newline we get a normal HTTP GET request:
GET / HTTP/1.1.
Connection: TE, close.
And this gets us to the end of the packet.
Hello, Internet! It’s been awhile, but I’m still here. Today I’d like to talk to you about SYN floods. One of the things I deal with daily in my current job is DDoS attacks, such as SYN floods. As I’ve watched the packets come in for these attacks I started noticing some patterns. I noticed at one point that many SYN floods (not all) use a 0 byte sequence number. The sequence and ack numbers in TCP are used to track a connection, since TCP is connection-oriented. The initial SYN packet includes a randomly generated sequence number between 0 and 4,294,967,295 with the ack number set to 0. Then the returning SYN+ACK packet from the server includes its own randomly generated sequence number, but uses the sequence number from the original SYN packet as its ack number. As the connection continues, these number increment by 1, and the seq and ack numbers are included in every packet where the seq number becomes the ack number of the acknowledging packet. While it is possible for a SYN number to be 0, it is extremely uncommon. Uncommon enough that we can quickly and easily filter out SYN floods that match this patter. The main thing when dealing with any kind of Denial of Service attack is to find the pattern that makes the malicious traffic unique and then you can filter it out. To check the sequence number, use
# tcpdump -i eth1 -nn tcp port 80 and 'tcp == 2' -c 100
23:13:03.087744 IP 10.1.1.1.1234> 184.108.40.206.80: Flags [S], seq 0, win 5840, length 0
23:13:03.088166 IP 10.1.1.2.4321> 192.168.1.1.80: Flags [S], seq 0, win 5840, length 0
You can see above, seq 0, which means the sequence number = 0. When I see the above actually happen in the wild (IP addresses changed to protect the innocent) there are hundreds of thousands of these packets hitting the NIC each second, and all the source IPs are spoofed, sometimes random source IPs and ports, and sometimes not (other patterns to watch for!!!). The u32 extension for iptables (I love this extension!) can filter based on any 4 or fewer bytes (or bits) in the packet. The rule below goes in mangle-PREROUTING to prevent issues with the routing cache and to block the bad packet as quicklly as possible, it’s specific on the IP and port being attacked, and then it checks that the packet is a TCP packet, checks the IHL (since the IP header length can be variable) and from that accurately gets the sequence number and checks to see if it is equal to 0. If all of these match, then the packet is dropped.
# iptables -t mangle -I PREROUTING -d 192.168.1.1 -p tcp --dport 80 -m u32 --u32 "6&0xFF=0x6&& 0>>22&0x3C@4=0x00" -j DROP
Full documentation on the u32 plugin is at http://www.netfilter.org/documentation/HOWTO/netfilter-extensions-HOWTO-3.html#ss3.21. The following is a brief explanation of how that rule works.
In its simplest form, the u32 patch for iptables grabs a block of 4 bytes, applies a mask, and then compares the results. So, we want to
start with checking if the packet is indeed TCP. The transport layer protocol for an IP packet is stored in the 9th byte of the IP header (http://bit.ly/WRuiVE for more info). Since u32 grabs 4 bytes at a time, we’ll want to grab bytes 6-9, so our filter starts with “6″. Then we apply a mask. The mask is represented in hex, but you should think of it all in binary, and it is AND’d to the 4 bytes that we grabbed. Since we only want the last byte we would use 0x000000FF, or shorten it to 0xFF. Then we compare that value to see if it’s TCP, which is 0×6. So our first check is “6&0xFF=0×6″.
Now that we’ve confirmed it’s a TCP packet, we want to find the sequence number. Apply multiple filters with “&&”. Typically, IP headers are going to be 20 bytes long; however, they can be variable length if there are any IP options, so for the sake of accuracy, we’re going to grab the IHL (Internet Header Length) from the IP header and use that to calculate the correct location of the sequence number. The IHL is the 1st byte of the IP header, so we start with byte 0. We can use “>>” to shift bytes to the right. Since we grabbed 4 bytes, 0-3, and we only want the 1st byte (0), we can shift it to the right 24 places, and only grab the second 4 bits, so we could do “0>>24&0xF”. This gets us the first byte and masks it with F (00001111) so we get the 2nd 4 bits. This number represents the length of the IP headers in 4-byte words. So to find the start of the TCP header, we can multiply this number by 4. The best way to do this, is to shift the bits to the left 2 places. Binary 101 = Decimal 5. Shift it to the left 2, and binary 10100 = Decimal 20. We do this with u32 by doing our initial shift to the right 22 instead of 24, and then apply a mask of 0x3C (111100) “0>>22&0x3C”. This will return the number of bytes in from the beginning of the IP header to the beginning of the TCP header.
Now that we’re at the beginning of the TCP header, we start from there and get bytes 4-7 which represent the sequence number. We can use @4 after the above to start from the position calculated and then grab 4 bytes starting at byte 4. This gets us the sequence number. We don’t need a mask here since all 4 bytes represent the sequence number so we just check that it equals 0, so “@4=0×00″.
I’m going to try to start posting useful stuff like the above more frequently.
A couple of years ago, we dropped cable TV and setup our XBox to watch live streaming Netflix. After awhile, I put together a computer running Boxee, and we started watching Hulu and other shows that way. Now we’ve got a PS3 along with the XBox, and Hulu Plus is available on both. I’ve also got PlayOn, and can watch Hulu, Netflix, etc. on my modded Nook Color. Basically, our family gets all of our video entertainment through the Internet and only the Internet. So when a tornado sweeped through my home town of Joplin, there was no breaking in, no important news updates, but we knew about it almost as instantly as it happened.
Thanks to Facebook and Twitter, we found out through friends and family that our hometown was under a tornado watch. Then the rumors started pouring in that St. John’s Hospital in Joplin was hit by a tornado and suffered severe damage. A quick Google search later and I’m reading live updated over twitter from various sources describing the damage, and linking to a live video feed on the Weather Channel’s website. Then the personal reports from friends and family started pouring in.
One friend reported early that her father’s house in Duquesne ( a suburb on the East side of Joplin) was destroyed. Once I realized that St. John’s and Duquesne were damaged, I realized that my sister’s house was in between those two points. I sent her a quick text, and got a call back from my brother-in-law letting me know that both my sisters and their husbands were safe, though one of my sisters’ house of damaged. A little while later we got a call from my wife’s sister. She told us that she was in her vehicle with her family at the corner of 20th and Rangeline (which we already knew suffered sever damage). My brother-in-law got some nasty cuts on his back because he was shielding the rest of his family, but he’s OK. Their car is totaled, but they’re OK.
For hours, my wife and I kept watching Facebook updates and twitter feeds, and saw pictures and videos of various spots around Joplin that we know well and the after effects of the tornado.
At some point during the chaos I had to sit back and was just a little amazed at the wealth of up-to-date information that was readily available to me, and that I was able to connect with so many family and friends and find out who was safe, who lost their house, and who was in ICU within hours of the tornado hitting thanks to both Facebook and Twitter. If we had cable, we would have been glued to the Weather Channel or CNN or some other station and would never have learned as much about the things that we cared the most about. Even the live feed on the Weather Channel’s website only focused on one small section on the far West end of Joplin, away from the center of commerce.
Anyway, whenever a disaster strikes, somebody always comments on how social media has changed the way we connect to the world, so I’m just saying it again. And in this case, it has changed for the better. Without Facebook and Twitter there would be people that I still don’t know if they’re alive or not, but thanks to these media outlets I know that my friends and family are safe.
Got a chance to help the Joplin relief effort out a bit yesterday. I only played a small part, but every bit counts. I can’t get there to help physically, but I plan to do what I can.
In case you didn’t know, Joplin is my hometown, but I moved to Houston this past December. Many of my friends and family still live in Joplin and all were effected in some way by the tornado last week. My sister’s house was hit and suffered some damage, but it is still standing. Several friends weren’t as lucky and completely lost their homes. One person I know is in ICU, but as far as I can tell, everyone else I know is alive and without serious injury.
Anyway, my former employer, is now the city’s main distribution center. So I got an email from their new tech guy yesterday. They were needing to setup additional phone lines to help deal with the volume of calls they are receiving, but he hasn’t learned the new phone system that well yet. So i spent my lunch break yesterday on the phone with their new tech guy and walked him through the process of programming additional extensions to be members of the Operator Group, and programming the various functions of the new extensions on a Samsung Prostar DCS. I know it’s not much, but it was something that helps the effort and that I was in a unique position to be the one available with the knowledge to help. It felt good to be part of things for a few minutes, but I really wish I could be there.
My son was looking at some of the pictures with me and he said, “It reminds me of Fallout.” I mentioned this to my friend Poorchoices and he said that’s exactly what it looks like.
I miss my hometown now more than ever and spend every night checking on friends on Facebook, and looking up more information on the damage, as well as the latest death toll. It just all seems so surreal. If we hadn’t moved, I’d be there in the middle of it right now, sorting donated goods, digging through rubbage, or who knows what else, and life just goes on here in Houston like nothing happened.
Anyway, if you want to help out, CHCC has regularly updated information available at http://www.chcchurch.org/the-storm regarding various ways you can help. They have a list of things they currently need as well as a place you can go to make donations.
Um.. I hate to admit it, but I just made a (l)User mistake. I just deleted my blog. Not sure exactly how, must have clicked on the wrong thing or something, or else things weren’t setup the way I thought they were. Basically, I was trying to clean up my hosting account with the wonderful hosting company Hostgator. My wife has had several blogs through the years and I was trying to remove one that she hasn’t used in a few(4-5) years. I had installed it using Fantastico Deluxe, so I went to remove it from Fantastico, but instead it deleted bamed.org!!!
Well, unlike many users I run across, I had a backup. it was about a week old, but I only lost one blog post, so not really a huge deal.
So, as any IT guy will tell you, BACKUP! BACKUP! BACKUP!
FYI, Cpanel makes this pretty easy and HostGator has an easy to follow tutorial at http://support.hostgator.com/articles/cpanel/how-to-generatedownload-a-full-backup. Restoring is actually pretty simple too. If you have root on the server it’s real easy (I don’t from home), but if you are a HostGator customer and you have a full backup all you need to do is upload it to your account and fill out the form at https://secure.hostgator.com/restore.php and be sure to specify the location of the backup that you generated and a friendly HostGator admin (maybe even me) will restore the backup for you at no charge.
If you don’t have root, and you only need to restore a few files, or a database or two, you can also do it manually. The CPanel generated backup is just a zipped up tarball that includes all of your account information in a few directories, a tarball of your home directory, and some SQL dumps of your MySQL databases. So, I untarred my backup from SSH on the suer as my user:
~: tar -xvzf ????backup-4.5.2011_18-38-58_bamed.tar.gz
?This puts the content of the backup in ~/backup-4.5.2011_18-38-58_bamed. Then all of my home directory is in a tarball named homdir.tar, so I untar it with:
~: tar -xvf backup-4.5.2011_18-38-58_bamed/homedir.tar
I ran this from my home directory, so the contents of homedir.tar extract directly into my home directory all the files going into the right places. Once that was done, then I re-created my WordPress database in MySQL by following the instructions at http://www.hostgator.com/tutorials/cpanel/hgx3/creating-a-mysql-database.htm. Not that I needed to actually follow the tutorial, I just low HG video tutorials. Save me a lot of time trying to explain step-by-step instructions. Anyway, I created the same DB name, unsername, and password that I had used before accidentally deleting everything. If you don’t have this information saved, after you restore your homedir you can pull it from your wp-config.php file.
Anyway, after recreating the DB, I was able to restore it from the backup in ~/backup-4.5.2011_18-38-58_bamed/mysql. The actual name of the backup file is the same as the name of the database you are restoring. Just go to PHPMyAdmin from Cpanel and restore the database using the instructions at http://support.hostgator.com/articles/cpanel/how-to-import-your-mysql-database. (Again, love those HG tutorials)
Then my site was back exactly as it had been on 4-5-2011 at 18:38:58(CDT). Pretty exciting ehh?
So, that’s a quick rundown of doing a manual restore. If I actually needed to restore some domain names, or email addresses, or anything else it would be a little more complicated, but I was only worried about a few files and one database so it was pretty easy and only took a few minutes. I could have let my peers at HG do it, but why waste their time when I can do it myself. Let them spend their time helping our customers.
Anyway, hopefully I’ll get back and blog some more stuff later.
“Time is an illusion. Lunchtime doubly so.” (Douglas Adams)
The only thing constant in life is change. And as such bamed and family are on the cusp of a most significant change. Seven years ago this month I began a new adventure as the ‘IT guy’ for College Heights Christian Church in Joplin, MO. “I may not have gone where I intended to go, but I think I have ended up where I needed to be.” (Douglas Adams) Later this month we will be leaving Missouri and heading South to the great state of Texas.
“Let the past hold on to itself and let the present move forward into the future.” (Douglas Adams)
At the end of this month I will be beginning a new adventure as a Linux sysadmin for HostGator.com in Houston. Anyone who knows me at all will know that I have always been a rather large fan of Linux and have made it the focus of not a few blog posts through the years. As such, I am looking forward to spending my days at a command line exercising my CLI-fu. I’m also rather excited about moving to the great town of Houston where things such as Linux Users Groups, 2600 meetings, and hacker spaces are more than something you read about on the internets.
“The fact that we live at the bottom of a deep gravity well, on the surface of a gas covered planet going around a nuclear fireball 90 million miles away and think this to be normal is obviously some indication of how skewed our perspective tends to be.” (Douglas Adams)
We should be all moved and begin settling in by Christmas. To all those we’re leaving behind, know that you will be missed, that you are welcome to stop by whenever you’re in the area, and that if you have any Linux experience HostGator is still hiring! So… until we meet again, “So long, and thanks for all the fish!” (Douglas Adams)
So, I haven’t been around here for awhile. But here I am, and I have something different for you. From time to time I try my hand at writing some fiction, typically science ficiton. Recently, I attempted something completely different and wrote a horror story for a competition at http://www.writing.com. The sub-genre, mood, and a prompt to start the story were provided and there was a 666 word limit. Well, I won! So, here it is for you to enjoy as well:
Read more ›
I was reading This Day all Gods Die by Stephen Donaldson last night and a somewhat disappointing thought occurred to me. The Gap series has been great. I’ve really enjoyed it and it has kept me on the edge of me seat through at least the last four books. The first one pretty much told us how it would end in the first chapter, but the rest have just gone from one major crisis to the next. I’ll let you read the book yourself if you want details, but the main thing is that all of humanity is at risk. Of course, the main characters have been at risk for quite sometime.
What disappointed me, is that even though I don’t yet know how the conflict will be resolved, I know it will be resolved. I expect that mankind will be saved and the “good guys” will win the day. They may not all survive, but in one way or another they will win. Similarly, in the previous books, I knew that the current conflict would be resolved. And since I also knew there were more books in the serious, I knew they would end up in trouble again. They had to; otherwise, what would the rest of the books be about?
I suppose what’s disappointing, is that even though this series has continued to surprise me, even shock me at times, and though I don’t know exactly how everything will end up, who will survive, etc., I’m reading the book expecting the day to be saved, and just waiting to see how. I don’t get to enjoy the shock and surprise that the good guys won because I already expect it. I’ve come to expect it from books, movies, and TV shows. I think more often, the good guys should lose. Humanity should be lost. Insurmountable odds should remain insurmountable. Every now and then, I’d like to read or watch a story where the hero can’t do the impossible. Actually, more often that that. The hero should lose more often than he wins. I want to see a hero driven to the brink of failure and then actually fail. Not because I’m a pessimist. Not even because I’m a realist. But if I actually thought there was a good chance the hero would fail, I could enjoy the suspense more. I would enjoy actually not knowing. Yes, I know, there are a few stories like this, but not many. When they come I am pleasantly surprised. But I want to be surprised when the hero wins. I want to be shocked when he/she/they are driven to the brink of failure and somehow come back. But as it stands, I expect it. And where’s the fun in that?