Small Hack to Send SMDR to Syslog, for IP Office and other PBXs

I wanted to log all our calls and found out that there’s a feature in IP Office called SMDR that sends logs to a server.

There were a few programs that could receive these logs, but I couldn’t find one that just logged the lines to a syslog. After initally reading how to do it in Python and looking a the Perl code from SimpleSMDR, it seemed like too much code. This small C program, smdr-syslog, to does what I want.

The attached file contains a better version of the program, with an installer for Ubuntu Linux.

Most of the code is from the man pages and the Wikipedia BSD sockets article.

Other software to record SMDR:
Simple SMDR
Free IP Office SMDR Receiver
SMDR Receiver that writes to CSV files

[code language=”c”]
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#define LISTEN_PORT 3000
#define BUF_SIZE 1000

int port = LISTEN_PORT;
int log_level = LOG_INFO;
int log_facility = LOG_USER;

void listen_loop() {
int s,c,n;
struct sockaddr_in addr;
ssize_t nread;
char buf[BUF_SIZE];
struct protoent *entry = getprotobyname("tcp");
s = c = n = 0;

// printf("protoent %s %d\n", entry->p_name, entry->p_proto );
s = socket( AF_INET, SOCK_STREAM, entry->p_proto );
if (s==-1)
{
perror("Can’t open socket.\n");
exit(1);
}

memset( &addr, 0, sizeof(addr) );

addr.sin_family = AF_INET;
addr.sin_port = htons(3000);
addr.sin_addr.s_addr = htonl(INADDR_ANY);

if ( -1 == bind(s, (struct sockaddr *) &addr, sizeof(addr) ) ) {
perror("bind failed");
close(s);
exit(1);
}

if (-1 == listen( s, 10 )) {
perror("can’t listen");
close(s);
exit(1);
}

for(;;)
{
int cfd = accept( s, NULL, NULL );
if ( 0 > cfd )
{
perror("accept failed");
close(s);
exit(1);
}

nread = read( cfd, buf, BUF_SIZE-1 );
buf[nread-1] = ‘\0’;
syslog( log_level, "%s", buf );

if (-1 == shutdown( cfd, SHUT_RDWR ))
{
perror("");
close(cfd);
close(s);
exit(1);
}
close(cfd);
}
}

void main( int argc, char * argv[] ) {
openlog( "[smdr-syslog]", 0, log_facility);
syslog( log_level, "%s", "smdr-syslog starting" );
listen_loop();
closelog();
}
[/code]

Compile with:

gcc -o smdr-syslog -O2 smdr-syslog.c

I still need to write a startup script to make sure this program runs on boot. BTW, this code was tested on Linux, but it should work on BSD too.

IP Office misfeature

Caveat: I just discovered something bad. The IP Office 500’s SMDR feature buffers records, and seems to treat it like some kid of FIFO. When you make a call, it sends you one call record. That call record will be from the past. This is incredibly annoying when you want a real-time view of your phones. IP Office has an alternative feature where you can set the address to 0.0.0.0 and it’ll go into a server mode, where you can connect to the port specified, and get a dump of all the records. So, for this server, I’d recommend setting the buffer to 10 records, and just suffer a 10-call delay. If you need to retrieve history, you can first go to the history feature on the station, and then turn to the logs if you need more history.

You can get a dump with netcat:

nc ipoffice.local.address 3000

The description of the buffer seemed to say that it would buffer the unsent records, and then dump them to the receiver when it could. It doesn’t seem to work that way, at all.

If IP Office is to have complete logs, I think that you need a different kind of application. You need a cron job that polls the IP Office and dumps the logs to syslog.

References

https://community.spiceworks.com/topic/1657844-call-reporting-system-for-avaya-ip-office