How to get media attribute from SDP body in pjsua2? - sip

Is there any way to get the media attribute, which is part of the SDP body, from a callback function or similar with the PJSUA2-library?
I'm looking for a way to get the remote media port and IP address that were sent in a SIP INVITE request.
I know how to register a callback for incoming calls, however, afaik, that callback does not include any information about the remote party.

Ofcourse there is a way to do this. For example in on_call_media_state callback:
include pjsua_internal header file: #include <pjsua-lib/pjsua_internal.h>
get pjsua_call structure pointer using call_id from callback's argument: pjsua_call *call = &pjsua_var.calls[call_id];
now you're able to read current local and remote SDP:
const pjmedia_sdp_session *remote_sdp;
const pjmedia_sdp_session *local_sdp;
pj_status_t status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
pj_status_t status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
for (int i = 0; i < remote_sdp->media_count; i++) {
// structure with access to SDP m values
pjmedia_sdp_media *m = remote_sdp->media[i];
}
I didn't run this code, but it should work.
Hope that will help you!

Excellent advice by DaszuOne!
Here is the complete working snippet copying SDP address:port for all media, comma separated if more than one (requires #include <pjmedia/sdp_neg.h> to get SDP negotiation state)
void MyCall::onCallState(OnCallStateParam &prm) {
CallInfo ci = getInfo();
pjsua_call *call = &pjsua_var.calls[ci.id];
if(call->inv->neg && pjmedia_sdp_neg_get_state(call->inv->neg) == PJMEDIA_SDP_NEG_STATE_DONE){
string remote_addr="";
const pjmedia_sdp_session *remote_sdp;
if(PJ_SUCCESS==pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp)){
for (int i = 0; i < remote_sdp->media_count; i++) {
if(i>0)
remote_addr.append(",");
pjmedia_sdp_media *m = remote_sdp->media[i];
remote_addr.append(string(m->conn->addr.ptr,m->conn->addr.slen));
remote_addr.append(":");
remote_addr.append(to_string(m->desc.port));
}
}else{
remote_addr="<none>";
}
callstate->othermedia = remote_addr;
string local_addr="";
const pjmedia_sdp_session *local_sdp;
if(PJ_SUCCESS==pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp)){
for (int i = 0; i < local_sdp->media_count; i++) {
if(i>0)
local_addr.append(",");
pjmedia_sdp_media *m = local_sdp->media[i];
local_addr.append(string(m->conn->addr.ptr,m->conn->addr.slen));
local_addr.append(":");
local_addr.append(to_string(m->desc.port));
}
}else{
local_addr="<none>";
}
callstate->mymedia = local_addr;
}
callstate->stateText = ci.stateText;
callstate->update();
};

Related

Server sends Welcome message more than once using select()

