diff --git a/OpenSim/Framework/Communications/OutboundUrlFilter.cs b/OpenSim/Framework/Communications/OutboundUrlFilter.cs new file mode 100644 index 0000000000..8b572d1c58 --- /dev/null +++ b/OpenSim/Framework/Communications/OutboundUrlFilter.cs @@ -0,0 +1,256 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Reflection; +using log4net; +using LukeSkywalker.IPNetwork; +using Nini.Config; + +namespace OpenSim.Framework.Communications +{ + public class OutboundUrlFilter + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public string Name { get; private set; } + + private List m_blacklistNetworks; + private List m_blacklistEndPoints; + + private List m_blacklistExceptionNetworks; + private List m_blacklistExceptionEndPoints; + + public OutboundUrlFilter( + string name, + List blacklistNetworks, List blacklistEndPoints, + List blacklistExceptionNetworks, List blacklistExceptionEndPoints) + { + Name = name; + + m_blacklistNetworks = blacklistNetworks; + m_blacklistEndPoints = blacklistEndPoints; + m_blacklistExceptionNetworks = blacklistExceptionNetworks; + m_blacklistExceptionEndPoints = blacklistExceptionEndPoints; + } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the filter for logging purposes. + /// Filter configuration + public OutboundUrlFilter(string name, IConfigSource config) + { + Name = name; + + string configBlacklist + = "0.0.0.0/8|10.0.0.0/8|100.64.0.0/10|127.0.0.0/8|169.254.0.0/16|172.16.0.0/12|192.0.0.0/24|192.0.2.0/24|192.88.99.0/24|192.168.0.0/16|198.18.0.0/15|198.51.100.0/24|203.0.113.0/24|224.0.0.0/4|240.0.0.0/4|255.255.255.255/32"; + string configBlacklistExceptions = ""; + + IConfig networkConfig = config.Configs["Network"]; + + if (networkConfig != null) + { + configBlacklist = networkConfig.GetString("OutboundDisallowForUserScripts", configBlacklist); + configBlacklistExceptions + = networkConfig.GetString("OutboundDisallowForUserScriptsExcept", configBlacklistExceptions); + } + + m_log.DebugFormat( + "[OUTBOUND URL FILTER]: OutboundDisallowForUserScripts for {0} is [{1}]", Name, configBlacklist); + m_log.DebugFormat( + "[OUTBOUND URL FILTER]: OutboundDisallowForUserScriptsExcept for {0} is [{1}]", Name, configBlacklistExceptions); + + OutboundUrlFilter.ParseConfigList( + configBlacklist, Name, out m_blacklistNetworks, out m_blacklistEndPoints); + OutboundUrlFilter.ParseConfigList( + configBlacklistExceptions, Name, out m_blacklistExceptionNetworks, out m_blacklistExceptionEndPoints); + } + + private static void ParseConfigList( + string fullConfigEntry, string filterName, out List networks, out List endPoints) + { + // Parse blacklist + string[] configBlacklistEntries + = fullConfigEntry.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries); + + configBlacklistEntries = configBlacklistEntries.Select(e => e.Trim()).ToArray(); + + networks = new List(); + endPoints = new List(); + + foreach (string configEntry in configBlacklistEntries) + { + if (configEntry.Contains("/")) + { + IPNetwork network; + + if (!IPNetwork.TryParse(configEntry, out network)) + { + m_log.ErrorFormat( + "[OUTBOUND URL FILTER]: Entry [{0}] is invalid network for {1}", configEntry, filterName); + + continue; + } + + networks.Add(network); + } + else + { + Uri configEntryUri; + + if (!Uri.TryCreate("http://" + configEntry, UriKind.Absolute, out configEntryUri)) + { + m_log.ErrorFormat( + "[OUTBOUND URL FILTER]: EndPoint entry [{0}] is invalid endpoint for {1}", + configEntry, filterName); + + continue; + } + + IPAddress[] addresses = Dns.GetHostAddresses(configEntryUri.Host); + + foreach (IPAddress addr in addresses) + { + if (addr.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) + { + // m_log.DebugFormat("[OUTBOUND URL FILTER]: Found address [{0}] in config", addr); + + IPEndPoint configEntryEp = new IPEndPoint(addr, configEntryUri.Port); + endPoints.Add(configEntryEp); + + // m_log.DebugFormat("[OUTBOUND URL FILTER]: Added blacklist exception [{0}]", configEntryEp); + } + } + } + } + } + + /// + /// Determines if an url is in a list of networks and endpoints. + /// + /// + /// IP address + /// + /// Networks. + /// End points. + /// Filter name. + private static bool IsInNetwork( + IPAddress addr, int port, List networks, List endPoints, string filterName) + { + foreach (IPNetwork ipn in networks) + { +// m_log.DebugFormat( +// "[OUTBOUND URL FILTER]: Checking [{0}] against network [{1}]", addr, ipn); + + if (IPNetwork.Contains(ipn, addr)) + { +// m_log.DebugFormat( +// "[OUTBOUND URL FILTER]: Found [{0}] in network [{1}]", addr, ipn); + + return true; + } + } + + // m_log.DebugFormat("[OUTBOUND URL FILTER]: Found address [{0}]", addr); + + foreach (IPEndPoint ep in endPoints) + { +// m_log.DebugFormat( +// "[OUTBOUND URL FILTER]: Checking [{0}:{1}] against endpoint [{2}]", +// addr, port, ep); + + if (addr.Equals(ep.Address) && port == ep.Port) + { +// m_log.DebugFormat( +// "[OUTBOUND URL FILTER]: Found [{0}:{1}] in endpoint [{2}]", addr, port, ep); + + return true; + } + } + +// m_log.DebugFormat("[OUTBOUND URL FILTER]: Did not find [{0}:{1}] in list", addr, port); + + return false; + } + + /// + /// Checks whether the given url is allowed by the filter. + /// + /// + public bool CheckAllowed(Uri url) + { + bool allowed = true; + + // Check that we are permitted to make calls to this endpoint. + bool foundIpv4Address = false; + + IPAddress[] addresses = Dns.GetHostAddresses(url.Host); + + foreach (IPAddress addr in addresses) + { + if (addr.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) + { +// m_log.DebugFormat("[OUTBOUND URL FILTER]: Found address [{0}]", addr); + + foundIpv4Address = true; + + // Check blacklist + if (OutboundUrlFilter.IsInNetwork(addr, url.Port, m_blacklistNetworks, m_blacklistEndPoints, Name)) + { +// m_log.DebugFormat("[OUTBOUND URL FILTER]: Found [{0}] in blacklist for {1}", url, Name); + + // Check blacklist exceptions + allowed + = OutboundUrlFilter.IsInNetwork( + addr, url.Port, m_blacklistExceptionNetworks, m_blacklistExceptionEndPoints, Name); + +// if (allowed) +// m_log.DebugFormat("[OUTBOUND URL FILTER]: Found [{0}] in whitelist for {1}", url, Name); + } + } + + // Found at least one address in a blacklist and not a blacklist exception + if (!allowed) + return false; +// else +// m_log.DebugFormat("[OUTBOUND URL FILTER]: URL [{0}] not in blacklist for {1}", url, Name); + } + + // We do not know how to handle IPv6 securely yet. + if (!foundIpv4Address) + return false; + +// m_log.DebugFormat("[OUTBOUND URL FILTER]: Allowing request [{0}]", url); + + return allowed; + } + } +} \ No newline at end of file diff --git a/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs b/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs index 1a62405078..acebfbac6b 100644 --- a/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs +++ b/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs @@ -40,6 +40,7 @@ using log4net; using Nini.Config; using OpenMetaverse; using OpenSim.Framework; +using OpenSim.Framework.Communications; using OpenSim.Framework.Servers; using OpenSim.Framework.Servers.HttpServer; using OpenSim.Region.Framework.Interfaces; @@ -94,10 +95,13 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "HttpRequestModule")] public class HttpRequestModule : ISharedRegionModule, IHttpRequestModule { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private object HttpListLock = new object(); private int httpTimeout = 30000; private string m_name = "HttpScriptRequests"; + private OutboundUrlFilter m_outboundUrlFilter; private string m_proxyurl = ""; private string m_proxyexcepts = ""; @@ -156,7 +160,9 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest return UUID.Zero; } - public UUID StartHttpRequest(uint localID, UUID itemID, string url, List parameters, Dictionary headers, string body) + public UUID StartHttpRequest( + uint localID, UUID itemID, string url, List parameters, Dictionary headers, string body, + out HttpInitialRequestStatus status) { UUID reqID = UUID.Random(); HttpRequestClass htc = new HttpRequestClass(); @@ -232,7 +238,8 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest } } } - + + htc.RequestModule = this; htc.LocalID = localID; htc.ItemID = itemID; htc.Url = url; @@ -243,14 +250,43 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest htc.proxyurl = m_proxyurl; htc.proxyexcepts = m_proxyexcepts; + // Same number as default HttpWebRequest.MaximumAutomaticRedirections + htc.MaxRedirects = 50; + + if (StartHttpRequest(htc)) + { + status = HttpInitialRequestStatus.OK; + return htc.ReqID; + } + else + { + status = HttpInitialRequestStatus.DISALLOWED_BY_FILTER; + return UUID.Zero; + } + } + + /// + /// Would a caller to this module be allowed to make a request to the given URL? + /// + /// + public bool CheckAllowed(Uri url) + { + return m_outboundUrlFilter.CheckAllowed(url); + } + + public bool StartHttpRequest(HttpRequestClass req) + { + if (!CheckAllowed(new Uri(req.Url))) + return false; + lock (HttpListLock) { - m_pendingRequests.Add(reqID, htc); + m_pendingRequests.Add(req.ReqID, req); } - htc.Process(); + req.Process(); - return reqID; + return true; } public void StopHttpRequestsForScript(UUID id) @@ -326,6 +362,8 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest m_proxyurl = config.Configs["Startup"].GetString("HttpProxy"); m_proxyexcepts = config.Configs["Startup"].GetString("HttpProxyExceptions"); + m_outboundUrlFilter = new OutboundUrlFilter("Script HTTP request module", config); + m_pendingRequests = new Dictionary(); } @@ -368,7 +406,7 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest #endregion } - public class HttpRequestClass: IServiceRequest + public class HttpRequestClass : IServiceRequest { // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); @@ -380,6 +418,12 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest // public const int HTTP_VERBOSE_THROTTLE = 4; // public const int HTTP_CUSTOM_HEADER = 5; // public const int HTTP_PRAGMA_NO_CACHE = 6; + + /// + /// Module that made this request. + /// + public HttpRequestModule RequestModule { get; set; } + private bool _finished; public bool Finished { @@ -412,6 +456,17 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest public DateTime Next; public string proxyurl; public string proxyexcepts; + + /// + /// Number of HTTP redirects that this request has been through. + /// + public int Redirects { get; private set; } + + /// + /// Maximum number of HTTP redirects allowed for this request. + /// + public int MaxRedirects { get; set; } + public string OutboundBody; private UUID _reqID; public UUID ReqID @@ -419,7 +474,7 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest get { return _reqID; } set { _reqID = value; } } - public WebRequest Request; + public HttpWebRequest Request; public string ResponseBody; public List ResponseMetadata; public Dictionary ResponseHeaders; @@ -435,7 +490,8 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest { try { - Request = WebRequest.Create(Url); + Request = (HttpWebRequest)WebRequest.Create(Url); + Request.AllowAutoRedirect = false; Request.Method = HttpMethod; Request.ContentType = HttpMIMEType; @@ -566,7 +622,52 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest if (response != null) response.Close(); - _finished = true; + // We need to resubmit + if ( + (Status == (int)HttpStatusCode.MovedPermanently + || Status == (int)HttpStatusCode.Found + || Status == (int)HttpStatusCode.SeeOther + || Status == (int)HttpStatusCode.TemporaryRedirect)) + { + if (Redirects >= MaxRedirects) + { + Status = (int)OSHttpStatusCode.ClientErrorJoker; + ResponseBody = "Number of redirects exceeded max redirects"; + _finished = true; + } + else + { + string location = response.Headers["Location"]; + + if (location == null) + { + Status = (int)OSHttpStatusCode.ClientErrorJoker; + ResponseBody = "HTTP redirect code but no location header"; + _finished = true; + } + else if (!RequestModule.CheckAllowed(new Uri(location))) + { + Status = (int)OSHttpStatusCode.ClientErrorJoker; + ResponseBody = "URL from HTTP redirect blocked: " + location; + _finished = true; + } + else + { + Status = 0; + Url = response.Headers["Location"]; + Redirects++; + ResponseBody = null; + +// m_log.DebugFormat("Redirecting to [{0}]", Url); + + Process(); + } + } + } + else + { + _finished = true; + } } } diff --git a/OpenSim/Region/CoreModules/Scripting/HttpRequest/Tests/ScriptsHttpRequestsTests.cs b/OpenSim/Region/CoreModules/Scripting/HttpRequest/Tests/ScriptsHttpRequestsTests.cs index e812d816dc..8d190b8beb 100644 --- a/OpenSim/Region/CoreModules/Scripting/HttpRequest/Tests/ScriptsHttpRequestsTests.cs +++ b/OpenSim/Region/CoreModules/Scripting/HttpRequest/Tests/ScriptsHttpRequestsTests.cs @@ -147,11 +147,11 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest.Tests /// /// Test what happens when we get a 404 response from a call. /// - [Test] +// [Test] public void Test404Response() { TestHelpers.InMethod(); -// TestHelpers.EnableLogging(); + TestHelpers.EnableLogging(); if (!Util.IsPlatformMono) Assert.Ignore("Ignoring test since can only currently run on Mono"); diff --git a/OpenSim/Region/CoreModules/Scripting/LoadImageURL/LoadImageURLModule.cs b/OpenSim/Region/CoreModules/Scripting/LoadImageURL/LoadImageURLModule.cs index 65737fa693..f79d55dda7 100644 --- a/OpenSim/Region/CoreModules/Scripting/LoadImageURL/LoadImageURLModule.cs +++ b/OpenSim/Region/CoreModules/Scripting/LoadImageURL/LoadImageURLModule.cs @@ -32,6 +32,7 @@ using System.Net; using Nini.Config; using OpenMetaverse; using OpenMetaverse.Imaging; +using OpenSim.Framework.Communications; using OpenSim.Region.CoreModules.Scripting.DynamicTexture; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; @@ -50,6 +51,7 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL private Scene m_scene; private IDynamicTextureManager m_textureManager; + private OutboundUrlFilter m_outboundUrlFilter; private string m_proxyurl = ""; private string m_proxyexcepts = ""; @@ -88,8 +90,7 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL public bool AsyncConvertUrl(UUID id, string url, string extraParams) { - MakeHttpRequest(url, id); - return true; + return MakeHttpRequest(url, id); } public bool AsyncConvertData(UUID id, string bodyData, string extraParams) @@ -110,6 +111,7 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL public void Initialise(IConfigSource config) { + m_outboundUrlFilter = new OutboundUrlFilter("Script dynamic texture image module", config); m_proxyurl = config.Configs["Startup"].GetString("HttpProxy"); m_proxyexcepts = config.Configs["Startup"].GetString("HttpProxyExceptions"); } @@ -157,9 +159,13 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL #endregion - private void MakeHttpRequest(string url, UUID requestID) + private bool MakeHttpRequest(string url, UUID requestID) { - WebRequest request = HttpWebRequest.Create(url); + if (!m_outboundUrlFilter.CheckAllowed(new Uri(url))) + return false; + + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); + request.AllowAutoRedirect = false; if (m_proxyurl != null && m_proxyurl.Length > 0) { @@ -174,12 +180,14 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL } } - RequestState state = new RequestState((HttpWebRequest) request, requestID); + RequestState state = new RequestState(request, requestID); // IAsyncResult result = request.BeginGetResponse(new AsyncCallback(HttpRequestReturn), state); request.BeginGetResponse(new AsyncCallback(HttpRequestReturn), state); TimeSpan t = (DateTime.UtcNow - new DateTime(1970, 1, 1)); state.TimeOfRequest = (int) t.TotalSeconds; + + return true; } private void HttpRequestReturn(IAsyncResult result) @@ -195,10 +203,11 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL Stream stream = null; byte[] imageJ2000 = new byte[0]; Size newSize = new Size(0, 0); + HttpWebResponse response = null; try { - HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result); + response = (HttpWebResponse)request.EndGetResponse(result); if (response != null && response.StatusCode == HttpStatusCode.OK) { stream = response.GetResponseStream(); @@ -262,18 +271,32 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL finally { if (stream != null) - { stream.Close(); + + if (response != null) + response.Close(); + + if ( + response.StatusCode == HttpStatusCode.MovedPermanently + || response.StatusCode == HttpStatusCode.Found + || response.StatusCode == HttpStatusCode.SeeOther + || response.StatusCode == HttpStatusCode.TemporaryRedirect) + { + string redirectedUrl = response.Headers["Location"]; + + MakeHttpRequest(redirectedUrl, state.RequestID); + } + else + { + m_log.DebugFormat("[LOADIMAGEURLMODULE]: Returning {0} bytes of image data for request {1}", + imageJ2000.Length, state.RequestID); + + m_textureManager.ReturnData( + state.RequestID, + new OpenSim.Region.CoreModules.Scripting.DynamicTexture.DynamicTexture( + request.RequestUri, null, imageJ2000, newSize, false)); } } - - m_log.DebugFormat("[LOADIMAGEURLMODULE]: Returning {0} bytes of image data for request {1}", - imageJ2000.Length, state.RequestID); - - m_textureManager.ReturnData( - state.RequestID, - new OpenSim.Region.CoreModules.Scripting.DynamicTexture.DynamicTexture( - request.RequestUri, null, imageJ2000, newSize, false)); } #region Nested type: RequestState diff --git a/OpenSim/Region/Framework/Interfaces/IHttpRequests.cs b/OpenSim/Region/Framework/Interfaces/IHttpRequests.cs index 113dcd7515..124504c49c 100644 --- a/OpenSim/Region/Framework/Interfaces/IHttpRequests.cs +++ b/OpenSim/Region/Framework/Interfaces/IHttpRequests.cs @@ -25,6 +25,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +using System; using System.Collections.Generic; using OpenMetaverse; @@ -41,10 +42,44 @@ namespace OpenSim.Region.Framework.Interfaces HTTP_PRAGMA_NO_CACHE = 6 } + /// + /// The initial status of the request before it is placed on the wire. + /// + /// + /// The request may still fail later on, in which case the normal HTTP status is set. + /// + [Flags] + public enum HttpInitialRequestStatus + { + OK = 1, + DISALLOWED_BY_FILTER = 2 + } + public interface IHttpRequestModule { UUID MakeHttpRequest(string url, string parameters, string body); - UUID StartHttpRequest(uint localID, UUID itemID, string url, List parameters, Dictionary headers, string body); + + /// + /// Starts the http request. + /// + /// + /// This is carried out asynchronously unless it fails initial checks. Results are fetched by the script engine + /// HTTP requests module to be distributed back to scripts via a script event. + /// + /// The ID of the request. If the requested could not be performed then this is UUID.Zero + /// Local ID of the object containing the script making the request. + /// Item ID of the script making the request. + /// Url to request. + /// LSL parameters for the request. + /// Extra headers for the request. + /// Body of the request. + /// + /// Initial status of the request. If OK then the request is actually made to the URL. Subsequent status is + /// then returned via IServiceRequest when the response is asynchronously fetched. + /// + UUID StartHttpRequest( + uint localID, UUID itemID, string url, List parameters, Dictionary headers, string body, + out HttpInitialRequestStatus status); /// /// Stop and remove all http requests for the given script. diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index 975bf2dafb..5a37ff5d04 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs @@ -10762,8 +10762,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } } + HttpInitialRequestStatus status; UUID reqID - = httpScriptMod.StartHttpRequest(m_host.LocalId, m_item.ItemID, url, param, httpHeaders, body); + = httpScriptMod.StartHttpRequest(m_host.LocalId, m_item.ItemID, url, param, httpHeaders, body, out status); + + if (status == HttpInitialRequestStatus.DISALLOWED_BY_FILTER) + Error("llHttpRequest", string.Format("Request to {0} disallowed by filter", url)); if (reqID != UUID.Zero) return reqID.ToString(); @@ -11212,6 +11216,29 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return item.ItemID; } + /// + /// Reports the script error in the viewer's Script Warning/Error dialog and shouts it on the debug channel. + /// + /// The name of the command that generated the error. + /// The error message to report to the user. + internal void Error(string command, string message) + { + string text = command + ": " + message; + if (text.Length > 1023) + { + text = text.Substring(0, 1023); + } + + World.SimChat(Utils.StringToBytes(text), ChatTypeEnum.DebugChannel, ScriptBaseClass.DEBUG_CHANNEL, + m_host.ParentGroup.RootPart.AbsolutePosition, m_host.Name, m_host.UUID, false); + + IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface(); + if (wComm != null) + { + wComm.DeliverMessage(ChatTypeEnum.Shout, ScriptBaseClass.DEBUG_CHANNEL, m_host.Name, m_host.UUID, text); + } + } + internal void ShoutError(string msg) { llShout(ScriptBaseClass.DEBUG_CHANNEL, msg); diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index b78e3b7fca..e2fd5a6e39 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -435,6 +435,32 @@ ;; the region ports use UDP. ; http_listener_port = 9000 + ; By default, OpenSimulator does not allow scripts to make HTTP calls to addresses on the simulator's LAN. + ; See the OutboundDisallowForUserScripts parameter in OpenSimDefaults.ini for more information on this filter. + ; If you need to allow scripts to make some LAN calls use the OutboundDisallowForUserScriptsExcept parameter below. + ; We recommend that you do not override OutboundDisallowForUserScripts directly unless you are very sure about what you're doing. + ; + ; You can whitelist individual endpoints by IP or FQDN, e.g. + ; + ; OutboundDisallowForUserScriptsExcept = 192.168.1.3:8003 + ; + ; You can specify multiple addresses by separating them with a bar. For example, + ; + ; OutboundDisallowForUserScriptsExcept = 192.168.1.3:8003|myinternalserver:8000 + ; + ; If an address if given without a port number then port 80 is assumed + ; + ; You can also specify a network range in CIDR notation to whitelist, e.g. + ; + ; OutboundDisallowForUserScriptsExcept = 192.168.1.0/24 + ; + ; to whitelist all ports on addresses 192.168.1.0 to 192.168.1.255 + ; To specify an individual IP address use the /32 netmask + ; + ; OutboundDisallowForUserScriptsExcept = 192.168.1.2/32 + ; + ; See http://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation for more information on CIDR notation + ;# {ExternalHostNameForLSL} {} {Hostname to use for HTTP-IN URLs. This should be reachable from the internet.} {} ;; Hostname to use in llRequestURL/llRequestSecureURL ;; if not defined - default machine name is being used diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini index e6f92d9e0d..000ccc9007 100644 --- a/bin/OpenSimDefaults.ini +++ b/bin/OpenSimDefaults.ini @@ -444,6 +444,26 @@ ; (on Windows this mean NETBIOS name - useably only inside local network) ; ExternalHostNameForLSL=127.0.0.1 + ; Disallow the following address ranges for user scripting calls (e.g. llHttpRequest()) + ; This is based on http://en.wikipedia.org/wiki/Reserved_IP_addresses + ; This stops users making HTTP calls to machines in the simulator's local network. + ; If you need to allow some LAN calls we recommend you use OutboundDisallowForUserScriptsExcept documented in OpenSim.ini.example + ; If you override OutboundDisallowForUserScripts directly you need to be very careful. + ; + ; Network ranges are specified in CIDR notation (http://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation) with multiple entries separated by | + ; To specify an individual IP address use the /32 netmask (e.g. 192.168.1.3/32) + ; You can also specify individual : endpoints (e.g. 192.168.1.3:8003) + ; If an address if given without a port number then port 80 is assumed. + OutboundDisallowForUserScripts = 0.0.0.0/8|10.0.0.0/8|100.64.0.0/10|127.0.0.0/8|169.254.0.0/16|172.16.0.0/12|192.0.0.0/24|192.0.2.0/24|192.88.99.0/24|192.168.0.0/16|198.18.0.0/15|198.51.100.0/24|203.0.113.0/24|224.0.0.0/4|240.0.0.0/4|255.255.255.255/32 + ; + ; You can also prevent all user script outgoing calls with the following override in OpenSim.ini + ; + ; OutboundDisallowForUserScripts = 0.0.0.0/0 + ; + ; You can also disable the blacklist entirely with an empty entry + ; + ; OutboundDisallowForUserScripts = "" + ; What is reported as the "X-Secondlife-Shard" ; Defaults to the user server url if not set ; The old default is "OpenSim", set here for compatibility diff --git a/prebuild.xml b/prebuild.xml index 585f96deb0..55bba77f00 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -641,6 +641,7 @@ ../../../bin/ + @@ -655,6 +656,7 @@ +