I am new to the world of compiled programming languages. I do most of my work with Perl and bash scripting. I was recently asked to pursue a listener to the Asterisk Manager Interface that will be able to pull out caller ID information based on the extension that the call is being sent to.

I have built a basic telnet listener that will login to the asterisk box on the managers interface and supply the credentials so that it will have full access to the AMI.

my issue that I am having trouble with finding a way to parse the output and grab what i want.

reading socket to a CR+LF would be ideal so I can parse each line separately and check for conditions to exist, then process the data that I need. I have only been working with C++ for about 2 weeks now with no formal training, so I am still pretty green. Any help would be appreciated.

below is a copy of my code, names addresses and ports have been changed to protect the innocent.

/*
** client.cpp -- Asterisk Manager Interface monitor client
*/

#include <unistd.h>
#include <errno.h>
#include <string>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <iostream>

using namespace std;

#define PORT "99999" // the port client will be connecting to 

#define MAXDATASIZE 512 // max number of bytes we can get at once 

// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
	if (sa->sa_family == AF_INET) {
		return &(((struct sockaddr_in*)sa)->sin_addr);
	}

	return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int main()
{
	int loop = 1;
	char *senddata;
	char *address = "foo.com";
	int sendlen;
	int bytes_sent;
	int sockfd, numbytes;  
	char buf[MAXDATASIZE];
	struct addrinfo hints, *servinfo, *p;
	int rv;
	char s[INET6_ADDRSTRLEN];

	memset(&hints, 0, sizeof hints);
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;

	if ((rv = getaddrinfo(address, PORT, &hints, &servinfo)) != 0) {
		fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
		return 1;
	}

	// loop through all the results and connect to the first we can
	for(p = servinfo; p != NULL; p = p->ai_next) {
		if ((sockfd = socket(p->ai_family, p->ai_socktype,
				p->ai_protocol)) == -1) {
			perror("client: socket");
			continue;
		}

		if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
			close(sockfd);
			perror("client: connect");
			continue;
		}

		break;
	}
	// exit if we cant connect
	if (p == NULL) {
		fprintf(stderr, "client: failed to connect\n");
		return 2;
	}

	inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr),
			s, sizeof s);
	cout<<"client: connecting to " << s <<"\n";

	freeaddrinfo(servinfo); // all done with this structure
	
	// get the initial login prompt from AMI
        if ((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1) {
		perror("recv");
		exit(1);
	}
	        buf[numbytes] = '\0';
	        cout<< buf << "\n";

	// send the login information and actions to the server using the hex codes for CR+LF because \r\n was not liked by asterisk
	// I should really break this out into a function, but will do that later, I really hate repeating code but it worked this way
	// so I didnt want to change it until I was closer to production.
	
	senddata = "Action: Login\x0D\x0A";
	sendlen = strlen(senddata);
	cout<< senddata;
	if ((bytes_sent = send(sockfd, senddata, sendlen, 0)) == -1) {
		perror("send");
		exit(1);
	}
	senddata = "ActionID: 1\x0D\x0A";
	sendlen = strlen(senddata);
	cout<< senddata;
	if ((bytes_sent = send(sockfd, senddata, sendlen, 0)) == -1) {
		perror("send");
		exit(1);
	}

	senddata = "Username: user\x0D\x0A";
	sendlen = strlen(senddata);
	cout<< senddata;
	if ((bytes_sent = send(sockfd, senddata, sendlen, 0)) == -1) {
		perror("send");
		exit(1);
	}
	// AMI requires you to send to CR+LF to signify that you are wanting to have the login processed
	senddata = "Secret: pass\x0D\x0A\x0D\x0A";
	sendlen = strlen(senddata);
	cout<< senddata;
	if ((bytes_sent = send(sockfd, senddata, sendlen, 0)) == -1) {
		perror("send");
		exit(1);
	}
	// Now i am going to read from the socket until I kill it, I really need to make it interactive so I can close the socket nicely but this
	// will have to do until I can figure out that part
	while ( loop == 1 )
	{
	if ((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1) {
	    perror("recv");
	    exit(1);
	}

	buf[numbytes] = '\0';

	cout<< buf << "\n";
	} // end while loop

	// after i am done with the socket there is no need to keep it open
	close(sockfd);
	// program exited cleanly
	return 0;

}// end main function
Comments
Excellent first post - code tags and all :)
Attachments
/*
** client.cpp -- Asterisk Manager Interface monitor client
*/

