2 p2pproxy Copyright (C) 2007 Jehan Monnier ()
4 SipListener.java - sip proxy.
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 package org.linphone.p2pproxy.core.sipproxy;
25 import java.io.IOException;
26 import java.net.InetSocketAddress;
27 import java.util.Collection;
28 import java.util.HashMap;
30 import java.util.Timer;
31 import java.util.TimerTask;
32 import java.util.concurrent.Callable;
33 import java.util.concurrent.Executors;
34 import java.util.concurrent.ExecutorService;
35 import java.util.concurrent.Future;
36 import net.jxta.document.AdvertisementFactory;
37 import net.jxta.id.IDFactory;
38 import org.apache.log4j.Logger;
39 import org.apache.log4j.NDC;
40 import org.linphone.p2pproxy.api.P2pProxyException;
41 import org.linphone.p2pproxy.api.P2pProxyUserNotFoundException;
43 import org.linphone.p2pproxy.core.Configurator;
44 import org.linphone.p2pproxy.core.JxtaNetworkManager;
45 import org.linphone.p2pproxy.core.P2pProxyAccountManagementMBean;
46 import org.linphone.p2pproxy.core.media.rtprelay.MediaType;
47 import org.linphone.p2pproxy.core.sipproxy.superpeers.SuperPeerProxy;
48 import org.zoolu.sip.header.ExpiresHeader;
49 import org.zoolu.sip.header.MultipleHeader;
50 import org.zoolu.sip.message.Message;
51 import org.zoolu.sip.message.MessageFactory;
52 import org.zoolu.sip.provider.SipProvider;
53 import org.zoolu.sip.provider.SipProviderListener;
54 import org.zoolu.sip.provider.SipStack;
55 import org.zoolu.sip.transaction.TransactionServer;
56 import java.util.Collections;
58 public class SipProxyRegistrar implements SipProviderListener,SipProxyRegistrarMBean {
59 private final static Logger mLog = Logger.getLogger(SipProxyRegistrar.class);
60 public final static String REGISTRAR_PORT="org.linphone.p2pproxy.SipListener.registrar.port";
61 public final static String REGISTRAR_PUBLIC_ADDRESS="org.linphone.p2pproxy.SipListener.registrar.public.address";
62 public final static String ADV_NAME = "p2p-proxy-proxyregistrar";
63 private final int ADV_LIFE_TIME=6000000;
65 private final SipProvider mProvider;
66 private final JxtaNetworkManager mJxtaNetworkManager;
67 private final ExecutorService mPool;
69 private final Map<String,Registration> mRegistrationTab = Collections.synchronizedMap(new HashMap<String,Registration>());
70 private final Map<String,SipMessageTask> mCancalableTaskTab = Collections.synchronizedMap(new HashMap<String,SipMessageTask>());
72 private final P2pProxyAccountManagementMBean mP2pProxyAccountManagement;
73 private final Configurator mProperties;
74 private final SuperPeerProxy mSuperPeerProxy;
75 private NetworkResourceAdvertisement mProxyRegistrationAdvertisement;
76 private final Timer mTimer = new Timer ("Proxy registrar adv publisher");
78 //private long mNumberOfEstablishedCall;
79 private long mNumberOfRefusedRegistration;
80 private long mNumberOfSuccessfullRegistration;
81 private long mNumberOfUSerNotFound;
82 private long mNumberOfUnknownUSers;
83 private long mNumberOfUnknownUsersForRegistration;
84 private long mNumberOfUnRegistration;
87 public static class Registration {
88 long RegistrationDate;
89 public long Expiration;
90 //implementation specific context
91 public Object NetResources;
92 public Map<MediaType,InetSocketAddress> RtpRelays = new HashMap<MediaType,InetSocketAddress>() ;
93 public String Contact;
94 public final String From;
95 public Registration(String aFrom) {From = aFrom;}
96 public String toString() {
97 return "reg date ["+RegistrationDate+"] exp ["+Expiration+"] contact ["+Contact+"] from ["+From+"]";
101 class SipMessageTask implements Callable<Boolean> {
102 private final SipProvider mProvider;
103 private final Message mMessage;
104 private Future<?> mFuture;
107 * @return Returns the mMessage.
109 public Message getMessage() {
112 SipMessageTask(SipProvider aProvider, Message aMessage) {
113 mProvider = aProvider;
116 public Boolean call() throws Exception {
117 NDC.push(mMessage.getFirstLine() + mMessage.getCallIdHeader().getCallId() +":");
119 if (mMessage.isRequest()) {
120 if (mMessage.isRegister()) {
121 processRegister(mProvider, mMessage);
123 proxyRequest(mProvider, mMessage);
126 //1 remove via header
127 SipUtils.removeVia(mProvider,mMessage);
129 proxyResponse(mProvider, mMessage);
131 if (mMessage.isInvite() && mCancalableTaskTab.containsKey(mMessage.getCallIdHeader().getCallId()) ) {
132 mCancalableTaskTab.remove(mMessage.getCallIdHeader().getCallId());
134 } catch (InterruptedException eInter) {
135 mLog.info("request interrupted",eInter);
138 catch (Exception e) {
139 mLog.error("unexpected behavior",e);
140 if (mMessage.isRequest()) {
142 lResp = MessageFactory.createResponse(mMessage,500,e.getMessage(),null);
143 TransactionServer lTransactionServer = new TransactionServer(mProvider,mMessage,null);
144 lTransactionServer.respondWith(lResp);
145 synchronized (SipProxyRegistrar.this) {
146 if (mMessage.isInvite() && mCancalableTaskTab.containsKey(mMessage.getCallIdHeader().getCallId()) ) {
147 mCancalableTaskTab.remove(mMessage.getCallIdHeader().getCallId());
157 * @return Returns the mFuture.
159 public Future<?> getFuture() {
163 * @param future The mFuture to set.
165 public void setFuture(Future<?> future) {
171 public SipProxyRegistrar(Configurator lProperties,JxtaNetworkManager aJxtaNetworkManager,P2pProxyAccountManagementMBean aP2pProxyAccountManagement) throws IOException {
172 mJxtaNetworkManager = aJxtaNetworkManager;
173 mP2pProxyAccountManagement = aP2pProxyAccountManagement;
174 mProperties = lProperties;
175 File lFile = new File(SipStack.log_path);
176 if (lFile.exists() == false) lFile.mkdir();
177 String lViaAddress = lProperties.getProperty(REGISTRAR_PUBLIC_ADDRESS);
178 int lPort = Integer.parseInt(lProperties.getProperty(REGISTRAR_PORT, "5060"));
179 String[] lProto = {SipProvider.PROTO_UDP};
180 mProvider=new SipProvider(lViaAddress,lPort,lProto,SipProvider.ALL_INTERFACES);
181 mProvider.addSipProviderListener(SipProvider.PROMISQUE,this);
182 mPool = Executors.newCachedThreadPool();
183 mSuperPeerProxy = new SuperPeerProxy(aJxtaNetworkManager, "sip:"+mProvider.getViaAddress()+":"+mProvider.getPort(),mRegistrationTab);
184 TimerTask lPublisherTask = new TimerTask() {
189 SipProxyRegistrar.this.publishAdvertisement();
190 } catch (IOException e) {
191 mLog.error("cannot publish proxy registar adv", e);
197 mTimer.scheduleAtFixedRate(lPublisherTask, 0, ADV_LIFE_TIME-ADV_LIFE_TIME/10);
198 TimerTask lRegistrationTableGC = new TimerTask() {
203 Collection<Registration> lCurrentRegistrations = mRegistrationTab.values();
204 long lCurrentDate = System.currentTimeMillis();
205 for (Registration lRegistration : lCurrentRegistrations) {
206 if ((lCurrentDate - lRegistration.RegistrationDate - lRegistration.Expiration) > 0) {
207 if (mLog.isInfoEnabled()) mLog.info("registration entry ["+lRegistration+"] has expired");
208 mRegistrationTab.remove(lRegistration.From);
214 mTimer.scheduleAtFixedRate(lRegistrationTableGC, 0, 60000);
217 public void onReceivedMessage(SipProvider aProvider, Message aMessage) {
218 if (aProvider.getListeners().containsKey(aMessage.getTransactionId())) {
219 if (mLog.isDebugEnabled()) mLog.debug ("nothing to do, transaction already handled for ["+aMessage+"]");
222 if (mLog.isInfoEnabled()) mLog.info("receiving message ["+aMessage+"]");
223 String lCallId = aMessage.getCallIdHeader().getCallId();
224 SipMessageTask lPendingSipMessageTask = mCancalableTaskTab.get(lCallId);
226 if (aMessage.isCancel() && lPendingSipMessageTask != null ) {
227 // search for pending transaction
229 lPendingSipMessageTask.getFuture().cancel(true);
230 mCancalableTaskTab.remove(lCallId);
232 SipUtils.removeVia(mProvider,lPendingSipMessageTask.getMessage());
234 Message lCancelResp = MessageFactory.createResponse(aMessage,200,"ok",null);
235 TransactionServer lCancelTransactionServer = new TransactionServer(mProvider,aMessage,null);
236 lCancelTransactionServer.respondWith(lCancelResp);
239 Message lInviteResp = MessageFactory.createResponse(lPendingSipMessageTask.getMessage(),487,"Request Terminated",null);
240 TransactionServer lInviteTransactionServer = new TransactionServer(mProvider,lPendingSipMessageTask.getMessage(),null);
241 lInviteTransactionServer.respondWith(lInviteResp);
244 SipMessageTask lSipMessageTask = new SipMessageTask(aProvider,aMessage);
245 lSipMessageTask.setFuture(mPool.submit(lSipMessageTask));
246 if (aMessage.isInvite()) {
247 mCancalableTaskTab.put(aMessage.getCallIdHeader().getCallId(),lSipMessageTask);
253 //////////////////////////////////////////////////////////////////////
255 /////////////////////////////////////////////////////////////////////
256 private void proxyResponse(SipProvider aProvider, Message aMessage) throws NumberFormatException, InterruptedException, P2pProxyException, IOException {
257 mSuperPeerProxy.proxyResponse(aProvider, aMessage);
259 private void proxyRequest(SipProvider aProvider, Message aMessage) throws Exception {
260 if (aMessage.isAck() && aMessage.getToHeader().getTag() == null) {
261 // just terminate the Invite transaction
265 if (aMessage.isInvite() == true) {
267 TransactionServer lTransactionServer = new TransactionServer(aProvider,aMessage,null);
268 Message l100Trying = MessageFactory.createResponse(aMessage,100,"trying",null);
269 lTransactionServer.respondWith(l100Trying);
270 lTransactionServer.terminate();
273 MultipleHeader lMultipleRoute = aMessage.getRoutes();
274 if (lMultipleRoute != null) {
275 lMultipleRoute.removeTop();
276 aMessage.setRoutes(lMultipleRoute);
279 SipUtils.addVia(aProvider,aMessage);
281 mSuperPeerProxy.proxyRequest(aProvider, aMessage);
282 }catch (P2pProxyUserNotFoundException e) {
284 SipUtils.removeVia(aProvider, aMessage);
285 if (aMessage.isInvite()) {
286 Message lresp = MessageFactory.createResponse(aMessage,404,e.getMessage(),null);
287 TransactionServer lTransactionServer = new TransactionServer(aProvider,aMessage,null);
288 lTransactionServer.respondWith(lresp);
289 lTransactionServer.terminate();
297 //////////////////////////////////////////////////////////////////////
298 ////Registrar methods
299 /////////////////////////////////////////////////////////////////////
301 private void processRegister(SipProvider aProvider, Message aMessage) throws IOException, P2pProxyException {
302 TransactionServer lTransactionServer = new TransactionServer(aProvider,aMessage,null);
303 Message l100Trying = MessageFactory.createResponse(aMessage,100,"trying",null);
304 lTransactionServer.respondWith(l100Trying);
305 Registration lRegistration=null;
307 //check if already registered
309 String lFromName = aMessage.getFromHeader().getNameAddress().getAddress().toString();
310 if ((lRegistration = mRegistrationTab.get(lFromName)) != null) {
312 updateRegistration(lRegistration,aMessage);
314 if (aMessage.getExpiresHeader().getDeltaSeconds() == 0) {
315 mRegistrationTab.remove(lFromName);
320 // test if account already created
322 if (mP2pProxyAccountManagement.isValidAccount(lFromName)) {
323 lRegistration = new Registration(lFromName);
324 // forgot contact, use rport lRegistration.Contact = aMessage.getContactHeader().getNameAddress().getAddress().toString();;
326 updateRegistration(lRegistration,aMessage);
327 mRegistrationTab.put(lFromName, lRegistration);
329 // create negative answers
330 mLog.info("account for user ["+lFromName+"] not created yet");
331 Message lresp = MessageFactory.createResponse(aMessage,404,"Not found",null);
332 lTransactionServer.respondWith(lresp);
336 // ok, create answers
337 Message lresp = MessageFactory.createResponse(aMessage,200,"Ok",null);
338 lresp.addContactHeader(aMessage.getContactHeader(), false);
339 ExpiresHeader lExpireHeader = new ExpiresHeader((int) (lRegistration.Expiration/1000));
340 lresp.addHeader(lExpireHeader, false);
341 lTransactionServer.respondWith(lresp);
344 private void updateRegistration(Registration aRegistration, Message aRegistrationMessage) throws P2pProxyException {
345 aRegistration.RegistrationDate = System.currentTimeMillis();
346 // default registration period
347 aRegistration.Expiration = 3600000;
348 if (aRegistrationMessage.getExpiresHeader() != null ) {
349 aRegistration.Expiration = aRegistrationMessage.getExpiresHeader().getDeltaSeconds()*1000;
351 aRegistration.Contact = "sip:"+aRegistrationMessage.getRemoteAddress()+":"+aRegistrationMessage.getRemotePort();
352 mSuperPeerProxy.updateRegistration(aRegistration, aRegistrationMessage);
358 mJxtaNetworkManager.getPeerGroup().getDiscoveryService().flushAdvertisement(mProxyRegistrationAdvertisement);
359 } catch (IOException e) {
360 mLog.warn("cannot flush registrar adv",e );
365 // public long getNumberOfEstablishedCall() {
366 // return mNumberOfEstablishedCall;
368 public long getNumberOfRefusedRegistration() {
369 return mNumberOfRefusedRegistration;
371 public long getNumberOfSuccessfullRegistration() {
372 return mNumberOfSuccessfullRegistration;
374 public long getNumberOfUSerNotFound() {
375 return mNumberOfUSerNotFound;
377 public long getNumberOfUnknownUSers() {
378 return mNumberOfUnknownUSers;
380 public long getNumberOfUnknownUsersForRegistration() {
381 return mNumberOfUnknownUsersForRegistration;
383 public String[] getRegisteredList() {
384 String[] lRegisteredList = new String[mRegistrationTab.size()] ;
386 for (String lRegistrationKey : mRegistrationTab.keySet()) {
387 lRegisteredList[i++] = lRegistrationKey;
389 return lRegisteredList;
391 public long getNumberOfUnRegistration() {
392 return mNumberOfUnRegistration;
394 private void publishAdvertisement() throws IOException {
395 if (mProxyRegistrationAdvertisement == null) {
396 mProxyRegistrationAdvertisement = (NetworkResourceAdvertisement) AdvertisementFactory.newAdvertisement(NetworkResourceAdvertisement.getAdvertisementType());
397 mProxyRegistrationAdvertisement.setID(IDFactory.newCodatID(mJxtaNetworkManager.getPeerGroup().getPeerGroupID(), Integer.toHexString(mSuperPeerProxy.getSipProxyRegistrarAddress().hashCode()).getBytes("US-ASCII")));
398 mProxyRegistrationAdvertisement.setAddress(mSuperPeerProxy.getSipProxyRegistrarAddress());
399 mProxyRegistrationAdvertisement.setName(ADV_NAME);
401 mJxtaNetworkManager.getPeerGroup().getDiscoveryService().publish(mProxyRegistrationAdvertisement,ADV_LIFE_TIME,ADV_LIFE_TIME/2);
402 mLog.info(mProxyRegistrationAdvertisement + "published");