I'm currently having an issue with pop3 server which is based on select() function. Basically server holds multiple clients at once, but Welcome message sends as many times as is the number of connected client.
This is an example of messages sent to clients.
//file descriptor, array of clients
fd_set readset;
int sock_arr[30];
int max_fd, rc;
servsock = socket(AF_INET, SOCK_STREAM, 0);
/*...*/
max_fd = servsock;
do
{
FD_ZERO(&readset);
FD_SET(servsock, &readset);
for (int i = 0; i < 30; i++) {
rc = sock_arr[i];
if (rc > 0)
FD_SET(rc, &readset);
if (rc > max_fd)
max_fd = rc;
}
activity = select(max_fd + 1, &readset, NULL, NULL, &timeout);
if (activity < 0)
{
perror(" select() failed");
break;
}
if (activity == 0)
{
printf(" select() timed out. End program.\n");
break;
}
Message is sent as many times as is the number of connected client e.g.
if first client is connected the message is sent once
if second client is connected the message is sent twice etc.
//here server accepts new connections
if (FD_ISSET(servsock, &readset)) {
serv_socket_len = sizeof(addr);
peersoc = accept(servsock,(struct sockaddr *) &addr, &serv_socket_len);
if (peersoc < 0) {
error("Accept failed!\n", ERR_SCK);
}
else {
char message[256];
strcat(message, reply_code[1]);
strcat(message, reply_code[3]);
strcat(message, reply_code[0]);
//Welcome message
send(peersoc, message, strlen(message), 0);
for (int i = 0; i < 30; i++) {
if (sock_arr[i] == 0) {
sock_arr[i] = peersoc;
break;
}
}
}
}
//server processing input messages from clients using threads
/*...*/
I have no idea what causes I assume something with file descriptors. Please give me some advice if possible.
Solved I have forgotten to clear buffer for sending message
...
char message[256];
memset(message, 0, sizeof(message));
...

Retrieving source address in a recvmmsg UDP socket

I am receiving multiple messages through a socket using:
result = recvmmsg(socket_, &messages_[0], MAX_NUM_MSG, MSG_WAITFORONE, NULL);
And I want get the source address and port, but I am getting an error in the struct assignment when I try:
msg = &messages_[0];
***struct sockaddr *src = &msg->msg_hdr.msg_name;***
srcport = ntohs(src->sin_port);
srcaddr = ntohl(src->sin_addr.s_addr);
invalid conversion from ‘void**’ to ‘sockaddr*'
The recvmmsg system call is an extension of recvmsg. As described in recvmsg: The msg_name field points to a caller-allocated buffer that is used to return the source address
That means you should preallocate memory space for msg_name by yourself, and also you should specify msg_namelen, please try:
sockaddr_in addrs[MAX_NUM_MSG];
for (int i = 0; i < MAX_NUM_MSG; ++i) {
messages_[i].msg_hdr.msg_name = &addrs[i];
messages_[i].msg_hdr.msg_namelen = sizeof(sockaddr_in);
}
So that you can access address when you have at least one message by doing (Remember to use sockaddr_in but not sockaddr):
struct sockaddr_in *src = messages_[0].msg_hdr.msg_name;

How to add a line to a google spreadsheet via email?

I need a google apps script which can do the following:
if I send an email to my alias email, say to myemail+expenses#gmail.com, it adds a line in a specific google spreadsheet with data from that email. E.g. timestamp, subject, the first line of a body - whatever.
I found interesting article describing similar process of sending data to google spreadsheet.
May be there is no any direct way of doing that, however, it should exist some workaround.
P.S. It should be done only by using a google echosystem. No php, servers etc.
There are two step for this.
#1
At first create a trigger to run the mail checker.
function switchTrigger() {
var isExist = false;
var triggers = ScriptApp.getProjectTriggers();
for (var i = 0; i < triggers.length; i++) {
if ( /*<CONDITION>*/ ) {
isExist = true;
ScriptApp.deleteTrigger(triggers[i]);
}
}
if (!isExist) {
ScriptApp.newTrigger( /*<CONDITION>*/ ).create();
Logger.log('create');
}
}
#2
Then you have to check emails. Something like that
function checkMail() {
var sh = SpreadsheetApp.openById(properties.targetSheetId).getSheets()[0];
var query = /* properties.queryString */ ;
var threads = GmailApp.search(query);
if (threads.length < 1) return;
for (var i = 0; i < threads.length; i++) {
var messages = threads[i].getMessages();
for (var j = 0; j < messages.length; j++) {
if (messages[j].isStarred()) {
sh.appendRow([
new Date(),
messages[j].getFrom(),
messages[j].getPlainBody()
]);
messages[j].unstar();
}
}
}
}
Be careful. You have to set up Gmail filters so that all incoming to myemail+expenses#gmail.com would be marked with an star at the beginning.
Working Example

Implementing an OPC DA client from scratch

I would like to implement my own OPC DA client (versions 2.02, 2.05a, 3.00) from scratch but without using any third-party. Also I would like to make use of OPCEnum.exe service to get a list of installed OPC servers. Is there any kind of document that explains detailed and step by step the process to implement an OPC client?
I have a c# implementation but actually it's hard to fit it in here. I'll try to summarize the steps required.
Mostly you need to have OpcRcw.Comn.dll and OpcRcw.Da.dll from the OPC Core Components Redistributable package downloable for free from Opcfoundation.org. Once installed, the files are located in C:\Windows\assembly\GAC_MSIL. Create a reference in your project.
About coding, this is what you should do (there are three objects you want to implement, Server, Group and Item):
Let's start with server:
Type typeofOPCserver = Type.GetTypeFromProgID(serverName, computerName, true);
m_opcServer = (IOPCServer)Activator.CreateInstance(typeofOPCserver);
m_opcCommon = (IOPCCommon)m_opcServer;
IConnectionPointContainer icpc = (IConnectionPointContainer)m_opcServer;
Guid sinkGUID = typeof(IOPCShutdown).GUID;
icpc.FindConnectionPoint(ref sinkGUID, out m_OPCCP);
m_OPCCP.Advise(this, out m_cookie_CP);
I've striped a LOT of checking to fit it in here, take it as a sample...
Then you need a method on server to add groups:
// Parameter as following:
// [in] active, so do OnDataChange callback
// [in] Request this Update Rate from Server
// [in] Client Handle, not necessary in this sample
// [in] No time interval to system UTC time
// [in] No Deadband, so all data changes are reported
// [in] Server uses english language to for text values
// [out] Server handle to identify this group in later calls
// [out] The answer from Server to the requested Update Rate
// [in] requested interface type of the group object
// [out] pointer to the requested interface
m_opcServer.AddGroup(m_groupName, Convert.ToInt32(m_isActive), m_reqUpdateRate, m_clientHandle, pTimeBias, pDeadband, m_LocaleID, out m_serverHandle, out m_revUpdateRate, ref iid, out objGroup);
// Get our reference from the created group
m_OPCGroupStateMgt = (IOPCGroupStateMgt)objGroup;
Finally you need to create items:
m_OPCItem = (IOPCItemMgt)m_OPCGroupStateMgt;
m_OPCItem.AddItems(itemList.Length, GetAllItemDefs(itemList), out ppResults, out ppErrors);
Where itemlist is an array of OPCITEMDEF[]. I build the above using GetAllItemDefs from a structure of mine.
private static OPCITEMDEF[] GetAllItemDefs(params OpcItem[] opcItemList)
{
OPCITEMDEF[] opcItemDefs = new OPCITEMDEF[opcItemList.Length];
for (int i = 0; i < opcItemList.Length; i++)
{
OpcItem opcItem = opcItemList[i];
opcItemDefs[i].szAccessPath = "";
opcItemDefs[i].bActive = Convert.ToInt32(opcItem.IsActive);
opcItemDefs[i].vtRequestedDataType = Convert.ToInt16(opcItem.ItemType, CultureInfo.InvariantCulture);
opcItemDefs[i].dwBlobSize = 0;
opcItemDefs[i].pBlob = IntPtr.Zero;
opcItemDefs[i].hClient = opcItem.ClientHandle;
opcItemDefs[i].szItemID = opcItem.Id;
}
return opcItemDefs;
}
Finally, about enumerating Servers, I use this two functions:
/// <summary>
/// Enumerates hosts that may be accessed for server discovery.
/// </summary>
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
public string[] EnumerateHosts()
{
IntPtr pInfo;
int entriesRead = 0;
int totalEntries = 0;
int result = NetServerEnum(
IntPtr.Zero,
LEVEL_SERVER_INFO_100,
out pInfo,
MAX_PREFERRED_LENGTH,
out entriesRead,
out totalEntries,
SV_TYPE_WORKSTATION | SV_TYPE_SERVER,
IntPtr.Zero,
IntPtr.Zero);
if (result != 0)
throw new ApplicationException("NetApi Error = " + String.Format("0x{0,0:X}", result));
string[] computers = new string[entriesRead];
IntPtr pos = pInfo;
for (int ii = 0; ii < entriesRead; ii++)
{
SERVER_INFO_100 info = (SERVER_INFO_100)Marshal.PtrToStructure(pos, typeof(SERVER_INFO_100));
computers[ii] = info.sv100_name;
pos = (IntPtr)(pos.ToInt32() + Marshal.SizeOf(typeof(SERVER_INFO_100)));
}
NetApiBufferFree(pInfo);
return computers;
}
/// <summary>
/// Returns a list of servers that support the specified specification on the specified host.
/// </summary>
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
public string[] GetAvailableServers(Specification specification)
{
lock (this)
{
// connect to the server.
ArrayList servers = new ArrayList();
MULTI_QI[] results = new MULTI_QI[1];
GCHandle hIID = GCHandle.Alloc(IID_IUnknown, GCHandleType.Pinned);
results[0].iid = hIID.AddrOfPinnedObject();
results[0].pItf = null;
results[0].hr = 0;
try
{
// create an instance.
Guid srvid = CLSID;
CoCreateInstanceEx(srvid, null, CLSCTX.CLSCTX_LOCAL_SERVER, IntPtr.Zero, 1, results);
m_server = (IOPCServerList2)results[0].pItf;
// convert the interface version to a guid.
Guid catid = new Guid(specification.ID);
// get list of servers in the specified specification.
IOPCEnumGUID enumerator = null;
m_server.EnumClassesOfCategories(1, new Guid[] { catid }, 0, null, out enumerator);
// read clsids.
Guid[] clsids = ReadClasses(enumerator);
// release enumerator
if (enumerator != null && enumerator.GetType().IsCOMObject)
Marshal.ReleaseComObject(enumerator);
// fetch class descriptions.
foreach (Guid clsid in clsids)
{
try
{
string url = CreateUrl(specification, clsid);
servers.Add(url);
}
catch (Exception) { }
}
}
catch
{
}
finally
{
if (hIID.IsAllocated) hIID.Free();
if (m_server != null && m_server.GetType().IsCOMObject)
Marshal.ReleaseComObject(m_server);
}
return (string[])servers.ToArray(typeof(string));
}
}
I know I've striped a lot but maybe it can still help you ;)
Please mark the answer as correct if you think I've been clear ;)
Kind Regards,
D.

Parsing email "Received:" headers

We need to parse Received: email headers according to RFC 5321. We need to extract domains or IPs through which the mail has traversed. Also, we need to figure out if an IP is an internal IP.
Is there already a library which can help out, preferably in C\C++?
For example:
Received: from server.mymailhost.com (mail.mymailhost.com [126.43.75.123])
by pilot01.cl.msu.edu (8.10.2/8.10.2) with ESMTP id NAA23597;
Fri, 12 Jul 2002 16:11:20 -0400 (EDT)
We need to extract the "by" server.
The format used by 'Received' lines is defined in RFC 2821, and regex can't parse it.
(You can try anyway, and for a limited subset of headers produced by known software you might succeed, but when you attach this to the range of strange stuff found in real-world mail it will fail.)
Use an existing RFC 2821 parser and you should be OK, but otherwise you should expect failure, and write the software to cope with it. Don't base anything important like a security system around it.
We need to extract the "by" server.
'from' is more likely to be of use. The hostname given in a 'by' line is as seen by the host itself, so there is no guarantee it will be a publically resolvable FQDN. And of course you don't tend to get valid (TCP-Info) there.
There is a Perl Received module which is a fork of the SpamAssassin code. It returns a hash for a Received header with the relevant information. For example
{ ip => '64.12.136.4',
id => '875522',
by => 'xxx.com',
helo => 'imo-m01.mx.aol.com' }
vmime should be fine, moreless any mail library will allow you to do that.
You'll want to use Regular Expressions possibly
(?<=by).*(?=with)
This will give you pilot01.cl.msu.edu (8.10.2/8.10.2)
Edit:
I find it amusing that this was modded down when it actually gets what the OP asked for.
C#:
string header = "Received: from server.mymailhost.com (mail.mymailhost.com [126.43.75.123]) by pilot01.cl.msu.edu (8.10.2/8.10.2) with ESMTP id NAA23597; Fri, 12 Jul 2002 16:11:20 -0400 (EDT)";
System.Text.RegularExpressions.Regex r = new System.Text.RegularExpressions.Regex(#"(?<=by).*(?=with)");
System.Text.RegularExpressions.Match m = r.Match(header);
Console.WriteLine(m.Captures[0].Value);
Console.ReadKey();
I didnt claim that it was complete, but am wondering if the person that gave it a -1 even tried. Meh..
You can use regular expressions. It would look like this(not tested):
#include <regex.h>
regex_t *re = malloc(sizeof(regex_t));
const char *restr = "by ([A-Za-z.]+) \(([^\)]*)\)";
check(regcomp(re, restr, REG_EXTENDED | REG_ICASE), "regcomp");
size_t nmatch = 1;
regmatch_t *matches = malloc(sizeof(regmatch_t) * nmatch);
int ret = regexec(re, YOUR_STRING, nmatch, matches, 0);
check(ret != 0, "regexec");
int size;
size = matches[2].rm_eo - matches[2].rm_so;
char *host = malloc(sizeof(char) * size);
strncpy(host, YOUR_STRING + matches[2].rm_so, size );
host[size] = '\0';
size = matches[3].rm_eo - matches[3].rm_so;
char *ip = malloc(sizeof(char) * size);
strncpy(ip, YOUR_STRING + matches[3].rm_so, size );
ip[size] = '\0';
check is a macro to help you figure out if there are any problems:
#define check(condition, description) if (condition) { fprintf(stdout, "%s:%i - %s - %s\n", __FILE__, __LINE__, description, strerror(errno)); exit(1); }
typedef struct mailHeaders{
char name[100];
char value[2000];
}mailHeaders;
int header_count = 0;
mailHeaders headers[30]; // A struct to hold the name value pairs
char *GetMailHeader(char *name)
{
char *value = NULL;;
int i;
for(i=0;i<header_count;i++){
if(strcmp(name,headers[i].name) == 0){
value = headers[i].value;
break;
}
}
return(value);
}
void ReadMail(void)
{
//Loop through the email message line by line to separate the headers. Then save the name value pairs to a linked list or struct.
char *Received = NULL // Received header
char *mail = NULL; // Buffer that has the email message.
char *line = NULL; // A line of text in the email.
char *name = NULL; // Header name
char *value = NULL; // Header value
int index = -1; // Header index
memset(&headers,'\0',sizeof(mailHeaders));
line = strtok(mail,"\n");
while(line != NULL)
{
if(*line == '\t') // Tabbed headers
{
strcat(headers[index].value,line); // Concatenate the tabbed values
}
else
{
name = line;
value = strchr(line,':'); // Split the name value pairs.
if(value != NULL)
{
*value='\0'; // NULL the colon
value++; // Move the pointer past the NULL character to separate the name and value
index++;
strcpy(headers[index].name,name); // Copy the name to the data structure
strcpy(headers[index].value,value); // Copy the value to the data structure
}
}
if(*line == '\r') // End of headers
break;
line = strtok(NULL,"\n"); // Get next header
header_count = index;
}
Received = GetMailHeader("Received");
}
It is not difficult to parse such headers, even manually line-by-line. A regex could help there by looking at by\s+(\w)+\(. For C++, you could try that library or that one.
Have you considered using regular expressions?
Here is a list of internal, non-routable address ranges.