/* * 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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,"",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; }