1 contributor
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <pcap.h>
#include "cnx_inode.h"
#include "inodelist.h"
#include "mypcap.h"
const int MAX_FDLINK = 10;
const int MAX_PID_LENGTH = 20;
#define MAX_SIZE 256
static char st_file_prefix[MAX_SIZE]="";//Will be time of day
static pcap_t *st_hdl_out_pcap = NULL;
const char *cst_cnx_file_details_list[]=
{
"/proc/net/tcp",
//~ "/proc/net/tcp6",
"/proc/net/udp",
//~ "/proc/net/udp6",
NULL,
};
bool is_number(const char*s) {
char* e = NULL;
uint32_t val = strtol(s, &e, 0);
return e != NULL && *e == (char)0;
}
void set_file_prefix(const char*file_prefix) {
strcpy(st_file_prefix,file_prefix);
}
void set_pcap_handle(pcap_t *pcap) {
st_hdl_out_pcap = pcap;
}
int _file_exist (const char *filename)
{
struct stat buffer;
return (stat (filename, &buffer) == 0);
}
/*
* parses a /proc/net/tcp-line of the form:
* sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt
*uid timeout inode
* 10: 020310AC:1770 9DD8A9C3:A525 01 00000000:00000000 00:00000000 00000000
*0 0 2119 1 c0f4f0c0 206 40 10 3 -1
* 11: 020310AC:0404 936B2ECF:0747 01 00000000:00000000 00:00000000 00000000
*1000 0 2109 1 c0f4fc00 368 40 20 2 -1
*
* and of the form:
* 2: 0000000000000000FFFF0000020310AC:0016
*0000000000000000FFFF00009DD8A9C3:A526 01 00000000:00000000 02:000A7214
*00000000 0 0 2525 2 c732eca0 201 40 1 2 -1
*
*/
void _addtoconninode(char *buffer) {
short int sa_family;
struct in6_addr result_addr_local = {};
struct in6_addr result_addr_remote = {};
char rem_addr[128], local_addr[128];
int local_port, rem_port;
struct in6_addr in6_local;
struct in6_addr in6_remote;
unsigned long inode;
int matches = sscanf(buffer, "%*d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %*X "
"%*X:%*X %*X:%*X %*X %*d %*d %ld %*512s\n",
local_addr, &local_port, rem_addr, &rem_port, &inode);
if (matches != 5) {
fprintf(stderr, "Unexpected buffer: '%s'\n", buffer);
exit(0);
}
if (inode == 0) {
/* connection is in TIME_WAIT state. We rely on
* the old data still in the table. */
return;
}
//~ printf("inode:%lu\n", inode);
if (strlen(local_addr) > 8) {
/* this is an IPv6-style row */
/* Demangle what the kernel gives us */
sscanf(local_addr, "%08X%08X%08X%08X", &in6_local.s6_addr32[0],
&in6_local.s6_addr32[1], &in6_local.s6_addr32[2],
&in6_local.s6_addr32[3]);
sscanf(rem_addr, "%08X%08X%08X%08X", &in6_remote.s6_addr32[0],
&in6_remote.s6_addr32[1], &in6_remote.s6_addr32[2],
&in6_remote.s6_addr32[3]);
if ((in6_local.s6_addr32[0] == 0x0) && (in6_local.s6_addr32[1] == 0x0) &&
(in6_local.s6_addr32[2] == 0xFFFF0000)) {
/* IPv4-compatible address */
result_addr_local.s6_addr32[0] = in6_local.s6_addr32[3];
result_addr_remote.s6_addr32[0] = in6_remote.s6_addr32[3];
sa_family = AF_INET;
} else {
/* real IPv6 address */
// inet_ntop(AF_INET6, &in6_local, addr6, sizeof(addr6));
// INET6_getsock(addr6, (struct sockaddr *) &localaddr);
// inet_ntop(AF_INET6, &in6_remote, addr6, sizeof(addr6));
// INET6_getsock(addr6, (struct sockaddr *) &remaddr);
// localaddr.sin6_family = AF_INET6;
// remaddr.sin6_family = AF_INET6;
result_addr_local = in6_local;
result_addr_remote = in6_remote;
sa_family = AF_INET6;
}
} else {
/* this is an IPv4-style row */
sscanf(local_addr, "%X", (unsigned int *)&result_addr_local);
sscanf(rem_addr, "%X", (unsigned int *)&result_addr_remote);
sa_family = AF_INET;
}
char *hashkey = (char *)malloc(HASHKEYSIZE * sizeof(char));
char *hashkey2 = (char *)malloc(HASHKEYSIZE * sizeof(char));
char *local_string = (char *)malloc(50);
char *remote_string = (char *)malloc(50);
inet_ntop(sa_family, &result_addr_local, local_string, 49);
inet_ntop(sa_family, &result_addr_remote, remote_string, 49);
snprintf(hashkey, HASHKEYSIZE * sizeof(char), "%s:%d-%s:%d", local_string,
local_port, remote_string, rem_port);
snprintf(hashkey2, HASHKEYSIZE * sizeof(char), "%s:%d-%s:%d", remote_string,
rem_port,local_string, local_port);
free(local_string);
//~ printf("inode[%lu]:hashkey:[%s]\n",inode,hashkey);
inodelist_insert_if_not_with_hashkey(inode,hashkey,hashkey2,strlen(hashkey));
/* workaround: sometimes, when a connection is actually from 172.16.3.1 to
* 172.16.3.3, packages arrive from 195.169.216.157 to 172.16.3.3, where
* 172.16.3.1 and 195.169.216.157 are the local addresses of different
* interfaces */
//~ for (class local_addr *current_local_addr = local_addrs;
//~ current_local_addr != NULL;
//~ current_local_addr = current_local_addr->next) {
//~ /* TODO maybe only add the ones with the same sa_family */
//~ snprintf(hashkey, HASHKEYSIZE * sizeof(char), "%s:%d-%s:%d",
//~ current_local_addr->string, local_port, remote_string, rem_port);
//~ conninode[hashkey] = inode;
//~ }
free(hashkey);
free(remote_string);
}
int _addprocinfo(const char *filename) {
FILE *procinfo = fopen(filename, "r");
char buffer[8192];
if (procinfo == NULL)
return 0;
char *ret = fgets(buffer, sizeof(buffer), procinfo);
do {
if (fgets(buffer, sizeof(buffer), procinfo))
_addtoconninode(buffer);
} while (!feof(procinfo));
fclose(procinfo);
return 1;
}
unsigned long _str2ulong(const char *ptr) {
unsigned long retval = 0;
while ((*ptr >= '0') && (*ptr <= '9')) {
retval *= 10;
retval += *ptr - '0';
ptr++;
}
return retval;
}
int _str2int(const char *ptr) {
int retval = 0;
while ((*ptr >= '0') && (*ptr <= '9')) {
retval *= 10;
retval += *ptr - '0';
ptr++;
}
return retval;
}
char *_read_file(int fd, char *content) {
char buf[255];
int length = 0;
//~ for (length; (length = read(fd, buf, sizeof(buf))) > 0;) {
while( (length = read(fd, buf, sizeof(buf))) > 0) {
if (length < 0) {
fprintf(stderr, "Error reading file: %s\n", strerror(errno));
exit(34);
}
memcpy(content, buf, length);
}
return content;
}
char *_read_file_filename(const char *filepath, char *contents) {
int fd = open(filepath, O_RDONLY);
if (fd < 0) {
fprintf(stderr, "Error opening %s: %s\n", filepath,
strerror(errno));
exit(3);
return NULL;
}
_read_file(fd,contents);
if (close(fd)) {
fprintf(stderr, "Error opening %s: %s\n", filepath,
strerror(errno));
exit(34);
}
return contents;
}
char *_getcmdline(pid_t pid,char *cmdline) {
const int maxfilenamelen = 14 + MAX_PID_LENGTH + 1;
char filename[maxfilenamelen];
snprintf(filename, maxfilenamelen, "/proc/%d/cmdline", pid);
bool replace_null = false;
_read_file_filename(filename, cmdline);
//~ // join parameters, keep prgname separate, don't overwrite trailing null
size_t idx = 0;
for (idx = 0; idx < strlen(cmdline); idx++) {
if (cmdline[idx] == 0x00) {
if (replace_null) {
cmdline[idx] = ' ';
}
replace_null = true;
}
}
return cmdline;
}
void _setnode(unsigned long inode, pid_t pid) {
char cmdline[9000]="";
_getcmdline(pid,cmdline);
inodelist_t *tempNodeList=inodelist_get_inode_from_value(inode);
if( NULL != tempNodeList ) {
tempNodeList->data.pid = pid;
if( 0 == strlen(tempNodeList->data.cmdline)) {
strcpy(tempNodeList->data.cmdline, cmdline);
//PCAP Dumper
if( NULL == tempNodeList->data.mydumper ) {
char pcapname[1024];
sprintf(pcapname,"%s--%d--%s.pcap", st_file_prefix, pid, cmdline );
uint32_t i = 0;
for( i = 0; i < strlen(pcapname);i++ ) {
if( pcapname[i] == '/' )
pcapname[i]='_';
else if( pcapname[i] == ':' )
pcapname[i]='_';
else if( pcapname[i] == '@' )
pcapname[i]='_';
else if( pcapname[i] == ' ' )
pcapname[i]='_';
}
tempNodeList->data.mydumper = mypcap_open( pcapname,"ab+" );
}
}
}
}
void _get_info_by_linkname(const char *pid, const char *linkname) {
if (strncmp(linkname, "socket:[", 8) == 0) {
_setnode(_str2ulong(linkname + 8), _str2int(pid));
}
}
void _get_info_for_pid(const char *pid) {
char dirname[10 + MAX_PID_LENGTH];
size_t dirlen = 10 + strlen(pid);
snprintf(dirname, dirlen, "/proc/%s/fd", pid);
DIR *dir = opendir(dirname);
if (!dir) {
fprintf(stderr, "Couldn't open dir %s, removing entry\n", dirname);
int32_t failed_index = inodelist_get_index_from_pid(_str2int(pid));
if( -1 != failed_index ) {
inodelist_remove_at(failed_index);
}
return;
}
/* walk through /proc/%s/fd/... */
struct dirent *entry;
while ((entry = readdir(dir))) {
if (entry->d_type != DT_LNK)
continue;
size_t fromlen = dirlen + strlen(entry->d_name) + 1;
char fromname[10 + MAX_PID_LENGTH + 1 + MAX_FDLINK];
snprintf(fromname, fromlen, "%s/%s", dirname, entry->d_name);
int linklen = 80;
char linkname[linklen];
int usedlen = readlink(fromname, linkname, linklen - 1);
if (usedlen == -1) {
continue;
}
assert(usedlen < linklen);
linkname[usedlen] = '\0';
_get_info_by_linkname(pid, linkname);
}
closedir(dir);
}
void cnx_inode_refresh() {
uint32_t i = 0;
while(NULL != cst_cnx_file_details_list[i]) {
if (!_addprocinfo(cst_cnx_file_details_list[i])) {
fprintf(stderr, "Error: couldn't open %s", cst_cnx_file_details_list[i] );
}
i++;
}
}
void reread_mapping() {
DIR *proc = opendir("/proc");
if (proc == 0) {
fprintf(stderr, "Error reading /proc, needed to get inode-to-pid-maping\n");
exit(1);
}
struct dirent *entry;
while ((entry = readdir(proc))) {
if (entry->d_type != DT_DIR)
continue;
if (!is_number(entry->d_name))
continue;
//~ printf("/proc/%s\n", entry->d_name );
_get_info_for_pid(entry->d_name);
}
closedir(proc);
}