subscriberRegistry/apps/sipauthserve.cpp

385 lines
11 KiB
C++

/*
* Copyright 2011 Kestrel Signal Processing, Inc.
* Copyright 2011, 2014 Range Networks, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <arpa/inet.h>
#include <cstdlib>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <fstream>
#include <iostream>
#include <netinet/in.h>
#include <osip2/osip.h>
#include <osipparser2/osip_message.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <Logger.h>
#include <Globals.h>
#include <NodeManager.h>
#include <JSONDB.h>
#include "servershare.h"
#include "SubscriberRegistry.h"
using namespace std;
ConfigurationTable gConfig("/etc/OpenBTS/sipauthserve.db", "sipauthserve", getConfigurationKeys());
Log dummy("sipauthserve", gConfig.getStr("Log.Level").c_str(), LOG_LOCAL7);
int my_udp_port;
// just using this for the database access
SubscriberRegistry gSubscriberRegistry;
/** The remote node manager. */
NodeManager gNodeManager;
/** The JSON<->DB interface. */
JSONDB gJSONDB;
/** Application specific NodeManager logic for handling requests. */
JsonBox::Object nmHandler(JsonBox::Object& request)
{
JsonBox::Object response;
std::string command = request["command"].getString();
std::string action = request["action"].getString();
if (command.compare("subscribers") == 0) {
request["table"] = JsonBox::Value("subscribers");
response = gJSONDB.query(request);
} else {
response["code"] = JsonBox::Value(501);
}
return response;
}
void prettyPrint(const char *label, osip_message_t *sip)
{
char *dest=NULL;
size_t length=0;
int i = osip_message_to_str(sip, &dest, &length);
if (i!=0) {
LOG(ERR) << "cannot get printable message";
} else {
LOG(INFO) << label << ":\n" << dest;
osip_free(dest);
}
}
string imsiFromSip(osip_message_t *sip)
{
char *dest;
osip_uri_t *fromUri = osip_from_get_url(sip->from);
if (!fromUri) {
LOG(ERR) << "osip_from_get_url problem";
return "";
}
osip_uri_to_str(fromUri, &dest);
string imsi = dest;
osip_free(dest);
return imsi;
}
string imsiToSip(osip_message_t *sip)
{
char *dest;
osip_uri_t *toUri = osip_to_get_url(sip->to);
if (!toUri) {
LOG(ERR) << "osip_to_get_url problem";
return "";
}
osip_uri_to_str(toUri, &dest);
string imsi = dest;
osip_free(dest);
return imsi;
}
// is imsi in the database?
bool imsiFound(string imsi)
{
string x = gSubscriberRegistry.imsiGet(imsi, "id");
return x.length() != 0;
}
string imsiClean(string imsi)
{
// remove leading sip:
if (0 == strncasecmp(imsi.c_str(), "sip:", 4)) {
imsi = imsi.substr(4);
}
// remove trailing @...
size_t p = imsi.find("@");
if (p != string::npos) {
imsi = imsi.substr(0, p);
}
// remove leading IMSI
if (0 == strncasecmp(imsi.c_str(), "imsi", 4)) {
imsi = imsi.substr(4);
}
return imsi;
}
char *processBuffer(char *buffer)
{
int i;
// parse sip message
osip_message_t *sip;
i=osip_message_init(&sip);
if (i!=0) {
LOG(ERR) << "cannot allocate";
osip_message_free(sip);
return NULL;
}
i=osip_message_parse(sip, buffer, strlen(buffer));
if (i!=0) {
LOG(ERR) << "cannot parse sip message";
osip_message_free(sip);
return NULL;
}
prettyPrint("request", sip);
// response starts as clone of message
osip_message_t *response;
osip_message_clone(sip, &response);
osip_from_t * contact_header = (osip_from_t*)osip_list_get(&sip->contacts,0);
osip_uri_t* contact_url = contact_header->url;
char *remote_host = contact_url->host;
char *remote_port = contact_url->port;
// return via
ostringstream newvia;
// newvia << "SIP/2.0/UDP localhost:5063;branch=1;received=string_address@foo.bar";
const char *my_ipaddress = "localhost";
newvia << "SIP/2.0/UDP " << my_ipaddress << ":" << my_udp_port << ";branch=1;received="
<< "string_address@foo.bar"; // << my_network.string_addr((struct sockaddr *)netaddr, netaddrlen, false);
osip_message_append_via(response, newvia.str().c_str());
// no method
osip_message_set_method(response, NULL);
string imsi = imsiClean(imsiFromSip(sip));
string imsiTo = imsiClean(imsiToSip(sip));
if ((imsi == "EXIT") && (imsiTo == "EXIT")) exit(0); // for testing only
if (!imsiFound(imsi)) {
LOG(NOTICE) << "imsi unknown";
// imsi problem => 404 IMSI Not Found
osip_message_set_status_code (response, 404);
osip_message_set_reason_phrase (response, osip_strdup("IMSI Not Found"));
} else if (gConfig.defines("SubscriberRegistry.IgnoreAuthentication")) {
osip_message_set_status_code (response, 200);
osip_message_set_reason_phrase (response, osip_strdup("OK"));
LOG(INFO) << "success, registering for IP address " << remote_host;
gSubscriberRegistry.imsiSet(imsi,"ipaddr", remote_host, "port", remote_port);
} else {
// look for rand and sres in Authorization header (assume imsi same as in from)
string randx;
string sres;
// sip parser is not working reliably for Authorization, so we'll do the parsing
char *RAND = strcasestr(buffer, "nonce=");
char *SRES = strcasestr(buffer, "response=");
if (RAND && SRES) {
// find RAND digits
RAND += 6;
while (!isalnum(*RAND)) { RAND++; }
RAND[32] = 0;
int j=0;
// FIXME -- These loops should use strspn instead.
while(isalnum(RAND[j])) { j++; }
RAND[j] = '\0';
// find SRES digits
SRES += 9;
while (!isalnum(*SRES)) { SRES++; }
int i=0;
// FIXME -- These loops should use strspn instead.
while(isalnum(SRES[i])) { i++; }
SRES[i] = '\0';
LOG(INFO) << "rand = /" << RAND << "/";
LOG(INFO) << "sres = /" << SRES << "/";
}
if (!RAND || !SRES) {
LOG(NOTICE) << "imsi known, 1st register";
// no rand and sres => 401 Unauthorized
osip_message_set_status_code (response, 401);
osip_message_set_reason_phrase (response, osip_strdup("Unauthorized"));
// but include rand in www_authenticate
osip_www_authenticate_t *auth;
osip_www_authenticate_init(&auth);
// auth type is required by osip_www_authenticate_to_str (and therefore osip_message_to_str)
string auth_type = "Digest";
osip_www_authenticate_set_auth_type(auth, osip_strdup(auth_type.c_str()));
// returning RAND in www_authenticate header
string randz = generateRand(imsi);
osip_www_authenticate_set_nonce(auth, osip_strdup(randz.c_str()));
i = osip_list_add (&response->www_authenticates, auth, -1);
if (i < 0) LOG(ERR) << "problem adding www_authenticate";
} else {
string kc;
bool sres_good = authenticate(imsi, RAND, SRES, &kc);
LOG(INFO) << "imsi known, 2nd register, good = " << sres_good;
if (sres_good) {
// sres matches rand => 200 OK
osip_message_set_status_code (response, 200);
osip_message_set_reason_phrase (response, osip_strdup("OK"));
if (kc.size() != 0) {
osip_authentication_info *auth;
osip_authentication_info_init(&auth);
osip_authentication_info_set_cnonce(auth, osip_strdup(kc.c_str()));
i = osip_list_add (&response->authentication_infos, auth, -1);
if (i < 0) LOG(ERR) << "problem adding authentication_infos";
}
// (pat 9-2013) Add the caller id.
static string calleridstr("callerid");
string callid = gSubscriberRegistry.imsiGet(imsi,calleridstr);
if (callid.size()) {
char buf[120];
// Per RFC3966 the telephone numbers should begin with "+" only if it is globally unique throughout the world.
// We should not add the "+" here, it should be in the database if appropriate.
snprintf(buf,120,"<tel:%s>",callid.c_str());
osip_message_set_header(response,"P-Associated-URI",buf);
}
// And register it.
LOG(INFO) << "success, registering for IP address " << remote_host;
gSubscriberRegistry.imsiSet(imsi,"ipaddr", remote_host, "port", remote_port);
} else {
// sres does not match rand => 401 Unauthorized
osip_message_set_status_code (response, 401);
osip_message_set_reason_phrase (response, osip_strdup("Unauthorized"));
}
}
}
prettyPrint("response", response);
size_t length = 0;
char *dest;
int ii = osip_message_to_str(response, &dest, &length);
if (ii != 0) {
LOG(ERR) << "cannot get printable message";
}
osip_message_free(sip);
osip_message_free(response);
return dest;
}
#define BUFLEN 5000
int
main(int argc, char **argv)
{
// TODO: Properly parse and handle any arguments
if (argc > 1) {
for (int argi = 0; argi < argc; argi++) {
if (!strcmp(argv[argi], "--version") ||
!strcmp(argv[argi], "-v")) {
cout << gVersionString << endl;
}
if (!strcmp(argv[argi], "--gensql")) {
cout << gConfig.getDefaultSQL(string(argv[0]), gVersionString) << endl;
}
if (!strcmp(argv[argi], "--gentex")) {
cout << gConfig.getTeX(string(argv[0]), gVersionString) << endl;
}
}
return 0;
}
sockaddr_in si_me;
sockaddr_in si_other;
int aSocket;
char buf[BUFLEN];
LOG(ALERT) << argv[0] << " (re)starting";
srand ( time(NULL) + (int)getpid() );
my_udp_port = gConfig.getNum("SubscriberRegistry.Port");
gSubscriberRegistry.init();
gNodeManager.setAppLogicHandler(&nmHandler);
gNodeManager.start(45064);
// init osip lib
osip_t *osip;
int i=osip_init(&osip);
if (i!=0) {
LOG(ALERT) << "cannot init sip lib";
exit(1);
}
if ((aSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
LOG(ALERT) << "can't initialize socket";
exit(1);
}
memset((char *) &si_me, 0, sizeof(si_me));
si_me.sin_family = AF_INET;
si_me.sin_port = htons(my_udp_port);
si_me.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(aSocket, (sockaddr*)&si_me, sizeof(si_me)) == -1) {
LOG(ALERT) << "can't bind socket on port " << my_udp_port;
exit(1);
}
LOG(NOTICE) << "binding on port " << my_udp_port;
while (true) {
gConfig.purge();
socklen_t slen = sizeof(si_other);
memset(buf, 0, BUFLEN);
if (recvfrom(aSocket, buf, BUFLEN, 0, (sockaddr*)&si_other, &slen) == -1) {
LOG(ERR) << "recvfrom problem";
continue;
}
LOG(INFO) << " receiving " << buf;
char *dest = processBuffer(buf);
if (dest == NULL) {
continue;
}
if (sendto(aSocket, dest, strlen(dest), 0, (sockaddr*)&si_other, sizeof(si_other)) == -1) {
LOG(ERR) << "sendto problem";
continue;
}
osip_free(dest);
}
close(aSocket);
return 0;
}