Internet Draft A.Kristensen draft-kristensen-sip-servlet-00.txt A. Byttner Expires: March 2000 Hewlett-Packard September 1999 The SIP Servlet API Status of this Memo This document is an Internet-Draft and is in full conformance with all provisions of Section 10 of RFC2026. Internet-Drafts are working documents of the Internet Engineering Task Force (IETF), its areas, and its working groups. Note that other groups may also distribute working documents as Internet-Drafts. Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress." The list of current Internet-Drafts can be accessed at http://www.ietf.org/ietf/1id-abstracts.txt The list of Internet-Draft Shadow Directories can be accessed at http://www.ietf.org/shadow.html. Abstract This document proposes a Java extension API for SIP servers. It allows SIP server functionality to be extended by associating incoming requests and responses with SIP servlets - Java programs which control the processing of SIP messages. The API is similar in spirit to the servlet API used with Web servers. Basing a SIP server extension mechanism on the notion of Java servlets has a number of advantages, e.g. the ability to remain stateful, low overhead, a typed API, a number of built-in security mechanisms, as well as convenient access to a wide range of APIs, e.g. directory services, databases, and the Java Media Framework. Table of Contents 1 Introduction................................................1 2 What are SIP Servlets.......................................3 2.1 Servlet Lifecycle.........................................3 2.1.1 Loading the Code........................................4 2.1.2 Instantiating and Initializing the Servlet..............4 2.1.3 Invoking the Servlet....................................4 2.1.4 Destroying the Servlet..................................5 2.2 Servlet Mappings..........................................5 3 Comparison to Related Technologies..........................6 3.1 The HTTP Servlet API......................................6 Internet Draft The SIP Servlet API September 1999 3.2 The Common Gateway Interface for SIP......................6 4 Overview of the SIP Servlet API.............................7 4.1 Interfaces................................................8 4.2 Classes...................................................9 4.3 Exceptions................................................9 5 Overview of SIP Servlet API Usage...........................9 5.1 Registering for Callbacks.................................9 5.2 Message Processing.......................................10 5.3 System Headers...........................................10 5.4 Creating Messages........................................11 5.5 The default action.......................................11 5.6 Request Tokens...........................................12 5.7 Access to Message Content................................12 5.8 The Contact Database.....................................13 5.9 Optional Behaviour.......................................13 6 API Reference..............................................13 6.1 Interfaces...............................................13 6.1.1 ContactDatabase........................................13 6.1.2 Contact................................................14 6.1.3 SingleThreadModel......................................14 6.1.4 SipAddress.............................................14 6.1.5 SipConstants...........................................15 6.1.6 SipFactory.............................................16 6.1.7 SipMessage.............................................16 6.1.8 SipRequest.............................................17 6.1.9 SipResponse............................................17 6.1.10 SipServlet.............................................18 6.1.11 SipServletContext......................................18 6.1.12 SipTransaction.........................................18 6.1.13 SipURL.................................................19 6.2 Classes..................................................19 6.2.1 SipUtils...............................................19 6.2.2 SipServletAdapter......................................20 6.3 Exceptions...............................................20 6.3.1 ParseException.........................................20 6.3.2 ServletException.......................................21 7 Examples...................................................21 7.1 RejectServlet............................................21 7.2 LogCallDuration..........................................21 8 Issues.....................................................23 9 Security Considerations....................................23 9.1 Sensitive Operations.....................................24 9.2 Buggy or Malicious Servlet Code..........................24 9.3 Protocol Violations......................................24 9.4 Denial of Service Attacks................................25 10 Acknowledgements...........................................25 11 Full Copyright Statement...................................25 1 Introduction One important ingredient of the success of the Web has been the fact that servers are (relatively) easily programmable. This means that Web servers can host a variety of applications which can be updated and managed independently of Web browsers. Client software A. Kristensen, A. Byttner [Page 2] Internet Draft The SIP Servlet API September 1999 need only to know how to access services using HTTP and how to render HTML and doesn't usually participate directly in executing service logic. In the PSTN, services such as personal mobility, call forwarding, call screening, etc., are also introduced via programmability of network servers. For a number of reasons, cultural differences being one, this has traditionally been done in a closed, proprietary manner, which translates directly into slower, more expensive, less creative, but also more stable and secure services. This document proposes a Java API for use with SIP servers and user agents. It defines the abstractions necessary for allowing a SIP protocol stack to defer some of its decision making regarding how to handle SIP requests and responses to SIP "servlets", Java classes which implement the SipServlet interface. SIP servlets can inspect and set message headers and bodies, and they can proxy and respond to requests as well as initiate their own requests and forward responses upstream. They can reside on top of a SIP protocol stack in proxies, registrars, redirect servers, and also user agents. The API defined in this document is SIP specific and is primarily intended as an extension mechanism for SIP servers, not as a generic API for SIP stacks. Furthermore, we do not assume that servlet code is as trusted as the server itself. This implies that security is a big issue and that whereever possible the API should not expose internal structures unnecessarily, e.g. actual sockets, timeouts and retransmission schemes, etc. Design rationales and open issues are discussed in indented paragraphs like this. 2 What are SIP Servlets A SIP servlet is a chunk of Java code that interacts with a SIP server, or more generally, a "servlet engine", to control or influence call processing in some way. The benefit of standardizing the servlet API is, of course, that servlets can be packaged as small applications and be deployed on any conformant SIP server or user agent. The primary path of communication between a servlet and a server is by the server passing objects representing SIP messages to the servlet. The servlet has access to all parts of the SIP message via those objects: headers and the body as well as the request or status line. The servlet decides what to do in response to the message: requests can be answered or proxied, possibly to multiple destinations. Responses can be created by the servlet and incoming responses can be forwarded or handled directly by the servlet. Additionally, servlets can initiate new SIP transactions of their own. 2.1 Servlet Lifecycle A. Kristensen, A. Byttner [Page 3] Internet Draft The SIP Servlet API September 1999 The lifecycle of a servlet is as follows: the code is loaded into the server; the servlet class is instantiated and initiated; the servlet is repeatedly invoked to handle incoming messages; the servlet is destroyed. Servlets can implement services that are shared by many users or services that are associated with just a single user. The API doesn't distinguish between these two extremes, but comforming servlet engines MAY make such a distinction in order to free resources related to single-user servlets as soon as possible. Alternatively, implementations may choose to destroy servlets after they have been unused for a fixed amount of time. A servlet instance can handle messages belonging to many transactions over the course of its lifetime, or just a single transaction. All messages. It should not be assumed that because two transactions are handled by the same servlet class they're handled by the same actual servlet instance. Transactions can be correlated by storing state as static data of the servlet class or in a database. Servlets handling subsequent transactions (e.g. a BYE request) can then easily look up associated state. Messages within the same transaction can share information by setting attributes on the transaction object. 2.1.1 Loading the Code First the servlet code is loaded into the servlet engine's Java Virtual Machine. The code may be loaded from a local file system, a remote Web server or something entirely different. The server is free to trust servlets to varying degrees depending on the source of the code, see section 9.1. The servlet code would typically be loaded only once, although some servlet engines may support a class reloading mechanism for ease of development. 2.1.2 Instantiating and Initializing the Servlet The servlet engine instantiates the servlet class using the default (empty) constructor, casts the resulting object to a SipServlet and passes the servlet its configuration: public void init(ServletConfig config) The ServletConfig provides access to the servlets view of the servlet engine, a ServletContext object. Different servlets may have different views of the servlet engine, i.e. different ServletContexts. 2.1.3 Invoking the Servlet A. Kristensen, A. Byttner [Page 4] Internet Draft The SIP Servlet API September 1999 The servlet is then repeatedly invoked to handle SIP messages. The method invoked depends on whether the message is a request or a response: public boolean gotRequest(SipRequest req) public boolean gotResponse(SipResponse res) The response determines whether the servlet engines default call processing is applied to the message or not. The default implementation of these methods in the abstract class SipServletAdapter returns false causing the server to perform the default action. 2.1.4 Destroying the Servlet When a server decides to terminate a servlet, e.g. because the server is being shut down or a new version of the servlet is being loaded, the servlets destroy method is invoked: public void destroy() and the server lets go of any reference it holds to it, thus allowing the servlet instance to get garbage collected. A servlet may (subject to security constraints) start its own threads making it possible for it to play an active part in a call beyond the setup phase. An IVR application, for example, might accept a call and then synthesize or play back an audio stream. 2.2 Servlet Mappings The exact mechanism used for triggering servlets from SIP messages is outside the scope of this document, but could depend on any aspect of a message, e.g. the request URI, headers (e.g. To and From) or the message body. One typical scenario might be as follows. User specific service logic is stored at the users "home-server" which would often be its registrar. The SIP specification defines the "address-of-record" as the SIP address under which the registry knows the registrand, typically of the form "user@domain" rather than "user@host" [RFC 2543, section 4.2.6]. For each address-of-record the home-server would associate a set of rules. Each rule consists of pattern-action pairs. The pattern is a predicate over the domain of SIP messages (a server might constrain this definition in some implementation specific way), while the action specifies which servlet to run and with what arguments and privileges. When receiving a SIP message the server would retrieve the rules it has configured for the address-of-record specified in the request- URI, find the first matching predicate and invoke the corresponding servlet, instantiating it first if necessary. A. Kristensen, A. Byttner [Page 5] Internet Draft The SIP Servlet API September 1999 This is just an example - many alternatives and variations can be imagined. 3 Comparison to Related Technologies Two technologies in particular are related to the SIP Servlet API. The servlet API defined for dynamic creation of content in Web servers [Servlet API] (henceforth referred to as the HTTP Servlet API) and the Common Gateway Interface for SIP [SIP CGI]. 3.1 The HTTP Servlet API The SIP Servlet API defined in this document has a lot in common with the HTTP Servlet API defined for Web server extensibility [Servlet API]. That API is structured into a generic and an HTTP-specific part. The generic part defines the general relationship between servlets and servlet engines and is intended to be applicable for a variety of request-response protocols. It would have been nice if the SIP Servlet API could have been defined in terms of this generic servlet API alongside the HTTP API, but this is impractical due to fundamental differences between the two, the most important being that whereas the HTTP Servlet API is designed to be supported by "origin" Web servers only, the SIP Servlet API needs to be supported by proxies as well. This implies that servlets has to be able to: o proxy requests, possibly to multiple destinations (i.e. there is not a one-to-one relationship between request and response objects) o receive responses as well as requests o forward responses o read and write parts of both requests and responses Additionally, access to request bodies in the HTTP Servlet API uses a stream metaphor. This is not necessarily appropriate for SIP messages as the engine needs to cache the whole body anyway to compute the Content-Length - SIP doesn't have HTTPs "chunked" encoding, nor is there much need for it as message bodies are generally fairly small. Due to these differences in model we believe it would not be worthwhile basing the SIP servlet API on the generic servlet API. 3.2 The Common Gateway Interface for SIP SIP CGI is an extension framework for SIP servers based on the philosophy of CGI as defined for Web servers. The relationship of the SIP Servlet API to SIP CGI is much like that of the HTTP Servlet API to HTTP CGI, i.e. it has the same set of advantages and disadvantages, in particular the processing overhead is relatively low as the servlet API doesn't require an external process to be spawned for each message. A. Kristensen, A. Byttner [Page 6] Internet Draft The SIP Servlet API September 1999 The fact that SIP transactions are more complicated than HTTP transactions and the fact that the SIP extension mechanism must be applicable to proxies does mean, however, that the limitations of the CGI approach compared to defining an API becomes more pronounced. For example: o An API can provide more convenient access to various structures (e.g. SIP URLs and Contact addresses) by representing them as abstractions rather than untyped strings. o The typing makes the API more safe. SIP CGI scripts communicate with servers using strings meaning that the risk of badly formatted messages is higher. This and the previous point can be ameliorated by the use of appropriate higher level libraries within CGI scripts, though. o Servlets can be given (controlled) access to the contact database maintained by a proxy or registrar. This is more difficult within a CGI model as the server generally has to know a priori what information to make available to scripts. o The lifetime of SIP servlets can go beyond the handling of individual messages and hence they can more easily keep state. o Likewise, handling of timers is less of an issue. As servlets persist for the entire duration of the transaction they relate to, they can manage their own timeouts, or rely on other APIs, e.g. javax.swing.Timer. Additionally, servlets have ready access a wide variety of APIs, e.g. directories, databases, CORBA, the Java Media Framework, etc. and they can reuse the Java security infrastructure, e.g. the sandbox model for safely executing untrusted code. One disadvantage of the API approach is that it is language dependent, although in fact the API can be supported by non-Java servers. The HTTP Servlet API, for example, is supported in some form or another on all major Web servers. It is worth noting that despite the differences between API and CGI based extension mechanisms, many of the issues involved in designing either one of these are in fact the same. 4 Overview of the SIP Servlet API All classes and interfaces of the SIP Servlet API are located within the org.ietf.sip package. The key interfaces are SipServlet and SipServletContext which represents servlets and their view of the servlet engine respectively. The servlet context implements the factory and contact database interfaces in addition to providing varios other facilities. A. Kristensen, A. Byttner [Page 7] Internet Draft The SIP Servlet API September 1999 The dynamic aspects of the API consists mainly of the SipRequest, SipResponse, and Transaction interfaces which provide information and control to servlets. 4.1 Interfaces ContactDatabase Access to the servlet engines contact database. This interface provides servlets with access to contact information for users. Contact Represents SIP Contact addresses, [RFC 2543, section 6.13]. ServletConfig Represents servlet configuration information. The mechanism used for associating configuration with servlet instances is outside the scope of the SIP Servlet API itself, but the information is made available to servlets via this class. SingleThreadModel The SingleThreadModel empty interface is implemented by servlets that does not wish to handle more than one event at a time. It is the servlet engines responsibility to ensure that it only ever invokes one callback method in the servlet at a time. SipAddress Represents value of SIP From and To headers and serves as the base class for the representation of Contact header values. SipConstants This interface defines a number of constants used throughout the SIP API. It defines constants corresponding to SIP methods and status codes. For convenience this interface is extended by several other interfaces. SipFactory Factory methods for SipURLs, Contacts, SipAddresses, and Transactions. SipMessage Base interface for SipRequest and SipResponse. As SIP entities can receive and send requests as well as responses, there is a need for SIP servlets to be able to set and get headers, content, and other information in both requests and response. This interface reflects the commonality between all SIP messages. SipRequest Represents SIP requests. This class provides access to all parts of SIP requests, both for incoming and outgoing messages. SipResponse Represents SIP responses. A. Kristensen, A. Byttner [Page 8] Internet Draft The SIP Servlet API September 1999 SipServlet The interface implemented by SIP servlets in order to extend SIP servlet engines. SipServletContext The SipServletContext represents the servlet engine, that is the SIP stack, to SipServlets. It provides access to a variety of functionality: logging, server attributes, server software information, factory methods for SIP messages, URLs, etc, as well as access to the servers contact database. SipTransaction Represents SIP transactions. SipURL Represents SIP URLs, i.e. addresses of SIP endpoints [RFC 2543, section 2]. SIP URLs are used in the request-URI of SIP requests as well as in To, From, and Contact header fields. 4.2 Classes SipUtils Miscellaneous static utility methods. SipServletAdapter An abstract implementation of the SipServlet interface. This provides default implementations for all methods and is a convenient base class for servlets. 4.3 Exceptions ParseException A ParseException is thrown when an error occurs while attempting to parse some entity. ServletException ServletExceptions can be thrown by servlets to indicate to the server that an error occurred during processing of a SIP message. 5 Overview of SIP Servlet API Usage The API is primarily intended for use in SIP servers, in particular proxies, but can also be supported by user agents. When a SIP request belonging to a new transaction is received, the server decides which servlet, if any, should receive it. As discussed in section 2.2 the mechanism for associating initial requests with servlets is not specified by this document. The server passes an object representing the request to the servlet, which then decides whether to proxy it, respond to it, or whether to let the server perform its default processing on the message. 5.1 Registering for Callbacks Servlets can register themselves as listeners on a transaction via the SipTransaction interface: A. Kristensen, A. Byttner [Page 9] Internet Draft The SIP Servlet API September 1999 public void addListener(SipServlet servlet) public void removeListener(SipServlet servlet) SipTransaction listeners receive all messages related to the corresponding transaction that a server receives. Servlets are automatically registered as listeners for the initial transactions but not for transactions it itself initiates. Hence, in order to receive responses to locally initiated request, a servlet will have to first register itself as a listener for that transaction. If the number of listeners on a transaction drops to zero and no more than one outstanding proxied request exists, the servlet engine MAY free up any resources it has associated with the transaction and become stateless with regard to that transaction. In this case the implementation of the SipTransaction interface should contain all information needed for the engine to go stateful at a later time, in case a servlet keeps a reference to the SipTransaction and later adds itself as a listener. The ability of servlets to unsubscribe themselves as listeners for a transaction is useful because it allows proxies to go stateless but it may also have performance benefits, at least for distributed servlet engines. Are finer grained registrations desirable, e.g. on final responses only as in SIP CGI? 5.2 Message Processing Servlets control how messages it receives are handled. It can proxy and initiate requests and consume responses or forward them upstream. This is done using the following methods: public Object SipRequest.send(SipURL nextHop) public void SipResponse.send() SipRequest.send is used both for initiating and forwarding requests. The SipURL argument identifies the next hop and basically represents the request URI as it will appear in the request line of the message. SipResponse.send is used to forward responses received in a proxy and to send locally generated responses upstream. The receiever is determined by the Via headers of the response. Servlets are free to inspect all parts of SIP messages, both headers and content, and to set the value of non-system header fields. 5.3 System Headers We use the term "system" headers to refer to those headers that are managed by the SIP stack and which servlets MUST NOT attempt to set epxlicitly via SipMessage.setHeader or similar "generic" methods. This includes the following headers: Call-ID, From, To, CSeq, Via, A. Kristensen, A. Byttner [Page 10] Internet Draft The SIP Servlet API September 1999 Record-Route, and Content-Length. These headers should not need to be manipulated directly by servlets. This makes it easier for a SIP stack to enforce correct protocol behaviour. 5.4 Creating Messages A servlet can respond to a request by first creating a corresponding response, and then send it. For example: public boolean doInvite(SipRequest req) { SipResponse res = req.createResponse(); res.setStatus(486); res.send(); return true; } The value of system headers of the response object is derived from the corresponding values in the request object, as are all other headers which a UAS is required to copy from requests to responses. In order to initiate an outgoing request in a new transaction it is necessary to first create a transaction object and then a request: from = context.createSipAddress("Bell ... req.send(to.getSipURL()); In this case the value of system headers is taken from the transaction object which was initialized with the spceified From and To headers and Call-ID and CSeq number chosen by the implementation. Servers MAY modify messages sent by servlets in other ways, e.g. by adding other headers. The last example shows how factory methods in SipServletContext are used instead of constructors in order to insulate servlet code from any specific implementation. 5.5 The default action SIP servlets respond to message callbacks with a boolean indicating whether or not the server should apply its default processing to the message (if the return value is false) or not (true). All methods in the SipServletAdapter class returns false, meaning that default message handling will take place for all callback methods that are not overridden. A. Kristensen, A. Byttner [Page 11] Internet Draft The SIP Servlet API September 1999 Using return values to control whether or not to perform default actions means a servlet cannot possibly instruct the server to perform it more than once, although it may be unfortunate that the default action can only be performed upon returning from a callback. 5.6 Request Tokens The SipRequest.send(SipURL) method returns a "request token" which can be used by servlets to match against similar tokens obtained from SipResponse.getRequestToken(), in order to match up responses with requests. Request tokens should be treated as opaque objects which are only good for comparing reference equality. Request tokens are used for the same purpose as in SIP CGI. They are needed in the SIP Servlet API because a forking proxy may invoke send() on a request object more than once, meaning that the request object itself cannot play the role of the request token. 5.7 Access to Message Content Access to the body of messages is via the following methods in the SipMessage interface: public byte[] getContent() public Object getParsedContent() throws ParseException public Object getMediaDescription() throws ParseException public void setContent(byte[] buf) getContent and setContent works in terms of the raw bytes constituting the content, whereas getParsedContent() and getMediaDescription() attempts to parse the content according to the MIME type of the message. The type of object returned will thus depend on the Content-Type of the message. A server is required at a minimum to be able to parse SDP [RFC 2327]. We should define or refer to a Java API for SDP that conformant implementations must adhere to. The difference between getParsedContent() and getMediaDescription() is that is that for messages with a MIME multipart Content-Type, getParsedContent() will return an object representing the entire multipart body while getMediaDescription() will recurse into the multipart body to find, parse and return a body part with a recognized media description. Should we integrate with the Java Activation Framework, e.g. define SipMessage to extend javax.activation.DataSource? Implementations of the present API may delegate parsing to JAF without changing the SIP Servlet API. A. Kristensen, A. Byttner [Page 12] Internet Draft The SIP Servlet API September 1999 For multipart messages getParsedContent() SHOULD return an instance of javax.mail.MultiPart, which represents MIME multipart messages in the JavaMail API [JavaMail]. "A SIP-CGI/1.1 server MAY choose to process some methods directly rather than passing them to scripts" [p. 15]. 5.8 The Contact Database Servlets have access to the servers contact database via the ContactDatabase interface (extended by SipServletContext). There is no assumption about how the contact database is implemented - it could be a database, local flat files, a directory server, or something else. Servlets can look up and modify contact information, subject to whatever security constraints the server might want to impose. 5.9 Optional Behaviour SIP servlet engines MAY choose to process incoming messages itself rather than handing them to a listening servlet. This can happen, for example, if the server doesn't satisfy a Requires header, if the message fails authentication or the caller doesn't have the required privileges. Likewise engines may attempt to detect protocol violations made by servlets and take appropriate actions. Conformant SIP servers would typically want to impose various security checks on servlets, see section 9. 6 API Reference We list here the interface and class signatures of the SIP Servlet API. The annotated API reference is browsable and the code downloadable from [SIPS Home]. 6.1 Interfaces 6.1.1 ContactDatabase public interface ContactDatabase Access to the servlet engines contact database. This interface allows servlets to obtain information about which "physical" contact addresses are currently registered for different "logical" addresses, and also to change it, for example, in response to incoming REGISTER requests. The server may lookup contact addresses based on the SipURL or display-name or other attributes of the argument. These operations are highly security sensitive and should be protected appropriately. A. Kristensen, A. Byttner [Page 13] Internet Draft The SIP Servlet API September 1999 public java.util.List getContacts(SipAddress sipAddr) public void setContacts(SipAddress sipAddr, java.util.List contacts) public void addContact(SipAddress sipAddr, Contact contact) public void removeContact(SipAddress sipAddr, Contact contact) 6.1.2 Contact public interface Contact extends SipAddress Represents SIP Contact addresses, [RFC 2543, section 6.13]. A Contact extends a SipAddress with three well-defined parameters: the q-value, and action, and an expires indication. This interfaces provides getters and setters for each of these. getWildcard() returns a static Contact instance representing the special "wildcard" Contact representing the "*" wildcard which can be used as the value of the Contact header in REGISTER requests, see [RFC 2543, section 6.13]. public Contact getWildcard() public float getQ() public void setQ(float q) public String getAction() public void setAction(String action) public String getExpires() public void setExpires(String expires) public interface ServletConfig public String getInitParameter(String name) public SipServletContext getServletContext() 6.1.3 SingleThreadModel public interface SingleThreadModel The SingleThreadModel empty interface is implemented by servlets that does not wish to handle more than one event at a time. It is the servlet engines responsibility to ensure that it only ever invokes one callback method in the servlet at a time. 6.1.4 SipAddress public interface SipAddress Represents value of SIP From and To headers and serves as the base class for the representation of Contact header values. A SipAddress can be thought of as representing something that conforms to the following grammar [RFC 2543, section 6.21]: SipAddress = (name-addr | addr-spec) *(";" addr-params) name-addr = [display-name] "<" addr-spec ">" addr-spec = SIP-URL | URI A. Kristensen, A. Byttner [Page 14] Internet Draft The SIP Servlet API September 1999 display-name = *token | quoted-string Note that a SipAddress can encapsulate either a SIP URL or an arbitrary non-SIP URI. These are represented in this class by SipURL and String attributes respectively, exactly one of which must (normally) be non-null. public String getDisplayName() public SipURL getSipURL() public String getURI() public String getParameter(String name) public java.util.Iterator getParameterNames() 6.1.5 SipConstants public interface SipConstants This interface defines a number of constants used throughout the SIP API. It defines constants corresponding to SIP methods and status codes. For convenience this interface is extended by several other interfaces. public static final String INVITE = "INVITE"; public static final String ACK = "ACK"; public static final String OPTIONS = "OPTIONS"; public static final String BYE = "BYE"; public static final String CANCEL = "CANCEL"; public static final String REGISTER = "REGISTER"; public static final int SC_TRYING = 100; public static final int SC_RINGING = 180; public static final int SC_CALL_FORWARDING = 181; public static final int SC_QUEUED = 182; public static final int SC_OK = 200; public static final int SC_MULTIPLE_CHOICES = 300; public static final int SC_MOVED_PERMANENTLY = 301; public static final int SC_MOVED_TEMPORARILY = 302; public static final int SC_SEE_OTHER = 303; public static final int SC_USE_PROXY = 305; public static final int SC_ALTERNATIVE_SERVICE = 380; public static final int SC_BAD_REQUEST = 400; public static final int SC_UNAUTHORIZED = 401; public static final int SC_PAYMENT_REQUIRED = 402; public static final int SC_FORBIDDEN = 403; public static final int SC_NOT_FOUND = 404; public static final int SC_METHOD_NOT_ALLOWED = 405; public static final int SC_PROXY_AUTHENTICATION_REQUIRED = 407; public static final int SC_REQUEST_TIMEOUT = 408; public static final int SC_CONFLICT = 409; public static final int SC_GONE = 410; public static final int SC_LENGTH_REQUIRED = 411; public static final int SC_PRECONDITION_FAILED = 412; public static final int SC_REQUEST_BODY_TOO_LARGE = 413; public static final int SC_REQUEST_URI_TOO_LARGE = 414; A. Kristensen, A. Byttner [Page 15] Internet Draft The SIP Servlet API September 1999 public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415; public static final int SC_BAD_EXTENSION = 420; public static final int SC_TEMPORARILY_NOT_AVAILABLE = 480; public static final int SC_INVALID_CALL_ID = 481; public static final int SC_LOOP_DETECTED = 482; public static final int SC_TOO_MANY_HOPS = 483; public static final int SC_INTERNAL_SERVER_ERROR = 500; public static final int SC_NOT_IMPLEMENTED = 501; public static final int SC_BAD_GATEWAY = 502; public static final int SC_SERVICE_UNAVAILABLE = 503; public static final int SC_GATEWAY_TIMEOUT = 504; public static final int SC_VERSION_NOT_SUPPORTED = 505; public static final int SC_BUSY = 600; public static final int SC_DECLINE = 603; public static final int SC_DOES_NOT_EXIST = 604; public static final int SC_NOT_ACCEPTABLE = 606; 6.1.6 SipFactory public interface SipFactory Factory methods for SipURLs, Contacts, SipAddresses, and Transactions. The createSipTransaction() method returns a new SIP transaction object with the specified parameters. This can be used to crate a new SipRequest object which is used to initiate new transactions. The servlet engine generates a new Call-ID and CSeq number for the transaction. Note that in order to receive responses to requests made within the returned transaction the caller must register itself as a listener with the transaction. public SipTransaction createSipTransaction(SipAddress from, SipAddress to) public SipURL createSipURL(String sipURL) throws ParseException public SipURL createSipURL(String user, String host) public SipURL createSipURL(String user, String host, int port) public SipAddress createSipAddress(String sipAddress) throws ParseException public SipAddress createSipAddress(SipURL sipURL) public SipAddress createSipAddress(SipURL sipURL, String displayName) public Contact createContact(String contact) throws ParseException public Contact createContact(SipURL url, String displayName) public Contact createContact(String uri, String displayName) public Contact createContact(SipAddress addr) 6.1.7 SipMessage public interface SipMessage extends SipConstants Base interface for SipRequest and SipResponse. A. Kristensen, A. Byttner [Page 16] Internet Draft The SIP Servlet API September 1999 As SIP entities can receive and send requests as well as responses, there is a need for SIP servlets to be able to set and get headers, content, and other information in both requests and response. This interface reflects the commonality between all SIP messages. public String getMethod() public String getProtocol() public String getRemoteAddr() public String getRemoteHost() public String getServerName() public int getServerPort() public String getServerProtocol() public SipTransaction getTransaction() public String getHeader(String name) public java.util.Iterator getHeaderNames() public java.util.Iterator getHeaders(String name) public void setHeader(String name, String value) public void addHeader(String name, String value) public SipAddress getFrom() public SipAddress getTo() public java.util.List getContacts() public void setContacts(java.util.List contacts) public int getContentLength() public String getContentType() public void setContentType(String type) public byte[] getContent() public Object getParsedContent() throws ParseException public Object getMediaDescription() throws ParseException public void setContent(byte[] buf) 6.1.8 SipRequest public interface SipRequest extends SipMessage public SipURL getRequestURI() public String getAuthType() public String getRemoteUser() public boolean recordRoute(boolean recordIt) public SipResponse createResponse() public Object send(SipURL url) 6.1.9 SipResponse public interface SipResponse extends SipMessage Represents SIP responses. public int getStatus() public String getReasonPhrase() public Object getRequestToken() public void setStatus(int code, String reason) public void setStatus(int code) public void send() A. Kristensen, A. Byttner [Page 17] Internet Draft The SIP Servlet API September 1999 6.1.10 SipServlet public interface SipServlet extends SipConstants The interface which must be implemented by SIP servlets in order to extend SIP servlet engines. This is implemented by the abstract class SipServletAdapter. public void init(ServletConfig config) public boolean gotRequest(SipRequest req) throws ServletException public boolean gotResponse(SipResponse res) throws ServletException public void destroy() public ServletConfig getServletConfig() public String getServletInfo() public void log(String msg) public void log(String msg, Throwable t) 6.1.11 SipServletContext public interface SipServletContext extends SipFactory, ContactDatabase The SipServletContext represents the servlet engine, that is, the SIP stack, to SipServlets. It provides access to a variety of functionality: logging, server attributes, server software information, factory methods for SIP messages, URLs, etc, as well as access to the servers contact database. public int getMajorVersion() public int getMinorVersion() public String getServerInfo() public Object getAttribute(String name) public java.util.Iterator getAttributeNames() public void setAttribute(String name, Object value) public void removeAttribute(String name) public void log(String msg) public void log(String msg, Throwable t) 6.1.12 SipTransaction public interface SipTransaction Represents SIP transactions. A transaction is identified by its CSeq number within a call leg. As calls and call legs are not represented explicitly in the API, transactions encapsulate Call-ID, From and To headers as well as the CSeq number. These transaction parameters doesn't change during the lifetime of a transaction, and hence are read-only attributes oif this class. SipTransactions created locally, e.g. by a servlet, are initialized with values for the From and To headers and the request method, whereas the value of the Call-ID, CSeq number and possibly tags associated with the From or To headers cannot be set by applications. A. Kristensen, A. Byttner [Page 18] Internet Draft The SIP Servlet API September 1999 Messages belonging to the same transaction will share the same SipTransaction instance. This is useful because transaction objects can hold uninterpreted client state, via its attribute methods. This requirement implies that distributed SIP servlet engines must route messages belonging to the same transaction to the same actual JVM. public String getCallID() public SipAddress getFrom() public SipAddress getTo() public String getCSeq() public SipRequest createRequest(String method) public Object getAttribute(String name) public java.util.Iterator getAttributeNames() public void setAttribute(String name, Object value) public void removeAttribute(String name) public void addListener(SipServlet servlet) public void removeListener(SipServlet servlet) 6.1.13 SipURL public interface SipURL Class SipURL represents addresses of SIP endpoints. SIP URLs are used in the request-URI of SIP requests as well as in To, From, and Contact header fields. See [RFC 2543, section 2] for the definiton of SIP URLs. public String getUser() public void setUser(String user) public String getPassword() public void setPassword(String password) public String getHost() public void setHost(String host) public int getPort() public void setPort(int port) public String getParameter(String name) public void setParameter(String name) public void setParameter(String name, String value) public void removeParameter(String name) public java.util.Iterator getParameterNames() public String toString() 6.2 Classes 6.2.1 SipUtils public abstract class SipUtils Miscellaneous static utility methods. A. Kristensen, A. Byttner [Page 19] Internet Draft The SIP Servlet API September 1999 public static long getJavaTime(String time) Given a time specification in the form of be 'delta-seconds' (an integer) or an RFC 1123 date specification returns Java time (absolute time in milliseconds since Jan 1 1970 - same unit as returned by System.currentTimeMillis()) corresponding to the specified SIP time. public static long getSipDeltaTime(String time) Returns the SIP delta time corresponding to a string which matches t = (SIP-date | delta-seconds) See RFC 2543, sections 6.20 and 6.13. 6.2.2 SipServletAdapter public abstract class SipServletAdapter implements SipServlet, ServletConfig A simple abstract adapter class for SIP servlets. In addition to providing default implementations for all methods, also dispatches incoming request to other methods based on the SIP request method. The default implementation of callbacks (gotRequest, gotResponse, do*) all return false to indicate to the servlet engine that it should perform the default action for the message. public void init(ServletConfig config) public boolean gotRequest(SipRequest req) throws ServletException public boolean doInvite(SipRequest req) throws ServletException public boolean doAck(SipRequest req) throws ServletException public boolean doOptions(SipRequest req) throws ServletException public boolean doBye(SipRequest req) throws ServletException public boolean doCancel(SipRequest req) throws ServletException public boolean doRegister(SipRequest req) throws ServletException public boolean gotResponse(SipResponse res) throws ServletException public void destroy() public ServletConfig getServletConfig() public String getInitParameter(String name) public java.util.Iterator getInitParameterNames() public SipServletContext getServletContext() public String getServletInfo() 6.3 Exceptions 6.3.1 ParseException public class ParseException extends java.lang.Exception A ParseException is thrown when an error occurs while attempting to parse some entity. A. Kristensen, A. Byttner [Page 20] Internet Draft The SIP Servlet API September 1999 public ParseException() public ParseException(java.lang.String msg) 6.3.2 ServletException public class ServletException extends java.lang.Exception ServletExceptions can be thrown by servlets to indicate to the server that an error occurred during processing of a SIP message. public ServletException () public ServletException (java.lang.String msg) 7 Examples 7.1 RejectServlet This following servlet overrides SipServletAdapter.doInvite to always reject INVITE requests. The status code and reason phrase of the response is configurable. import org.ietf.sip.*; public class RejectServlet extends SipServletAdapter { protected int statusCode; protected String reasonPhrase; public void init(ServletConfig config) { super.init(config); try { statusCode = Integer.parseInt( getInitParameter("status-code")); reasonPhrase = getInitParameter("reason-phrase"); } catch (Exception _) { statusCode = SC_INTERNAL_SERVER_ERROR; } } public boolean doInvite(SipRequest req) { SipResponse res = req.createResponse(); res.setStatus(statusCode, reasonPhrase); res.send(); return true; } } This can easily be expanded to provide a more complete call-screening service. 7.2 LogCallDuration This example logs call detail records to a database. It illustrates how a servlet can use local state to associate transactions belonging to the same call and also shows how servlets can amortize the cost of A. Kristensen, A. Byttner [Page 21] Internet Draft The SIP Servlet API September 1999 expensive resource allocation (the database connection) over a number of invocations by allocating the resource in the init() method and free it in destroy(). The initial INVITE causes the servlet to to ensure that it is on the path of a subsequent BYE request by invoking recordRoute(true) on the SipRequest object. When, or if, a 2xx response to the INVITE is seen the start time of the call is recorded. When a 2xx response to a subsequent BYE is seen, the call details are logged to the database. Both doInvite() and gotResponse() returns false so that the servlet engine will perform its default processing on those messages. import java.util.Hashtable; import org.ietf.sip.*; public class LogCallDuration extends SipServletAdapter { static Hashtable idToStart = new Hashtable(); public void init(ServletConfig config) { super.init(config); // open database connection... } // insert Record-Route header in INVITE request public boolean doInvite(SipRequest req) { req.recordRoute(true); return false; } // on 2xx response to INVITE: save start time // on 2xx response to BYE: log call details to database public boolean gotResponse(SipResponse res) { String callID = res.getCallID(); String method = res.getMethod(); if (res.getStatus()/100 == 2) { if (method.equals(INVITE)) { Long start = new Long(System.currentTimeMillis()); idToStart.put(callID, start); } else if (method.equals(BYE)) { Long start = (Long) idToStart.remove(callID); if (start != null) { logCall(callID, res.getFrom(), res.getTo(), start.longValue(), System.currentTimeMillis()); } } } return false; } void logCall(String callID, A. Kristensen, A. Byttner [Page 22] Internet Draft The SIP Servlet API September 1999 SipAddress from, SipAddress to, long start, long end) { // save call details to database... } public void destroy() { // close database connection... } } 8 Issues o Servlet chaining: It is potentially useful or even essential that we allow multiple servlets to execute in response to a incoming SIP messages. If a servlet implements a single service this is necessary, for example, whenever a user subscribes to multiple services which are triggered by the same condition. We have ignored the question of how servlet engines are supposed to handle this situation. One possiblity is to execute servlets one at a time until one returns true, indicating that the message has been fully handled. o Deployment descriptors: It would be useful to be able to package up servlet code together with additional information describing how the servlet is to be deployed into a SIP servlet engine, e.g. describe servlet parameters, invocation mappings (see section 2.2), and security information. o JAF Integration? o We need to have an API for SDP structures. o Should we insist that getMethod() returns an interned String? (Would enable clients to use reference comparisons rather than string equality.) o Servlets are prohibited from setting system headers, but there is a potentially significant overhead associated with enforcing this. This can be addressed by providing non-generic setters for the most commonly used headers but could also be addressed by changing the signature of generic methods, e.g. to take a HeaderName argument rather than a String, and controlling how HeaderNames can be obtained - static getters and non-public constructors. This would allow header identifiers to be knowledgeable about the contexts in which they can be used... 9 Security Considerations The biggest security concern raised by the introduction of SIP servlets is the havoc wreaking potential of this sometimes untrusted code. A. Kristensen, A. Byttner [Page 23] Internet Draft The SIP Servlet API September 1999 9.1 Sensitive Operations Other than access to resources such as as file systems, network, windowing system, etc., the SIP Servlet API introduces a couple of sensitive operations of its own: o Access to a registrars contact database (the ContactDatabase interface) is highly sensitive. The whereabouts of people should usually be considered private data belonging to those individuals and should be protected against unauthorized access. Apart from just being private information, the ability to modify registration data can be used to gain control over calls and thus o The ability to initiate arbitrary SIP requests. o The ability to generate responses to requests. A server may execute untrusted code in a "servlet sandbox" - an environment that restricts the set of operations that can be performed. In addition to the original sandbox model used in browsers to execute untrusted applet code the JDK1.2 security model allows fine-grained access-rights to be associated with code from different sources. Using this, servlets can, for example, be allowed to look up and/or modify contact information for a specific subset of people. Servlet engines are free to control access to any of the methods defined in this document. A failed permission check causes a runtime exception (e.g. java.security.AccessControlException) to be thrown. As runtime exceptions does not have to be declared in throws clauses of method signatures any method can potentially be disallowed for specific servlets. The most obvious candidates for access control are: o all methods in the ContactDatabase interface (extended by SipServletContext) o SipServletContext.createSipTransaction - as this is needed to initiate requests. o SipRequest.createResponse - if a servlet is allowed to forward responses but not to create responses of its own Additionally, a server MAY limit and monitor the number of requests and responses initiated by a servlet. 9.2 Buggy or Malicious Servlet Code Buggy or malicious servlet code shouldn't be able to core dump a servlet engine. The lack of pointers and the presence of exception handling allows servers to protect themselves against this type of badly written extension code. 9.3 Protocol Violations As the very point of SIP servlets is that responsibility for message processing can be delegated to code that can be developed and distributed independently of servlet engines, these extensions almost A. Kristensen, A. Byttner [Page 24] Internet Draft The SIP Servlet API September 1999 necessarily have the potential to violate the SIP protocol in various ways. A SIP servlet engine MAY attempt to detect various types of protocol violations and destroy or otherwise curb misbehaving in addition to informing the administrator. Such violations include: o Illegal message formats, e.g. badly formatted header fields, attempts to set the Class-ID or From fields of proxied requests, say. o Illegal state changes, for example generating a BYE request for an already disconnected call leg, a response to an ACK, or multiple final non-2xx responses. Having servlet engines manage most of the headers directly involved in message routing - Call-IDs, CSeq numbers, From and To headers and Via header processing - makes it easier to detect protocol violations as servlets should never set these headers explicitly. 9.4 Denial of Service Attacks The access control security checks that can be performed with JDK1.2 and the older sandbox model cannot protect a server against denial- of-service attacks based on excessive use of memory or CPU cycles. It may well be feasible to analyze Java bytecode in order to enforce restrictions on SIP servlets such as they contain no loops and allocate only a bounded amount of memory. Such a check could be performed at the time the code is uploaded or otherwise installed on a server. A less ambitious approach is for a server administrator to manually inspect the servlet code or to rely on code signing techniques to achieve an appropriate level of trust in servlet code. 10 Acknowledgements As mentioned in section 3.2 many of the issues faced in the development of a SIP servlet API are similar to the ones identified in [SIP CGI] and this draft has benefitted from that earlier work. 11 Full Copyright Statement Copyright (c) The Internet Society (1999). All Rights Reserved. This document and translations of it may be copied and furnished to others, and derivative works that comment on or otherwise explain it or assist in its implementation may be prepared, copied, published and distributed, in whole or in part, without restriction of any kind, provided that the above copyright notice and this paragraph are included on all such copies and derivative works. However, this document itself may not be modified in any way, such as by removing the copyright notice or references to the Internet Society or other Internet organizations, except as needed for the purpose of A. Kristensen, A. Byttner [Page 25] Internet Draft The SIP Servlet API September 1999 developing Internet standards in which case the procedures for copyrights defined in the Internet Standards process must be followed, or as required to translate it into languages other than English. The limited permissions granted above are perpetual and will not be revoked by the Internet Society or its successors or assigns. This document and the information contained herein is provided on an "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. References [SIPS Home] SIP Servlet API homepage, http://www-uk.hpl.hp.com/people/ak/sip/servlet/ [JavaMail] The JavaMail(TM) API, http://java.sun.com/products/javamail/ [RFC 2543] M. Handley, H. Schulzrinne, E. Schooler, and J. Rosenberg, "SIP: Session Initiation Protocol", RFC 2543, March 1999. [RFC 2327] M. Handley and V. Jacobson,"SDP: Session Description Protocol", RFC 2327, April 1999. [Servlet API] James D. Davidson, "Java(TM) Servlet Specification, v2.2", August 1999. [SIP CGI] J. Lennox, J. Rosenberg, and H. Schulzrinne, "Common Gateway Interface for SIP", Internet Draft, May 20, 1999. Work in progress. Authors' Addresses Anders Kristensen Hewlett-Packard Laboratories Filton Road, Stoke Gifford Bristol BS34 8QZ United Kingdom E-mail: ak@hplb.hpl.hp.com Anders Byttner Hewlett-Packard Laboratories Filton Road, Stoke Gifford Bristol BS34 8QZ United Kingdom E-mail: andbyt@hplb.hpl.hp.com A. Kristensen, A. Byttner [Page 26]