2007-07-15 10:03:09 +00:00
|
|
|
/*****************************************************************************\
|
|
|
|
** **
|
2007-07-18 09:44:25 +00:00
|
|
|
** Linux Call Router **
|
2007-07-15 10:03:09 +00:00
|
|
|
** **
|
|
|
|
**---------------------------------------------------------------------------**
|
|
|
|
** Copyright: Andreas Eversberg **
|
|
|
|
** **
|
|
|
|
** Asterisk socket client **
|
|
|
|
** **
|
|
|
|
\*****************************************************************************/
|
|
|
|
|
2007-07-26 12:23:56 +00:00
|
|
|
/*
|
|
|
|
|
|
|
|
How does it work:
|
|
|
|
|
|
|
|
To connect, open a socket and send a MESSAGE_HELLO to admin socket with
|
|
|
|
the application name. This name is unique an can be used for routing calls.
|
|
|
|
|
|
|
|
To make a call, send a MESSAGE_NEWREF and a new reference is received.
|
|
|
|
When receiving a call, a new reference is received.
|
|
|
|
The reference is received with MESSAGE_NEWREF.
|
|
|
|
|
|
|
|
Make a MESSAGE_SETUP or receive a MESSAGE_SETUP with the reference.
|
|
|
|
|
|
|
|
To release call and reference, send or receive MESSAGE_RELEASE.
|
|
|
|
From that point on, the ref is not valid, so no other message may be sent
|
|
|
|
with that reference.
|
2007-08-12 08:16:19 +00:00
|
|
|
|
2007-07-26 12:23:56 +00:00
|
|
|
*/
|
|
|
|
|
2007-07-15 10:03:09 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/un.h>
|
|
|
|
#include "macro.h"
|
|
|
|
#include "extension.h"
|
|
|
|
#include "message.h"
|
|
|
|
#include "admin.h"
|
2007-08-12 09:16:45 +00:00
|
|
|
#include "cause.h"
|
|
|
|
#include "asterisk_client.h"
|
2007-07-15 10:03:09 +00:00
|
|
|
|
|
|
|
int sock;
|
|
|
|
|
|
|
|
struct admin_list {
|
|
|
|
struct admin_list *next;
|
|
|
|
struct admin_msg msg;
|
|
|
|
} *admin_first = NULL;
|
|
|
|
|
|
|
|
/*
|
2007-08-12 09:16:45 +00:00
|
|
|
* channel and call instances
|
|
|
|
*/
|
|
|
|
struct chan_bchannel *bchannel_first;
|
|
|
|
struct chan_call *call_first;
|
|
|
|
|
|
|
|
struct chan_bchannel *find_bchannel_addr(unsigned long addr)
|
|
|
|
{
|
|
|
|
struct chan_bchannel *bchannel = bchannel_first;
|
|
|
|
|
|
|
|
while(bchannel)
|
|
|
|
{
|
|
|
|
if (bchannel->addr == addr)
|
|
|
|
break;
|
|
|
|
bchannel = bchannel->next;
|
|
|
|
}
|
|
|
|
return(bchannel);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct chan_bchannel *find_bchannel_ref(unsigned long ref)
|
|
|
|
{
|
|
|
|
struct chan_bchannel *bchannel = bchannel_first;
|
|
|
|
|
|
|
|
while(bchannel)
|
|
|
|
{
|
|
|
|
if (bchannel->ref == ref)
|
|
|
|
break;
|
|
|
|
bchannel = bchannel->next;
|
|
|
|
}
|
|
|
|
return(bchannel);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct chan_call *find_call_ref(unsigned long ref)
|
|
|
|
{
|
|
|
|
struct chan_call *call = call_first;
|
|
|
|
|
|
|
|
while(call)
|
|
|
|
{
|
|
|
|
if (call->ref == ref)
|
|
|
|
break;
|
|
|
|
call = call->next;
|
|
|
|
}
|
|
|
|
return(call);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct chan_call *find_call_addr(unsigned long addr)
|
|
|
|
{
|
|
|
|
struct chan_call *call = call_first;
|
|
|
|
|
|
|
|
while(call)
|
|
|
|
{
|
|
|
|
if (call->addr == addr)
|
|
|
|
break;
|
|
|
|
call = call->next;
|
|
|
|
}
|
|
|
|
return(call);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct chan_bchannel *alloc_bchannel(void)
|
|
|
|
{
|
|
|
|
struct chan_bchannel **bchannelp = &bchannel_first;
|
|
|
|
|
|
|
|
while(*bchannelp)
|
|
|
|
bchannelp = &((*bchannelp)->next);
|
|
|
|
|
|
|
|
*bchannelp = (struct chan_bchannel *)MALLOC(sizeof(struct chan_bchannel));
|
|
|
|
return(*bchannelp);
|
|
|
|
}
|
|
|
|
|
|
|
|
void free_bchannel(struct chan_bchannel *bchannel)
|
|
|
|
{
|
|
|
|
struct chan_bchannel **temp = &bchannel_first;
|
|
|
|
|
|
|
|
while(*temp)
|
|
|
|
{
|
|
|
|
if (*temp == bchannel)
|
|
|
|
{
|
|
|
|
*temp = (*temp)->next;
|
|
|
|
free(bchannel);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
temp = &((*temp)->next);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct chan_call *alloc_call(void)
|
|
|
|
{
|
|
|
|
struct chan_call **callp = &call_first;
|
|
|
|
|
|
|
|
while(*callp)
|
|
|
|
callp = &((*callp)->next);
|
|
|
|
|
|
|
|
*callp = (struct chan_call *)MALLOC(sizeof(struct chan_call));
|
|
|
|
return(*callp);
|
|
|
|
}
|
|
|
|
|
|
|
|
void free_call(struct chan_call *call)
|
|
|
|
{
|
|
|
|
struct chan_call **temp = &call_first;
|
|
|
|
|
|
|
|
while(*temp)
|
|
|
|
{
|
|
|
|
if (*temp == call)
|
|
|
|
{
|
|
|
|
*temp = (*temp)->next;
|
|
|
|
free(call);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
temp = &((*temp)->next);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* enque message to LCR
|
2007-07-15 10:03:09 +00:00
|
|
|
*/
|
2007-08-12 08:16:19 +00:00
|
|
|
int send_message(int message_type, unsigned long ref, union parameter *param)
|
2007-07-15 10:03:09 +00:00
|
|
|
{
|
|
|
|
struct admin_list *admin, **adminp;
|
|
|
|
|
|
|
|
adminp = &admin_first;
|
|
|
|
while(*adminp)
|
|
|
|
adminp = &((*adminp)->next);
|
|
|
|
admin = (struct admin_list *)MALLOC(sizeof(struct admin_list));
|
|
|
|
*adminp = admin;
|
|
|
|
|
|
|
|
admin->msg.type = message_type;
|
2007-08-12 08:16:19 +00:00
|
|
|
admin->msg.ref = ref;
|
2007-07-15 10:03:09 +00:00
|
|
|
memcpy(&admin->msg.param, param, sizeof(union parameter));
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
2007-08-12 08:16:19 +00:00
|
|
|
/*
|
|
|
|
* message received from LCR
|
|
|
|
*/
|
|
|
|
int receive_message(int message_type, unsigned long ref, union parameter *param)
|
|
|
|
{
|
|
|
|
union parameter newparam;
|
2007-08-12 09:16:45 +00:00
|
|
|
struct chan_bchannel *bchannel;
|
|
|
|
struct chan_call *call;
|
2007-08-12 08:16:19 +00:00
|
|
|
|
|
|
|
memset(&newparam, 0, sizeof(union parameter));
|
|
|
|
|
|
|
|
/* handle bchannel message*/
|
|
|
|
if (message_type == MESSAGE_BCHANNEL)
|
|
|
|
{
|
2007-08-12 09:16:45 +00:00
|
|
|
switch(param->bchannel.type)
|
2007-08-12 08:16:19 +00:00
|
|
|
{
|
|
|
|
case BCHANNEL_ASSIGN:
|
2007-08-12 09:16:45 +00:00
|
|
|
if (find_bchannel_addr(param->bchannel.addr))
|
2007-08-12 08:16:19 +00:00
|
|
|
{
|
|
|
|
fprintf(stderr, "error: bchannel addr %x already assigned.\n", param->bchannel.addr);
|
|
|
|
return(-1);
|
|
|
|
}
|
2007-08-12 09:16:45 +00:00
|
|
|
/* create bchannel */
|
|
|
|
bchannel = alloc_bchannel();
|
|
|
|
bchannel->addr = param->bchannel.addr;
|
|
|
|
/* in case, ref is not set, this bchannel instance must
|
2007-08-12 08:16:19 +00:00
|
|
|
* be created until it is removed again by LCR */
|
2007-08-12 09:16:45 +00:00
|
|
|
bchannel->ref = ref;
|
2007-08-12 08:16:19 +00:00
|
|
|
/* link to call */
|
2007-08-12 09:16:45 +00:00
|
|
|
if ((call = find_call_ref(ref)))
|
2007-08-12 08:16:19 +00:00
|
|
|
{
|
2007-08-12 09:16:45 +00:00
|
|
|
call->addr = param->bchannel.addr;
|
2007-08-12 08:16:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#warning open stack
|
|
|
|
/* acknowledge */
|
|
|
|
newparam.bchannel.type = BCHANNEL_ASSIGN_ACK;
|
|
|
|
newparam.bchannel.addr = param->bchannel.addr;
|
|
|
|
send_message(MESSAGE_BCHANNEL, 0, &newparam);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BCHANNEL_REMOVE:
|
2007-08-12 09:16:45 +00:00
|
|
|
if (!(bchannel = find_bchannel_addr(param->bchannel.addr)))
|
2007-08-12 08:16:19 +00:00
|
|
|
{
|
|
|
|
fprintf(stderr, "error: bchannel addr %x already assigned.\n", param->bchannel.addr);
|
|
|
|
return(-1);
|
|
|
|
}
|
|
|
|
/* unlink from call */
|
2007-08-12 09:16:45 +00:00
|
|
|
if ((call = find_call_ref(bchannel->ref)))
|
2007-08-12 08:16:19 +00:00
|
|
|
{
|
2007-08-12 09:16:45 +00:00
|
|
|
call->addr = 0;
|
2007-08-12 08:16:19 +00:00
|
|
|
}
|
2007-08-12 09:16:45 +00:00
|
|
|
/* remove bchannel */
|
|
|
|
free_bchannel(bchannel);
|
2007-08-12 08:16:19 +00:00
|
|
|
#warning close stack
|
|
|
|
/* acknowledge */
|
|
|
|
newparam.bchannel.type = BCHANNEL_REMOVE_ACK;
|
|
|
|
newparam.bchannel.addr = param->bchannel.addr;
|
|
|
|
send_message(MESSAGE_BCHANNEL, 0, &newparam);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2007-08-12 09:16:45 +00:00
|
|
|
fprintf(stderr, "received unknown bchannel message %d\n", param->bchannel.type);
|
|
|
|
}
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* handle new ref */
|
|
|
|
if (message_type == MESSAGE_NEWREF)
|
|
|
|
{
|
|
|
|
if (param->direction)
|
|
|
|
{
|
|
|
|
/* new ref from lcr */
|
|
|
|
if (!ref || find_call_ref(ref))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "illegal new ref %d received\n", ref);
|
|
|
|
return(-1);
|
|
|
|
}
|
|
|
|
call = alloc_call();
|
|
|
|
call->ref = ref;
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
/* new ref, as requested from this remote application */
|
|
|
|
call = find_call_ref(0);
|
|
|
|
if (!call)
|
|
|
|
{
|
|
|
|
/* send release, if ref does not exist */
|
|
|
|
newparam.disconnectinfo.cause = CAUSE_NORMAL;
|
|
|
|
newparam.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
|
|
|
|
send_message(MESSAGE_RELEASE, ref, &newparam);
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
call->ref = ref;
|
|
|
|
#warning process call (send setup, if pending)
|
2007-08-12 08:16:19 +00:00
|
|
|
}
|
|
|
|
return(0);
|
|
|
|
}
|
2007-08-12 09:16:45 +00:00
|
|
|
|
|
|
|
/* check ref */
|
|
|
|
if (!ref)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "received message %d without ref\n", message_type);
|
|
|
|
return(-1);
|
|
|
|
}
|
|
|
|
call = find_call_ref(ref);
|
|
|
|
if (!call)
|
|
|
|
{
|
|
|
|
/* ignore ref that is not used (anymore) */
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* handle messages */
|
2007-08-12 08:16:19 +00:00
|
|
|
switch(message_type)
|
2007-08-12 09:16:45 +00:00
|
|
|
{
|
|
|
|
#warning we must see if ref is a reply or a request, do we??
|
|
|
|
case MESSAGE_RELEASE:
|
|
|
|
#warning release call
|
|
|
|
free_call(call);
|
|
|
|
return(0);
|
|
|
|
|
|
|
|
case MESSAGE_SETUP:
|
|
|
|
#warning handle incoming setup, send to asterisk
|
|
|
|
break;
|
|
|
|
}
|
2007-08-12 08:16:19 +00:00
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
2007-07-15 10:03:09 +00:00
|
|
|
|
|
|
|
/* asterisk handler
|
|
|
|
* warning! not thread safe
|
|
|
|
* returns -1 for socket error, 0 for no work, 1 for work
|
|
|
|
*/
|
|
|
|
int handle_socket(void)
|
|
|
|
{
|
|
|
|
int work = 0;
|
|
|
|
int len;
|
|
|
|
struct admin_message msg;
|
|
|
|
struct admin_list *admin;
|
|
|
|
|
|
|
|
/* read from socket */
|
|
|
|
len = read(sock, &msg, sizeof(msg));
|
|
|
|
if (len == 0)
|
|
|
|
{
|
|
|
|
printf("Socket closed\n");
|
|
|
|
return(-1); // socket closed
|
|
|
|
}
|
|
|
|
if (len > 0)
|
|
|
|
{
|
|
|
|
if (len != sizeof(msg))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Socket short read (%d)\n", len);
|
|
|
|
return(-1); // socket error
|
|
|
|
}
|
|
|
|
if (msg.message != ADMIN_MESSAGE)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Socket received illegal message %d\n", msg.message);
|
|
|
|
return(-1); // socket error
|
|
|
|
}
|
2007-08-12 09:16:45 +00:00
|
|
|
receive_message(msg.u.msg.type, msg.u.msg.ref, &msg.u.msg.param);
|
2007-07-15 10:03:09 +00:00
|
|
|
printf("message received %d\n", msg.u.msg.type);
|
|
|
|
work = 1;
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
if (errno != EWOULDBLOCK)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Socket error %d\n", errno);
|
|
|
|
return(-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* write to socket */
|
|
|
|
if (!admin_first)
|
|
|
|
return(work);
|
|
|
|
admin = admin_first;
|
|
|
|
len = write(sock, &admin->msg, sizeof(msg));
|
|
|
|
if (len == 0)
|
|
|
|
{
|
|
|
|
printf("Socket closed\n");
|
|
|
|
return(-1); // socket closed
|
|
|
|
}
|
|
|
|
if (len > 0)
|
|
|
|
{
|
|
|
|
if (len != sizeof(msg))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Socket short write (%d)\n", len);
|
|
|
|
return(-1); // socket error
|
|
|
|
}
|
|
|
|
/* free head */
|
|
|
|
admin_first = admin->next;
|
|
|
|
FREE(admin, 0);
|
|
|
|
|
|
|
|
work = 1;
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
if (errno != EWOULDBLOCK)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Socket error %d\n", errno);
|
|
|
|
return(-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return(work);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* main function
|
|
|
|
*/
|
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
char *socket_name = SOCKET_NAME;
|
|
|
|
int conn;
|
|
|
|
struct sockaddr_un sock_address;
|
|
|
|
int ret;
|
|
|
|
unsigned long on = 1;
|
2007-07-26 12:23:56 +00:00
|
|
|
union parameter param;
|
2007-07-15 10:03:09 +00:00
|
|
|
|
|
|
|
/* open socket */
|
|
|
|
if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Failed to create socket.\n");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set socket address and name */
|
|
|
|
memset(&sock_address, 0, sizeof(sock_address));
|
|
|
|
sock_address.sun_family = PF_UNIX;
|
|
|
|
UCPY(sock_address.sun_path, socket_name);
|
|
|
|
|
|
|
|
/* connect socket */
|
|
|
|
if ((conn = connect(sock, (struct sockaddr *)&sock_address, SUN_LEN(&sock_address))) < 0)
|
|
|
|
{
|
|
|
|
close(sock);
|
|
|
|
fprintf(stderr, "Failed to connect to socket \"%s\".\nIs LCR running?\n", sock_address.sun_path);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set non-blocking io */
|
|
|
|
if (ioctl(sock, FIONBIO, (unsigned char *)(&on)) < 0)
|
|
|
|
{
|
|
|
|
close(sock);
|
|
|
|
fprintf(stderr, "Failed to set socket into non-blocking IO.\n");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* enque hello message */
|
2007-07-26 12:23:56 +00:00
|
|
|
memset(¶m, 0, sizeof(param));
|
|
|
|
SCPY(param.hello.application, "asterisk");
|
2007-08-12 09:16:45 +00:00
|
|
|
send_message(MESSAGE_HELLO, 0, ¶m);
|
2007-07-15 10:03:09 +00:00
|
|
|
|
|
|
|
while(42)
|
|
|
|
{
|
|
|
|
ret = handle_socket();
|
|
|
|
if (ret < 0)
|
|
|
|
break;
|
|
|
|
if (!ret)
|
|
|
|
usleep(30000);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* close socket */
|
|
|
|
close(sock);
|
|
|
|
/* now we say good bye */
|
|
|
|
if (ret)
|
|
|
|
{
|
|
|
|
printf("%s\n", ret);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|