|
30 | 30 | import akka.event.Logging;
|
31 | 31 | import akka.event.LoggingAdapter;
|
32 | 32 | import akka.util.Timeout;
|
33 |
| - |
34 | 33 | import com.google.i18n.phonenumbers.NumberParseException;
|
35 | 34 | import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
36 | 35 | import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
|
37 |
| - |
38 | 36 | import gov.nist.javax.sip.header.UserAgent;
|
39 |
| - |
40 | 37 | import org.apache.commons.configuration.Configuration;
|
| 38 | +import org.apache.commons.configuration.HierarchicalConfiguration; |
41 | 39 | import org.joda.time.DateTime;
|
42 | 40 | import org.restcomm.connect.commons.configuration.RestcommConfiguration;
|
43 | 41 | import org.restcomm.connect.commons.dao.Sid;
|
44 | 42 | import org.restcomm.connect.commons.patterns.StopObserving;
|
45 | 43 | import org.restcomm.connect.commons.telephony.CreateCallType;
|
| 44 | +import org.restcomm.connect.commons.telephony.ProxyRule; |
46 | 45 | import org.restcomm.connect.commons.util.SdpUtils;
|
47 | 46 | import org.restcomm.connect.commons.util.UriUtils;
|
48 | 47 | import org.restcomm.connect.dao.AccountsDao;
|
|
89 | 88 | import org.restcomm.connect.telephony.api.UpdateCallScript;
|
90 | 89 | import org.restcomm.connect.telephony.api.util.B2BUAHelper;
|
91 | 90 | import org.restcomm.connect.telephony.api.util.CallControlHelper;
|
92 |
| - |
93 | 91 | import scala.concurrent.Await;
|
94 | 92 | import scala.concurrent.Future;
|
95 | 93 | import scala.concurrent.duration.Duration;
|
|
109 | 107 | import javax.servlet.sip.TelURL;
|
110 | 108 | import javax.sip.header.RouteHeader;
|
111 | 109 | import javax.sip.message.Response;
|
112 |
| - |
113 | 110 | import java.io.IOException;
|
114 | 111 | import java.net.InetAddress;
|
115 | 112 | import java.net.URI;
|
@@ -199,6 +196,10 @@ public final class CallManager extends UntypedActor {
|
199 | 196 | private String imsDomain;
|
200 | 197 | private String imsAccount;
|
201 | 198 |
|
| 199 | + private boolean actAsProxyOut; |
| 200 | + private List<ProxyRule> proxyOutRules; |
| 201 | + private boolean isActAsProxyOutUseFromHeader; |
| 202 | + |
202 | 203 | // used for sending warning and error logs to notification engine and to the console
|
203 | 204 | private void sendNotification(String errMessage, int errCode, String errType, boolean createNotification) {
|
204 | 205 | NotificationsDao notifications = storage.getNotificationsDao();
|
@@ -321,6 +322,32 @@ public CallManager(final Configuration configuration, final ServletContext conte
|
321 | 322 | && imsDomain != null && !imsDomain.isEmpty();
|
322 | 323 | }
|
323 | 324 | }
|
| 325 | + if (!runtime.subset("acting-as-proxy").isEmpty() && !runtime.subset("acting-as-proxy").subset("proxy-rules").isEmpty()) { |
| 326 | + final Configuration proxyConfiguration = runtime.subset("acting-as-proxy"); |
| 327 | + final Configuration proxyOutRulesConf = proxyConfiguration.subset("proxy-rules"); |
| 328 | + this.actAsProxyOut = proxyConfiguration.getBoolean("enabled", false); |
| 329 | + if (actAsProxyOut) { |
| 330 | + isActAsProxyOutUseFromHeader = proxyConfiguration.getBoolean("use-from-header", true); |
| 331 | + proxyOutRules = new ArrayList<ProxyRule>(); |
| 332 | + |
| 333 | + List<HierarchicalConfiguration> rulesList = ((HierarchicalConfiguration)proxyOutRulesConf).configurationsAt("rule"); |
| 334 | + for(HierarchicalConfiguration rule : rulesList){ |
| 335 | + String fromHost = rule.getString("from-uri"); |
| 336 | + String toHost = rule.getString("to-uri"); |
| 337 | + final String username = rule.getString("proxy-to-username"); |
| 338 | + final String password = rule.getString("proxy-to-password"); |
| 339 | + ProxyRule proxyRule = new ProxyRule(fromHost, toHost, username, password); |
| 340 | + proxyOutRules.add(proxyRule); |
| 341 | + } |
| 342 | + |
| 343 | + if (logger.isInfoEnabled()) { |
| 344 | + String msg = String.format("`ActAsProxy` feature is enabled with %d rules.",proxyOutRules.size()); |
| 345 | + logger.info(msg); |
| 346 | + } |
| 347 | + |
| 348 | + actAsProxyOut = actAsProxyOut && (proxyOutRules != null) && !proxyOutRules.isEmpty(); |
| 349 | + } |
| 350 | + } |
324 | 351 | firstTimeCleanup();
|
325 | 352 | }
|
326 | 353 |
|
@@ -534,11 +561,12 @@ private void invite(final Object message) throws IOException, NumberParseExcepti
|
534 | 561 | IExtensionCreateCallRequest er = new CreateCall(fromUser, toUser, "", "", false, 0, CreateCallType.PSTN, client.getAccountSid(), null,null, null, null);
|
535 | 562 | ec.executePreOutboundAction(er, this.extensions);
|
536 | 563 | if (er.isAllowed()) {
|
537 |
| - if (isWebRTC(request)) { |
| 564 | + if (actAsProxyOut) { |
| 565 | + processRequestAndProxyOut(request, client, toUser); |
| 566 | + } else if (isWebRTC(request)) { |
538 | 567 | //This is a WebRTC client that dials out
|
539 | 568 | //TODO: should we inject headers for this case?
|
540 |
| - proxyThroughMediaServer(request, client, toUser); |
541 |
| - |
| 569 | + proxyThroughMediaServerAsNumber(request, client, toUser); |
542 | 570 | } else {
|
543 | 571 | // https://telestax.atlassian.net/browse/RESTCOMM-335
|
544 | 572 | String proxyURI = activeProxy;
|
@@ -589,6 +617,10 @@ private void invite(final Object message) throws IOException, NumberParseExcepti
|
589 | 617 | // This is a call to a registered DID (application)
|
590 | 618 | return;
|
591 | 619 | }
|
| 620 | + if (actAsProxyOut) { |
| 621 | + processRequestAndProxyOut(request, client, toUser); |
| 622 | + return; |
| 623 | + } |
592 | 624 | }
|
593 | 625 | final SipServletResponse response = request.createResponse(SC_NOT_FOUND);
|
594 | 626 | response.send();
|
@@ -751,7 +783,80 @@ private boolean isWebRTC(final SipServletRequest request) {
|
751 | 783 | return false;
|
752 | 784 | }
|
753 | 785 |
|
754 |
| - private void proxyThroughMediaServer(final SipServletRequest request, final Client client, final String destNumber) { |
| 786 | + private void processRequestAndProxyOut (final SipServletRequest request, final Client client, final String destNumber) { |
| 787 | + String requestFromHost = null; |
| 788 | + |
| 789 | + ProxyRule matchedProxyRule = null; |
| 790 | + |
| 791 | + SipURI fromUri = null; |
| 792 | + try { |
| 793 | + if (isActAsProxyOutUseFromHeader) { |
| 794 | + fromUri = ((SipURI) request.getFrom().getURI()); |
| 795 | + } else { |
| 796 | + fromUri = ((SipURI) request.getAddressHeader("Contact").getURI()); |
| 797 | + } |
| 798 | + } catch (ServletParseException e) { |
| 799 | + logger.error("Problem while trying to process an `ActAsProxy` request, "+e); |
| 800 | + } |
| 801 | + requestFromHost = fromUri.getHost()+":"+fromUri.getPort(); |
| 802 | + |
| 803 | + for (ProxyRule proxyRule: proxyOutRules) { |
| 804 | + if (requestFromHost != null) { |
| 805 | + if (requestFromHost.equalsIgnoreCase(proxyRule.getFromUri())) { |
| 806 | + matchedProxyRule = proxyRule; |
| 807 | + break; |
| 808 | + } |
| 809 | + } |
| 810 | + } |
| 811 | + |
| 812 | + if (matchedProxyRule != null) { |
| 813 | + String sipUri = String.format("sip:%s@%s", destNumber, matchedProxyRule.getToUri()); |
| 814 | + String rcml; |
| 815 | + if (matchedProxyRule.getUsername() != null && !matchedProxyRule.getUsername().isEmpty() && matchedProxyRule.getPassword() != null && !matchedProxyRule.getPassword().isEmpty()) { |
| 816 | + rcml = String.format("<Response><Dial><Sip username=\"%s\" password=\"%s\">%s</Sip></Dial></Response>", matchedProxyRule.getUsername(), matchedProxyRule.getPassword(), sipUri); |
| 817 | + } else { |
| 818 | + rcml = String.format("<Response><Dial><Sip>%s</Sip></Dial></Response>", sipUri); |
| 819 | + } |
| 820 | + |
| 821 | + final VoiceInterpreterBuilder builder = new VoiceInterpreterBuilder(system); |
| 822 | + builder.setConfiguration(configuration); |
| 823 | + builder.setStorage(storage); |
| 824 | + builder.setCallManager(self()); |
| 825 | + builder.setConferenceManager(conferences); |
| 826 | + builder.setBridgeManager(bridges); |
| 827 | + builder.setSmsService(sms); |
| 828 | + |
| 829 | + Sid accountSid = null; |
| 830 | + String apiVersion = null; |
| 831 | + if (client != null) { |
| 832 | + accountSid = client.getAccountSid(); |
| 833 | + apiVersion = client.getApiVersion(); |
| 834 | + } else { |
| 835 | + //Todo get Administrators account from RestcommConfiguration |
| 836 | + accountSid = new Sid("ACae6e420f425248d6a26948c17a9e2acf"); |
| 837 | + apiVersion = RestcommConfiguration.getInstance().getMain().getApiVersion(); |
| 838 | + } |
| 839 | + |
| 840 | + builder.setAccount(accountSid); |
| 841 | + builder.setVersion(apiVersion); |
| 842 | + final Account account = storage.getAccountsDao().getAccount(accountSid); |
| 843 | + builder.setEmailAddress(account.getEmailAddress()); |
| 844 | + builder.setRcml(rcml); |
| 845 | + builder.setMonitoring(monitoring); |
| 846 | + final ActorRef interpreter = builder.build(); |
| 847 | + final ActorRef call = call(null); |
| 848 | + final SipApplicationSession application = request.getApplicationSession(); |
| 849 | + application.setAttribute(Call.class.getName(), call); |
| 850 | + call.tell(request, self()); |
| 851 | + interpreter.tell(new StartInterpreter(call), self()); |
| 852 | + } else { |
| 853 | + if (logger.isInfoEnabled()) { |
| 854 | + logger.info("No rule matched for the `ActAsProxy` feature"); |
| 855 | + } |
| 856 | + } |
| 857 | + } |
| 858 | + |
| 859 | + private void proxyThroughMediaServerAsNumber (final SipServletRequest request, final Client client, final String destNumber) { |
755 | 860 | String rcml = "<Response><Dial>"+destNumber+"</Dial></Response>";
|
756 | 861 | final VoiceInterpreterBuilder builder = new VoiceInterpreterBuilder(system);
|
757 | 862 | builder.setConfiguration(configuration);
|
|
0 commit comments