text: int CVE_2010_1188_VULN_tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
  			  struct tcphdr *th, unsigned len)
  {
  	struct tcp_sock *tp = tcp_sk(sk);
  	struct inet_connection_sock *icsk = inet_csk(sk);
  	int queued = 0;
  
  	tp->rx_opt.saw_tstamp = 0;
  
  	switch (sk->sk_state) {
  	case TCP_CLOSE:
  		goto discard;
  
  	case TCP_LISTEN:
  		if(th->ack)
  			return 1;
  
  		if(th->rst)
  			goto discard;
  
  		if(th->syn) {
  			if (icsk->icsk_af_ops->conn_request(sk, skb) < 0)
  				return 1;
  
  			/* Now we have several options: In theory there is 
  			 * nothing else in the frame. KA9Q has an option to 
  			 * send data with the syn, BSD accepts data with the
  			 * syn up to the [to be] advertised window and 
  			 * Solaris 2.1 gives you a protocol error. For now 
  			 * we just ignore it, that fits the spec precisely 
  			 * and avoids incompatibilities. It would be nice in
  			 * future to drop through and process the data.
  			 *
  			 * Now that TTCP is starting to be used we ought to 
  			 * queue this data.
  			 * But, this leaves one open to an easy denial of
  		 	 * service attack, and SYN cookies can't defend
  			 * against this problem. So, we drop the data
  			 * in the interest of security over speed.
  			 */
  			goto discard;
  		}
  		goto discard;
  
  	case TCP_SYN_SENT:
  		queued = tcp_rcv_synsent_state_process(sk, skb, th, len);
  		if (queued >= 0)
  			return queued;
  
  		/* Do step6 onward by hand. */
  		tcp_urg(sk, skb, th);
  		__kfree_skb(skb);
  		tcp_data_snd_check(sk, tp);
  		return 0;
  	}
  
  	if (tcp_fast_parse_options(skb, th, tp) && tp->rx_opt.saw_tstamp &&
  	    tcp_paws_discard(sk, skb)) {
  		if (!th->rst) {
  			NET_INC_STATS_BH(LINUX_MIB_PAWSESTABREJECTED);
  			tcp_send_dupack(sk, skb);
  			goto discard;
  		}
  		/* Reset is accepted even if it did not pass PAWS. */
  	}
  
  	/* step 1: check sequence number */
  	if (!tcp_sequence(tp, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq)) {
  		if (!th->rst)
  			tcp_send_dupack(sk, skb);
  		goto discard;
  	}
  
  	/* step 2: check RST bit */
  	if(th->rst) {
  		tcp_reset(sk);
  		goto discard;
  	}
  
  	tcp_replace_ts_recent(tp, TCP_SKB_CB(skb)->seq);
  
  	/* step 3: check security and precedence [ignored] */
  
  	/*	step 4:
  	 *
  	 *	Check for a SYN in window.
  	 */
  	if (th->syn && !before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt)) {
  		NET_INC_STATS_BH(LINUX_MIB_TCPABORTONSYN);
  		tcp_reset(sk);
  		return 1;
  	}
  
  	/* step 5: check the ACK field */
  	if (th->ack) {
  		int acceptable = tcp_ack(sk, skb, FLAG_SLOWPATH);
  
  		switch(sk->sk_state) {
  		case TCP_SYN_RECV:
  			if (acceptable) {
  				tp->copied_seq = tp->rcv_nxt;
  				mb();
  				tcp_set_state(sk, TCP_ESTABLISHED);
  				sk->sk_state_change(sk);
  
  				/* Note, that this wakeup is only for marginal
  				 * crossed SYN case. Passively open sockets
  				 * are not waked up, because sk->sk_sleep ==
  				 * NULL and sk->sk_socket == NULL.
  				 */
  				if (sk->sk_socket) {
  					sk_wake_async(sk,0,POLL_OUT);
  				}
  
  				tp->snd_una = TCP_SKB_CB(skb)->ack_seq;
  				tp->snd_wnd = ntohs(th->window) <<
  					      tp->rx_opt.snd_wscale;
  				tcp_init_wl(tp, TCP_SKB_CB(skb)->ack_seq,
  					    TCP_SKB_CB(skb)->seq);
  
  				/* tcp_ack considers this ACK as duplicate
  				 * and does not calculate rtt.
  				 * Fix it at least with timestamps.
  				 */
  				if (tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr &&
  				    !tp->srtt)
  					tcp_ack_saw_tstamp(sk, 0);
  
  				if (tp->rx_opt.tstamp_ok)
  					tp->advmss -= TCPOLEN_TSTAMP_ALIGNED;
  
  				/* Make sure socket is routed, for
  				 * correct metrics.
  				 */
  				icsk->icsk_af_ops->rebuild_header(sk);
  
  				tcp_init_metrics(sk);
  
  				tcp_init_congestion_control(sk);
  
  				/* Prevent spurious tcp_cwnd_restart() on
  				 * first data packet.
  				 */
  				tp->lsndtime = tcp_time_stamp;
  
  				tcp_mtup_init(sk);
  				tcp_initialize_rcv_mss(sk);
  				tcp_init_buffer_space(sk);
  				tcp_fast_path_on(tp);
  			} else {
  				return 1;
  			}
  			break;
  
  		case TCP_FIN_WAIT1:
  			if (tp->snd_una == tp->write_seq) {
  				tcp_set_state(sk, TCP_FIN_WAIT2);
  				sk->sk_shutdown |= SEND_SHUTDOWN;
  				dst_confirm(sk->sk_dst_cache);
  
  				if (!sock_flag(sk, SOCK_DEAD))
  					/* Wake up lingering close() */
  					sk->sk_state_change(sk);
  				else {
  					int tmo;
  
  					if (tp->linger2 < 0 ||
  					    (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq &&
  					     after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt))) {
  						tcp_done(sk);
  						NET_INC_STATS_BH(LINUX_MIB_TCPABORTONDATA);
  						return 1;
  					}
  
  					tmo = tcp_fin_time(sk);
  					if (tmo > TCP_TIMEWAIT_LEN) {
  						inet_csk_reset_keepalive_timer(sk, tmo - TCP_TIMEWAIT_LEN);
  					} else if (th->fin || sock_owned_by_user(sk)) {
  						/* Bad case. We could lose such FIN otherwise.
  						 * It is not a big problem, but it looks confusing
  						 * and not so rare event. We still can lose it now,
  						 * if it spins in bh_lock_sock(), but it is really
  						 * marginal case.
  						 */
  						inet_csk_reset_keepalive_timer(sk, tmo);
  					} else {
  						tcp_time_wait(sk, TCP_FIN_WAIT2, tmo);
  						goto discard;
  					}
  				}
  			}
  			break;
  
  		case TCP_CLOSING:
  			if (tp->snd_una == tp->write_seq) {
  				tcp_time_wait(sk, TCP_TIME_WAIT, 0);
  				goto discard;
  			}
  			break;
  
  		case TCP_LAST_ACK:
  			if (tp->snd_una == tp->write_seq) {
  				tcp_update_metrics(sk);
  				tcp_done(sk);
  				goto discard;
  			}
  			break;
  		}
  	} else
  		goto discard;
  
  	/* step 6: check the URG bit */
  	tcp_urg(sk, skb, th);
  
  	/* step 7: process the segment text */
  	switch (sk->sk_state) {
  	case TCP_CLOSE_WAIT:
  	case TCP_CLOSING:
  	case TCP_LAST_ACK:
  		if (!before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt))
  			break;
  	case TCP_FIN_WAIT1:
  	case TCP_FIN_WAIT2:
  		/* RFC 793 says to queue data in these states,
  		 * RFC 1122 says we MUST send a reset. 
  		 * BSD 4.4 also does reset.
  		 */
  		if (sk->sk_shutdown & RCV_SHUTDOWN) {
  			if (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq &&
  			    after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt)) {
  				NET_INC_STATS_BH(LINUX_MIB_TCPABORTONDATA);
  				tcp_reset(sk);
  				return 1;
  			}
  		}
  		/* Fall through */
  	case TCP_ESTABLISHED: 
  		tcp_data_queue(sk, skb);
  		queued = 1;
  		break;
  	}
  
  	/* tcp_data could move socket to TIME-WAIT */
  	if (sk->sk_state != TCP_CLOSE) {
  		tcp_data_snd_check(sk, tp);
  		tcp_ack_snd_check(sk);
  	}
  
  	if (!queued) { 
  discard:
  		__kfree_skb(skb);
  	}
  	return 0;
  }
  