mirror of https://github.com/sipwise/kamailio.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
322 lines
8.5 KiB
322 lines
8.5 KiB
/*
|
|
* Copyright (C) 2009 1&1 Internet AG
|
|
*
|
|
* This file is part of sip-router, a free SIP server.
|
|
*
|
|
* sip-router is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version
|
|
*
|
|
* sip-router is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include "pdb_server_backend.h"
|
|
#include "log.h"
|
|
|
|
|
|
#define NETBUFSIZE 200
|
|
#define DEFAULT_BINDADDR "0.0.0.0"
|
|
#define DEFAULT_PORT 5574
|
|
|
|
|
|
void print_usage(char *program)
|
|
{
|
|
set_log_level(LOG_INFO);
|
|
LINFO("version: pdb_server %d\n", PDB_VERSION);
|
|
LINFO("Listens on a UDP port for queries and sends answer UDP packets "
|
|
"back.\n");
|
|
LINFO("\n");
|
|
LINFO("Usage: %s [<option>...]\n", program);
|
|
LINFO(" %s -m <data file> [-i <bind addr>] [-p <port>] [-d <log level>]\n",
|
|
program);
|
|
LINFO("\n");
|
|
LINFO(" Options:\n");
|
|
LINFO(" -m <file>: Specifies the file containing the backend data.\n");
|
|
LINFO(" -i <bind addr>: Specifies the address to bind the UDP socket "
|
|
"to.\n");
|
|
LINFO(" Default is '%s'.\n", DEFAULT_BINDADDR);
|
|
LINFO(" -p <port>: Specifies the port to listen at in udp_server "
|
|
"mode.\n");
|
|
LINFO(" Default is %ld.\n", (long int)DEFAULT_PORT);
|
|
LINFO(" -d <debug level>: %ld for debug level.\n", LOG_DEBUG);
|
|
LINFO(" %ld for info level.\n", LOG_INFO);
|
|
LINFO(" %ld for notice level.\n", LOG_NOTICE);
|
|
LINFO(" %ld for warning level.\n", LOG_WARNING);
|
|
LINFO(" %ld for error level.\n", LOG_ERR);
|
|
LINFO(" %ld for critical level.\n", LOG_CRIT);
|
|
LINFO(" %ld for alert level.\n", LOG_ALERT);
|
|
LINFO(" %ld for emergency level.\n", LOG_EMERG);
|
|
LINFO(" %ld to disable all messages.\n",
|
|
LOG_EMERG - 1);
|
|
LINFO(" Default is warning level.\n");
|
|
LINFO(" -v: Print the version\n");
|
|
LINFO(" -h: Print this help.\n");
|
|
}
|
|
|
|
int pdb_msg_server_send(int so, char *buf, size_t answerlen,
|
|
struct sockaddr *fromaddr, socklen_t fromaddrlen)
|
|
{
|
|
ssize_t bytes_sent;
|
|
int try = 0;
|
|
again:
|
|
bytes_sent = sendto(so, buf, answerlen, 0, fromaddr, fromaddrlen);
|
|
if(bytes_sent < 3) {
|
|
if((errno == EINTR) && (try < 3)) {
|
|
try++;
|
|
LERR("sendto() failed - trying again. errno=%d (%s)\n", errno,
|
|
strerror(errno));
|
|
goto again;
|
|
}
|
|
LERR("sendto() failed with errno=%d (%s)\n", errno, strerror(errno));
|
|
if((errno == EAGAIN) || (errno == EINTR) || (errno == EWOULDBLOCK))
|
|
return 0;
|
|
return -1;
|
|
}
|
|
if(bytes_sent != answerlen) {
|
|
LERR("cannot send the whole answer (%ld/%ld).\n", (long int)bytes_sent,
|
|
(long int)answerlen);
|
|
return 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
Receive query request and send answer via UDP.
|
|
UDP Payload of request must contain the phone number (only digits allowed).
|
|
The Answer contains the number of the request followed by '\0' and the carrier id.
|
|
Loops until a receive or send error occurs and returns -1.
|
|
However, the following errors are ignored: EAGAIN, EINTR, EWOULDBLOCK.
|
|
*/
|
|
int udp_server(int so)
|
|
{
|
|
struct pdb_msg msg;
|
|
struct sockaddr fromaddr;
|
|
socklen_t fromaddrlen;
|
|
size_t answerlen = 0;
|
|
ssize_t bytes_received;
|
|
carrier_t carrierid;
|
|
char buf[sizeof(struct pdb_msg)];
|
|
int i;
|
|
|
|
for(;;) {
|
|
fromaddrlen = sizeof(fromaddr);
|
|
bytes_received = recvfrom(
|
|
so, buf, sizeof(struct pdb_msg), 0, &fromaddr, &fromaddrlen);
|
|
if(bytes_received < 0) {
|
|
LERR("recvfrom() failed with errno=%d (%s)\n", errno,
|
|
strerror(errno));
|
|
if((errno == EAGAIN) || (errno == EINTR) || (errno == EWOULDBLOCK))
|
|
continue;
|
|
return -1;
|
|
}
|
|
|
|
switch(buf[0]) {
|
|
case PDB_VERSION_1:
|
|
/* get received bytes */
|
|
memcpy(&msg, buf, bytes_received);
|
|
// pdb_msg_dbg(msg);
|
|
short int *_id =
|
|
(short int *)&(msg.hdr.id); /* make gcc happy */
|
|
msg.hdr.id = ntohs(*_id);
|
|
|
|
i = 0;
|
|
while(i < strlen(msg.bdy.payload)) {
|
|
if(msg.bdy.payload[i] < '0' || msg.bdy.payload[i] > '9') {
|
|
pdb_msg_format_send(&msg, PDB_VERSION_1,
|
|
PDB_TYPE_REPLY_ID, PDB_CODE_NOT_NUMBER,
|
|
htons(msg.hdr.id), NULL, 0);
|
|
goto msg_send;
|
|
}
|
|
i++;
|
|
}
|
|
/* lookup pdb_id */
|
|
carrierid = lookup_number(msg.bdy.payload);
|
|
|
|
/* check if not found pdb_id */
|
|
if(carrierid == -1) {
|
|
pdb_msg_format_send(&msg, PDB_VERSION_1, PDB_TYPE_REPLY_ID,
|
|
PDB_CODE_NOT_FOUND, htons(msg.hdr.id), NULL, 0);
|
|
goto msg_send;
|
|
}
|
|
|
|
/* convert to network byte order*/
|
|
carrierid = htons(carrierid);
|
|
|
|
/* prepare the message payload to be sent
|
|
* add the number string and append the carrier id
|
|
*/
|
|
memcpy(buf, msg.bdy.payload, msg.hdr.length - sizeof(msg.hdr));
|
|
memcpy(buf + msg.hdr.length - sizeof(msg.hdr), &carrierid,
|
|
sizeof(carrierid));
|
|
|
|
/* all ok, send pdb_msg with pdb_id in payload */
|
|
pdb_msg_format_send(&msg, PDB_VERSION_1, PDB_TYPE_REPLY_ID,
|
|
PDB_CODE_OK, htons(msg.hdr.id), buf,
|
|
msg.hdr.length - sizeof(msg.hdr) + sizeof(carrierid));
|
|
goto msg_send;
|
|
|
|
break;
|
|
|
|
/* old pdb version; no pdb_msg used */
|
|
default:
|
|
/* take only digits */
|
|
i = 0;
|
|
while((i < bytes_received) && (buf[i] >= '0')
|
|
&& (buf[i] <= '9'))
|
|
i++;
|
|
buf[i] = 0; /* terminate string */
|
|
i++;
|
|
|
|
/* lookup pdb_id */
|
|
carrierid = lookup_number(buf);
|
|
|
|
/* convert to network byte order*/
|
|
carrierid = htons(carrierid);
|
|
|
|
/* append carrier id to answer */
|
|
memcpy(&(buf[i]), &carrierid, sizeof(carrierid));
|
|
answerlen = i + sizeof(carrierid);
|
|
goto buf_send;
|
|
|
|
break;
|
|
}
|
|
|
|
msg_send:
|
|
// pdb_msg_dbg(msg);
|
|
if(pdb_msg_server_send(
|
|
so, (char *)&msg, msg.hdr.length, &fromaddr, fromaddrlen)
|
|
< 0) {
|
|
return -1;
|
|
}
|
|
continue;
|
|
|
|
buf_send:
|
|
if(pdb_msg_server_send(so, buf, answerlen, &fromaddr, fromaddrlen)) {
|
|
return -1;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int opt;
|
|
char *backend_data_filename = NULL;
|
|
char *bind_addr = DEFAULT_BINDADDR;
|
|
unsigned short bind_port = DEFAULT_PORT;
|
|
int use_syslog = 0;
|
|
int log_level = LOG_WARNING;
|
|
|
|
long int ret;
|
|
|
|
int so;
|
|
struct sockaddr_in sa;
|
|
|
|
while((opt = getopt(argc, argv, "m:i:p:vhdl:")) != -1) {
|
|
switch(opt) {
|
|
case 'm':
|
|
backend_data_filename = optarg;
|
|
break;
|
|
case 'i':
|
|
bind_addr = optarg;
|
|
break;
|
|
case 'p':
|
|
ret = strtol(optarg, NULL, 10);
|
|
if((ret < 0) || (ret > 65535)) {
|
|
init_log("pdb_server", use_syslog);
|
|
LERR("invalid port '%s' specified.\n", optarg);
|
|
return -1;
|
|
}
|
|
bind_port = ret;
|
|
break;
|
|
case 'v':
|
|
set_log_level(LOG_INFO);
|
|
LINFO("version: pdb_server %d\n", PDB_VERSION);
|
|
return 0;
|
|
break;
|
|
case 'h':
|
|
init_log("pdb_server", use_syslog);
|
|
print_usage(argv[0]);
|
|
return 0;
|
|
break;
|
|
case 'd':
|
|
use_syslog = 1;
|
|
break;
|
|
case 'l':
|
|
ret = strtol(optarg, NULL, 10);
|
|
if((ret < LOG_EMERG - 1) || (ret > LOG_DEBUG)) {
|
|
init_log("pdb_server", use_syslog);
|
|
LERR("invalid log level '%s' specified.\n", optarg);
|
|
return -1;
|
|
}
|
|
log_level = ret;
|
|
break;
|
|
default:
|
|
init_log("pdb_server", use_syslog);
|
|
LERR("invalid option '%c'.\n", opt);
|
|
print_usage(argv[0]);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
init_log("pdb_server", use_syslog);
|
|
set_log_level(log_level);
|
|
|
|
if(backend_data_filename == NULL) {
|
|
LERR("no data file specified.\n");
|
|
return 1;
|
|
}
|
|
|
|
if(init_backend(backend_data_filename) < 0) {
|
|
LERR("cannot initialize backend.\n");
|
|
return -1;
|
|
}
|
|
|
|
so = socket(AF_INET, SOCK_DGRAM, 0);
|
|
if(so < 0) {
|
|
LERR("socket() failed with errno=%d (%s)\n", errno, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
memset(&sa, 0, sizeof(sa));
|
|
sa.sin_family = AF_INET;
|
|
sa.sin_port = htons(bind_port);
|
|
if(inet_aton(bind_addr, &(sa.sin_addr)) == 0) {
|
|
LERR("invalid address '%s'.\n", bind_addr);
|
|
close(so);
|
|
return -1;
|
|
}
|
|
|
|
if(bind(so, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
|
|
LERR("bind() failed with errno=%d (%s)\n", errno, strerror(errno));
|
|
close(so);
|
|
return -1;
|
|
}
|
|
|
|
udp_server(so);
|
|
close(so);
|
|
|
|
return 0;
|
|
}
|