udtrace/utils.c

187 lines
4.2 KiB
C

#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "utils.h"
#define MAX_UNIX_FDS 32
#ifdef ENABLE_TITAN
extern void mncc_dissector(int fd, bool is_out, const char *fn, const uint8_t *data, unsigned int len);
extern void pcu_dissector(int fd, bool is_out, const char *fn, const uint8_t *data, unsigned int len);
#endif
/***********************************************************************
* Utility functions
***********************************************************************/
/* taken from libosmocore */
static char hexd_buff[4096];
static const char hex_chars[] = "0123456789abcdef";
static char *hexdump(const unsigned char *buf, int len, char *delim)
{
int i;
char *cur = hexd_buff;
hexd_buff[0] = 0;
for (i = 0; i < len; i++) {
const char *delimp = delim;
int len_remain = sizeof(hexd_buff) - (cur - hexd_buff);
if (len_remain < 3)
break;
*cur++ = hex_chars[buf[i] >> 4];
*cur++ = hex_chars[buf[i] & 0xf];
while (len_remain > 1 && *delimp) {
*cur++ = *delimp++;
len_remain--;
}
*cur = 0;
}
hexd_buff[sizeof(hexd_buff)-1] = 0;
return hexd_buff;
}
static void default_dissector(int fd, bool is_out, const char *fn, const uint8_t *data, unsigned int len)
{
fprintf(stderr, "%d %s %c %s\n", fd, fn, is_out ? 'W' : 'R', hexdump(data, len, ""));
}
static struct sock_state unix_fds[MAX_UNIX_FDS];
__attribute__ ((constructor)) static void udtrace_init(void)
{
int i;
LOG("Unix Domain Socket Trace initialized (TITAN support "
#ifdef ENABLE_TITAN
"enabled"
#else
"DISABLED"
#endif
")\n");
for (i = 0; i < ARRAY_SIZE(unix_fds); i++) {
unix_fds[i] = (struct sock_state) { -1, NULL, NULL };
}
}
/* add a file descriptor from the list of to-be-traced ones */
void udtrace_add_fd(int fd)
{
struct sock_state *ss;
/* Find an unused state in unix_fds */
ss = udtrace_sstate_by_fd(-1);
if (!ss) {
LOG("Couldn't add UNIX FD %d (no space in unix_fds)\n", fd);
return;
}
LOG("Adding FD %d\n", fd);
ss->fd = fd;
}
/* add a file descriptor from the list of to-be-traced ones */
void udtrace_add_fd_child(int pfd, int cfd)
{
struct sock_state *pss, *css;
/* Find the parent socket state first */
pss = udtrace_sstate_by_fd(pfd);
if (!pss) {
LOG("Couldn't find parent UNIX FD %d for %d\n", pfd, cfd);
return;
}
/* Find an unused state in unix_fds */
css = udtrace_sstate_by_fd(-1);
if (!css) {
LOG("Couldn't add UNIX FD %d (no space in unix_fds)\n", cfd);
return;
}
LOG("Adding FD %d as a child of %d\n", cfd, pfd);
css->dissector = pss->dissector;
css->path = strdup(pss->path);
css->fd = cfd;
}
/* delete a file descriptor from the list of to-be-traced ones */
void udtrace_del_fd(int fd)
{
struct sock_state *ss;
/* Find the corresponding state in unix_fds */
ss = udtrace_sstate_by_fd(fd);
if (!ss) {
LOG("Couldn't delete UNIX FD %d (no such FD in unix_fds)\n", fd);
return;
}
LOG("Removing FD %d\n", fd);
free((void *) ss->path);
*ss = (struct sock_state) { -1, NULL, NULL };
}
static void udtrace_resolve_dissector(struct sock_state *ss)
{
/* actual useful dissectors resovled by path */
if (0) { }
#ifdef ENABLE_TITAN
else if (strstr(ss->path, "mncc"))
ss->dissector = mncc_dissector;
else if (strstr(ss->path, "pcu"))
ss->dissector = pcu_dissector;
#endif
else
ss->dissector = &default_dissector;
}
/* set the path of a given fd */
void udtrace_fd_set_path(int fd, const char *path)
{
struct sock_state *ss;
/* Find the corresponding state in unix_fds */
ss = udtrace_sstate_by_fd(fd);
if (!ss) {
LOG("Couldn't set path for UNIX FD %d (no such FD in unix_fds)\n", fd);
return;
}
ss->path = strdup(path);
udtrace_resolve_dissector(ss);
}
struct sock_state *udtrace_sstate_by_fd(int fd)
{
int i;
for (i = 0; i < ARRAY_SIZE(unix_fds); i++) {
if (unix_fds[i].fd == fd)
return &unix_fds[i];
}
return NULL;
}
/* is the given file descriptor part of the to-be-traced unix domain fd's? */
bool is_unix_socket(int fd)
{
if (udtrace_sstate_by_fd(fd))
return true;
else
return false;
}
void udtrace_data(int fd, bool is_out, const char *fn, const uint8_t *data, unsigned int len)
{
struct sock_state *ss = udtrace_sstate_by_fd(fd);
if (!data || !len || !ss)
return;
ss->dissector(fd, is_out, fn, data, len);
}