#include <unistd.h>
#include <errno.h>
#include <string>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <iostream>

using namespace std;

#define PORT "99999" // the port client will be connecting to 

#define MAXDATASIZE 512 // max number of bytes we can get at once 

// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
	if (sa->sa_family == AF_INET) {
		return &(((struct sockaddr_in*)sa)->sin_addr);
	}

	return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int main()
{
	int loop = 1;
	char *senddata;
	char *address = "foo.com";
	int sendlen;
	int bytes_sent;
	int sockfd, numbytes;  
	char buf[MAXDATASIZE];
	struct addrinfo hints, *servinfo, *p;
	int rv;
	char s[INET6_ADDRSTRLEN];

	memset(&hints, 0, sizeof hints);
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;

	if ((rv = getaddrinfo(address, PORT, &hints, &servinfo)) != 0) {
		fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
		return 1;
	}

	// loop through all the results and connect to the first we can
	for(p = servinfo; p != NULL; p = p->ai_next) {
		if ((sockfd = socket(p->ai_family, p->ai_socktype,
				p->ai_protocol)) == -1) {
			perror("client: socket");
			continue;
		}

		if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
			close(sockfd);
			perror("client: connect");
			continue;
		}

		break;
	}
	// exit if we cant connect
	if (p == NULL) {
		fprintf(stderr, "client: failed to connect\n");
		return 2;
	}

	inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr),
			s, sizeof s);
	cout<<"client: connecting to " << s <<"\n";

	freeaddrinfo(servinfo); // all done with this structure
	
	// get the initial login prompt from AMI
        if ((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1) {
		perror("recv");
		exit(1);
	}
	        buf[numbytes] = '\0';
	        cout<< buf << "\n";

	// send the login information and actions to the server using the hex codes for CR+LF because \r\n was not liked by asterisk
	// I should really break this out into a function, but will do that later, I really hate repeating code but it worked this way
	// so I didnt want to change it until I was closer to production.
	
	senddata = "Action: Login\x0D\x0A";
	sendlen = strlen(senddata);
	cout<< senddata;
	if ((bytes_sent = send(sockfd, senddata, sendlen, 0)) == -1) {
		perror("send");
		exit(1);
	}
	senddata = "ActionID: 1\x0D\x0A";
	sendlen = strlen(senddata);
	cout<< senddata;
	if ((bytes_sent = send(sockfd, senddata, sendlen, 0)) == -1) {
		perror("send");
		exit(1);
	}

	senddata = "Username: user\x0D\x0A";
	sendlen = strlen(senddata);
	cout<< senddata;
	if ((bytes_sent = send(sockfd, senddata, sendlen, 0)) == -1) {
		perror("send");
		exit(1);
	}
	// AMI requires you to send to CR+LF to signify that you are wanting to have the login processed
	senddata = "Secret: pass\x0D\x0A\x0D\x0A";
	sendlen = strlen(senddata);
	cout<< senddata;
	if ((bytes_sent = send(sockfd, senddata, sendlen, 0)) == -1) {
		perror("send");
		exit(1);
	}
	// Now i am going to read from the socket until I kill it, I really need to make it interactive so I can close the socket nicely but this
	// will have to do until I can figure out that part
	while ( loop == 1 )
	{
	if ((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1) {
	    perror("recv");
	    exit(1);
	}

	buf[numbytes] = '\0';

	cout<< buf << "\n";
	} // end while loop

	// after i am done with the socket there is no need to keep it open
	close(sockfd);
	// program exited cleanly
	return 0;

}// end main function

yes I say parse meaning, look through the output from the socket and match against a regex of sorts. I found a regex lib for c++ but i am having trouble with the reading from the socket and making sense out of it... I can get the data and display it with cout or what have you.

my main reason for doing this is because we have a need for a program that will run on windows, and will be porting it over using wxWidgets as a front end for it... perl on mac or linux is great but I wanted to make the application run natively on windows, and using c++ with some little changes to basic socket commands it will run on windows mac linux whatever.

thanks

> cout<< buf << "\n";
So each one of these outputs all the data, just not necessarily on a line by line basis?

Unfortunately, that's how it works.
You need to add another layer of code which takes raw message fragments, and turns them into lines ending in \n

This article has been dead for over six months. Start a new discussion instead.