void rebuild_stream(uchar *payload, ushort psize, ulong seq, int syn) { ulong newseq; ushort newpsize; struct tcp_frag *frag; struct tcp_stream *stream; stream = _fromsv ? _svstream : _clstream; if (stream->first) { stream->first = 0; stream->seq = seq + psize; if (syn) { ++stream->seq; } //stream->wpcb(payload, payload_size, stream->user); handle_packet(payload, psize); return; } if (seq < stream->seq) { newseq = seq + psize; if (newseq > stream->seq) { newpsize = stream->seq - seq; if (newpsize > psize) { payload = 0; psize = 0; } else { payload = payload + newpsize; psize -= newpsize; } seq = stream->seq; psize = newseq - stream->seq; } } if (seq == stream->seq) { stream->seq += psize; if (syn) { ++stream->seq; } if (payload) { //stream->wpcb(payload, payload_size, stream->user); handle_packet(payload, psize); } while (check_fragments(stream)); } else { if (psize > 0 && seq > stream->seq) { frag = malloc(sizeof(struct tcp_frag)); frag->payload = malloc(psize); frag->seq = seq; frag->psize = psize; memcpy(frag->payload, payload, psize); if (stream->frags) { frag->next = stream->frags; } else { frag->next = 0; } stream->frags = frag; } } }
void reassemble_tcp( guint32 tcp_stream, guint32 sequence, guint32 acknowledgement, guint32 length, const char* data, guint32 data_length, int synflag, address *net_src, address *net_dst, guint srcport, guint dstport) { guint8 srcx[MAX_IPADDR_LEN], dstx[MAX_IPADDR_LEN]; int src_index, j, first = 0, len; guint32 newseq; tcp_frag *tmp_frag; tcp_stream_chunk sc; src_index = -1; /* First, check if this packet should be processed. */ if (find_tcp_index) { if ((port[0] == srcport && port[1] == dstport && ADDRESSES_EQUAL(&tcp_addr[0], net_src) && ADDRESSES_EQUAL(&tcp_addr[1], net_dst)) || (port[1] == srcport && port[0] == dstport && ADDRESSES_EQUAL(&tcp_addr[1], net_src) && ADDRESSES_EQUAL(&tcp_addr[0], net_dst))) { find_tcp_index = FALSE; tcp_stream_to_follow = tcp_stream; } else { return; } } else if ( tcp_stream != tcp_stream_to_follow ) return; if ((net_src->type != AT_IPv4 && net_src->type != AT_IPv6) || (net_dst->type != AT_IPv4 && net_dst->type != AT_IPv6)) return; if (net_src->type == AT_IPv4) len = 4; else len = 16; memcpy(srcx, net_src->data, len); memcpy(dstx, net_dst->data, len); /* follow_tcp_index() needs to learn address/port pairs */ if (find_tcp_addr) { find_tcp_addr = FALSE; memcpy(ip_address[0], net_src->data, net_src->len); port[0] = srcport; memcpy(ip_address[1], net_dst->data, net_dst->len); port[1] = dstport; } /* Check to see if we have seen this source IP and port before. (Yes, we have to check both source IP and port; the connection might be between two different ports on the same machine.) */ for( j=0; j<2; j++ ) { if (memcmp(src_addr[j], srcx, len) == 0 && src_port[j] == srcport ) { src_index = j; } } /* we didn't find it if src_index == -1 */ if( src_index < 0 ) { /* assign it to a src_index and get going */ for( j=0; j<2; j++ ) { if( src_port[j] == 0 ) { memcpy(src_addr[j], srcx, len); src_port[j] = srcport; src_index = j; first = 1; break; } } } if( src_index < 0 ) { fprintf( stderr, "ERROR in reassemble_tcp: Too many addresses!\n"); return; } if( data_length < length ) { incomplete_tcp_stream = TRUE; } /* Before adding data for this flow to the data_out_file, check whether * this frame acks fragments that were already seen. This happens when * frames are not in the capture file, but were actually seen by the * receiving host (Fixes bug 592). */ if( frags[1-src_index] ) { memcpy(sc.src_addr, dstx, len); sc.src_port = dstport; sc.dlen = 0; /* Will be filled in in check_fragments */ while ( check_fragments( 1-src_index, &sc, acknowledgement ) ) ; } /* Initialize our stream chunk. This data gets written to disk. */ memcpy(sc.src_addr, srcx, len); sc.src_port = srcport; sc.dlen = data_length; /* now that we have filed away the srcs, lets get the sequence number stuff figured out */ if( first ) { /* this is the first time we have seen this src's sequence number */ seq[src_index] = sequence + length; if( synflag ) { seq[src_index]++; } /* write out the packet data */ write_packet_data( src_index, &sc, data ); return; } /* if we are here, we have already seen this src, let's try and figure out if this packet is in the right place */ if( sequence < seq[src_index] ) { /* this sequence number seems dated, but check the end to make sure it has no more info than we have already seen */ newseq = sequence + length; if( newseq > seq[src_index] ) { guint32 new_len; /* this one has more than we have seen. let's get the payload that we have not seen. */ new_len = seq[src_index] - sequence; if ( data_length <= new_len ) { data = NULL; data_length = 0; incomplete_tcp_stream = TRUE; } else { data += new_len; data_length -= new_len; } sc.dlen = data_length; sequence = seq[src_index]; length = newseq - seq[src_index]; /* this will now appear to be right on time :) */ } } if ( sequence == seq[src_index] ) { /* right on time */ seq[src_index] += length; if( synflag ) seq[src_index]++; if( data ) { write_packet_data( src_index, &sc, data ); } /* done with the packet, see if it caused a fragment to fit */ while( check_fragments( src_index, &sc, 0 ) ) ; } else { /* out of order packet */ if(data_length > 0 && ((glong)(sequence - seq[src_index]) > 0) ) { tmp_frag = (tcp_frag *)g_malloc( sizeof( tcp_frag ) ); tmp_frag->data = (gchar *)g_malloc( data_length ); tmp_frag->seq = sequence; tmp_frag->len = length; tmp_frag->data_len = data_length; memcpy( tmp_frag->data, data, data_length ); if( frags[src_index] ) { tmp_frag->next = frags[src_index]; } else { tmp_frag->next = NULL; } frags[src_index] = tmp_frag; } } } /* end reassemble_tcp */
/* record network stream as per spec in opt */ static int record() { int rsock = -1, destfd = -1, rc = 0, wtime_sec = 0; struct in_addr raddr; struct timeval rtv; struct dstream_ctx ds; ssize_t nmsgs = 0; ssize_t nrcv = -1, lrcv = -1, t_delta = 0; int64_t n_total = 0; ssize_t nwr = -1, lwr = -1; sig_atomic_t quit = 0; struct rdata_opt ropt; int oflags = 0; char* data = NULL; static const u_short RSOCK_TIMEOUT = 5; extern const char CMD_UDP[]; /* NOPs to eliminate warnings in lean version */ (void)&t_delta; (void)&lrcv; t_delta = lrcv = lwr = 0; quit=0; check_fragments( NULL, 0, 0, 0, 0, g_flog ); /* init */ do { data = malloc( g_recopt.bufsize ); if( NULL == data ) { mperror(g_flog, errno, "%s: cannot allocate [%ld] bytes", __func__, (long)g_recopt.bufsize ); rc = ERR_INTERNAL; break; } rc = subscribe( &rsock, &raddr ); if( 0 != rc ) break; rtv.tv_sec = RSOCK_TIMEOUT; rtv.tv_usec = 0; rc = setsockopt( rsock, SOL_SOCKET, SO_RCVTIMEO, &rtv, sizeof(rtv) ); if( -1 == rc ) { mperror(g_flog, errno, "%s: setsockopt - SO_RCVTIMEO", __func__); rc = ERR_INTERNAL; break; } oflags = O_CREAT | O_TRUNC | O_WRONLY | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; # if defined(O_LARGEFILE) /* O_LARGEFILE is not defined under FreeBSD ??-7.1 */ oflags |= O_LARGEFILE; # endif destfd = open( g_recopt.dstfile, oflags, (mode_t)(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)); if( -1 == destfd ) { mperror( g_flog, errno, "%s: cannot create destination file [%s]", __func__, g_recopt.dstfile ); rc = ERR_INTERNAL; break; } rc = calc_buf_settings( &nmsgs, NULL ); if (0 != rc) return -1; if( nmsgs < (ssize_t)1 ) { (void) tmfprintf( g_flog, "Buffer for inbound data is too small [%ld] bytes; " "the minimum size is [%ld] bytes\n", (long)g_recopt.bufsize, (long)ETHERNET_MTU ); rc = ERR_PARAM; break; } TRACE( (void)tmfprintf( g_flog, "Inbound buffer set to " "[%d] messages\n", nmsgs ) ); rc = init_dstream_ctx( &ds, CMD_UDP, NULL, nmsgs ); if( 0 != rc ) return -1; (void) set_nice( g_recopt.nice_incr, g_flog ); /* set up alarm to break main loop */ if( 0 != g_recopt.end_time ) { wtime_sec = (int)difftime( g_recopt.end_time, time(NULL) ); assert( wtime_sec >= 0 ); (void) alarm( wtime_sec ); (void)tmfprintf( g_flog, "Recording will end in [%d] seconds\n", wtime_sec ); } } while(0); /* record loop */ ropt.max_frgs = g_recopt.rbuf_msgs; ropt.buf_tmout = -1; for( n_total = 0; (0 == rc) && !(quit = must_quit()); ) { nrcv = read_data( &ds, rsock, data, g_recopt.bufsize, &ropt ); if( -1 == nrcv ) { rc = ERR_INTERNAL; break; } if( 0 == n_total ) { (void) tmfprintf( g_flog, "Recording to file=[%s] started.\n", g_recopt.dstfile ); } TRACE( check_fragments( "received new", g_recopt.bufsize, lrcv, nrcv, t_delta, g_flog ) ); lrcv = nrcv; if( nrcv > 0 ) { if( g_recopt.max_fsize && ((n_total + nrcv) >= g_recopt.max_fsize) ) { break; } nwr = write_data( &ds, data, nrcv, destfd ); if( -1 == nwr ) { rc = ERR_INTERNAL; break; } n_total += (size_t)nwr; /* TRACE( tmfprintf( g_flog, "Wrote [%ld] to file, total=[%ld]\n", (long)nwr, (long)n_total ) ); */ TRACE( check_fragments( "wrote to file", nrcv, lwr, nwr, t_delta, g_flog ) ); lwr = nwr; } if( ds.flags & F_SCATTERED ) reset_pkt_registry( &ds ); } /* record loop */ (void) tmfprintf( g_flog, "Recording to file=[%s] stopped at filesize=[%lu] bytes\n", g_recopt.dstfile, (u_long)n_total ); /* CLEANUP */ (void) alarm(0); TRACE( (void)tmfprintf( g_flog, "Exited record loop: wrote [%lu] bytes to file [%s], " "rc=[%d], alarm=[%ld], quit=[%ld]\n", (u_long)n_total, g_recopt.dstfile, rc, g_alarm, (long)quit ) ); free_dstream_ctx( &ds ); if( data ) free( data ); close_mcast_listener( rsock, &raddr ); if( destfd >= 0 ) (void) close( destfd ); if( quit ) TRACE( (void)tmfprintf( g_flog, "%s process must quit\n", g_udpxrec_app ) ); return rc; }
/* relay traffic from source to destination socket * */ static int relay_traffic( int ssockfd, int dsockfd, struct server_ctx* ctx, int dfilefd, const struct in_addr* mifaddr ) { volatile sig_atomic_t quit = 0; int rc = 0; ssize_t nmsgs = -1; ssize_t nrcv = 0, nsent = 0, nwr = 0, lrcv = 0, lsent = 0; char* data = NULL; size_t data_len = g_uopt.rbuf_len; struct rdata_opt ropt; time_t pause_time = 0, rfr_tm = time(NULL); sigset_t ubset; const int ALLOW_PAUSES = get_flagval( "UDPXY_ALLOW_PAUSES", 0 ); const ssize_t MAX_PAUSE_MSEC = get_sizeval( "UDPXY_PAUSE_MSEC", 1000); /* permissible variation in data-packet size */ static const ssize_t t_delta = 0x20; struct dstream_ctx ds; static const int SET_PID = 1; struct tps_data tps; assert( ctx && mifaddr && MAX_PAUSE_MSEC > 0 ); (void) sigemptyset (&ubset); sigaddset (&ubset, SIGINT); sigaddset (&ubset, SIGQUIT); sigaddset (&ubset, SIGTERM); /* restore the ability to receive *quit* signals */ rc = sigprocmask (SIG_UNBLOCK, &ubset, NULL); if (0 != rc) { mperror (g_flog, errno, "%s: sigprocmask", __func__); return -1; } /* NOPs to eliminate warnings in lean version */ (void)&lrcv; (void)&lsent; (void)&t_delta; check_fragments( NULL, 0, 0, 0, 0, g_flog ); /* INIT */ rc = calc_buf_settings( &nmsgs, NULL ); if (0 != rc) return -1; TRACE( (void)tmfprintf( g_flog, "Data buffer will hold up to " "[%d] messages\n", nmsgs ) ); rc = init_dstream_ctx( &ds, ctx->cmd, g_uopt.srcfile, nmsgs ); if( 0 != rc ) return -1; (void) set_nice( g_uopt.nice_incr, g_flog ); do { if( NULL == g_uopt.srcfile ) { rc = set_timeouts( ssockfd, dsockfd, ctx->rcv_tmout, 0, ctx->snd_tmout, 0 ); if( 0 != rc ) break; } if( dsockfd > 0 ) { rc = sync_dsockbuf_len( ssockfd, dsockfd ); if( 0 != rc ) break; rc = send_http_response( dsockfd, 200, "OK" ); if( 0 != rc ) break; /* timeshift: to detect PAUSE make destination * socket non-blocking, otherwise make it blocking * (since it might have been set unblocking earlier) */ rc = set_nblock( dsockfd, (ALLOW_PAUSES ? 1 : 0) ); if( 0 != rc ) break; } data = malloc(data_len); if( NULL == data ) { mperror( g_flog, errno, "%s: malloc", __func__ ); break; } if( g_uopt.cl_tpstat ) tpstat_init( &tps, SET_PID ); } while(0); TRACE( (void)tmfprintf( g_flog, "Relaying traffic from socket[%d] " "to socket[%d], buffer size=[%d], Rmsgs=[%d], pauses=[%d]\n", ssockfd, dsockfd, data_len, g_uopt.rbuf_msgs, ALLOW_PAUSES) ); /* RELAY LOOP */ ropt.max_frgs = g_uopt.rbuf_msgs; ropt.buf_tmout = g_uopt.dhold_tmout; pause_time = 0; while( (0 == rc) && !(quit = must_quit()) ) { if( g_uopt.mcast_refresh > 0 ) { check_mcast_refresh( ssockfd, &rfr_tm, mifaddr ); } nrcv = read_data( &ds, ssockfd, data, data_len, &ropt ); if( -1 == nrcv ) break; TRACE( check_fragments( "received new", data_len, lrcv, nrcv, t_delta, g_flog ) ); lrcv = nrcv; if( dsockfd && (nrcv > 0) ) { nsent = write_data( &ds, data, nrcv, dsockfd ); if( -1 == nsent ) break; if ( nsent < 0 ) { if ( !ALLOW_PAUSES ) break; if ( 0 != pause_detect( nsent, MAX_PAUSE_MSEC, &pause_time ) ) break; } TRACE( check_fragments("sent", nrcv, lsent, nsent, t_delta, g_flog) ); lsent = nsent; } if( (dfilefd > 0) && (nrcv > 0) ) { nwr = write_data( &ds, data, nrcv, dfilefd ); if( -1 == nwr ) break; TRACE( check_fragments( "wrote to file", nrcv, lsent, nwr, t_delta, g_flog ) ); lsent = nwr; } if( ds.flags & F_SCATTERED ) reset_pkt_registry( &ds ); if( uf_TRUE == g_uopt.cl_tpstat ) tpstat_update( ctx, &tps, nsent ); } /* end of RELAY LOOP */ /* CLEANUP */ TRACE( (void)tmfprintf( g_flog, "Exited relay loop: received=[%ld], " "sent=[%ld], quit=[%ld]\n", (long)nrcv, (long)nsent, (long)quit ) ); free_dstream_ctx( &ds ); if( NULL != data ) free( data ); if( 0 != (quit = must_quit()) ) { TRACE( (void)tmfprintf( g_flog, "Child process=[%d] must quit\n", getpid()) ); } return rc; }