/CoolEngine/IronPython/Src/IronPython.Modules/socket.cs
C# | 1715 lines | 1422 code | 172 blank | 121 comment | 186 complexity | 636155de9ac01bb0d7edad2af9e81902 MD5 | raw file
Large files files are truncated, but you can click here to view the full file
- /* ****************************************************************************
- *
- * Copyright (c) Microsoft Corporation.
- *
- * This source code is subject to terms and conditions of the Microsoft Public License. A
- * copy of the license can be found in the License.html file at the root of this distribution. If
- * you cannot locate the Microsoft Public License, please send an email to
- * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
- * by the terms of the Microsoft Public License.
- *
- * You must not remove this notice, or any other, from this software.
- *
- *
- * ***************************************************************************/
-
- #if !SILVERLIGHT // System.NET
-
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.IO;
- using System.Net;
- using System.Net.Sockets;
- using System.Runtime.InteropServices;
- using System.Text;
-
- using Microsoft.Scripting;
- using Microsoft.Scripting.Actions;
- using Microsoft.Scripting.Math;
- using Microsoft.Scripting.Runtime;
-
- using IronPython.Runtime;
- using IronPython.Runtime.Calls;
- using IronPython.Runtime.Types;
- using IronPython.Runtime.Operations;
- using IronPython.Runtime.Exceptions;
-
- using SpecialNameAttribute = System.Runtime.CompilerServices.SpecialNameAttribute;
-
- [assembly: PythonModule("socket", typeof(IronPython.Modules.PythonSocket))]
- namespace IronPython.Modules {
- public static class PythonSocket {
- private static readonly object _defaultTimeoutKey = new object();
- private static readonly object _defaultBufsizeKey = new object();
- private const int DefaultBufferSize = 8192;
-
- [SpecialName]
- public static void PerformModuleReload(PythonContext/*!*/ context, IAttributesCollection/*!*/ dict) {
- if (!context.HasModuleState(_defaultTimeoutKey)) {
- context.SetModuleState(_defaultTimeoutKey, null);
- }
-
- context.SetModuleState(_defaultBufsizeKey, DefaultBufferSize);
- }
-
- public const string __doc__ = "Implementation module for socket operations.\n\n"
- + "This module is a loose wrapper around the .NET System.Net.Sockets API, so you\n"
- + "may find the corresponding MSDN documentation helpful in decoding error\n"
- + "messages and understanding corner cases.\n"
- + "\n"
- + "This implementation of socket differs slightly from the standard CPython\n"
- + "socket module. Many of these differences are due to the implementation of the\n"
- + ".NET socket libraries. These differences are summarized below. For full\n"
- + "details, check the docstrings of the functions mentioned.\n"
- + " - s.accept(), s.connect(), and s.connect_ex() do not support timeouts.\n"
- + " - Timeouts in s.sendall() don't work correctly.\n"
- + " - s.dup() is not implemented.\n"
- + " - getservbyname() and getservbyport() are not implemented.\n"
- + " - SSL support is not implemented."
- + "\n"
- + "An Extra IronPython-specific function is exposed only if the clr module is\n"
- + "imported:\n"
- + " - s.HandleToSocket() returns the System.Net.Sockets.Socket object associated\n"
- + " with a particular \"file descriptor number\" (as returned by s.fileno()).\n"
- ;
-
- #region Socket object
-
- public static PythonType SocketType = DynamicHelpers.GetPythonTypeFromType(typeof(socket));
-
- [PythonSystemType]
- [Documentation("socket([family[, type[, proto]]]) -> socket object\n\n"
- + "Create a socket (a network connection endpoint) of the given family, type,\n"
- + "and protocol. socket() accepts keyword arguments.\n"
- + " - family (address family) defaults to AF_INET\n"
- + " - type (socket type) defaults to SOCK_STREAM\n"
- + " - proto (protocol type) defaults to 0, which specifies the default protocol\n"
- + "\n"
- + "This module supports only IP sockets. It does not support raw or Unix sockets.\n"
- + "Both IPv4 and IPv6 are supported.")]
- public class socket : IWeakReferenceable {
- #region Fields
-
- /// <summary>
- /// handleToSocket allows us to translate from Python's idea of a socket resource (file
- /// descriptor numbers) to .NET's idea of a socket resource (System.Net.Socket objects).
- /// In particular, this allows the select module to convert file numbers (as returned by
- /// fileno()) and convert them to Socket objects so that it can do something useful with them.
- /// </summary>
- private static readonly Dictionary<IntPtr, List<Socket>> handleToSocket = new Dictionary<IntPtr, List<Socket>>();
-
- private const int DefaultAddressFamily = (int)AddressFamily.InterNetwork;
- private const int DefaultSocketType = (int)System.Net.Sockets.SocketType.Stream;
- private const int DefaultProtocolType = (int)ProtocolType.Unspecified;
-
- internal Socket _socket;
- private WeakRefTracker weakRefTracker = null;
-
- #endregion
-
- #region Public API
-
- public socket(CodeContext/*!*/ context, [DefaultParameterValue(DefaultAddressFamily)] int addressFamily,
- [DefaultParameterValue(DefaultSocketType)] int socketType,
- [DefaultParameterValue(DefaultProtocolType)] int protocolType) {
-
- System.Net.Sockets.SocketType type = (System.Net.Sockets.SocketType)Enum.ToObject(typeof(System.Net.Sockets.SocketType), socketType);
- if (!Enum.IsDefined(typeof(System.Net.Sockets.SocketType), type)) {
- throw MakeException(new SocketException((int)SocketError.SocketNotSupported));
- }
- AddressFamily family = (AddressFamily)Enum.ToObject(typeof(AddressFamily), addressFamily);
- if (!Enum.IsDefined(typeof(AddressFamily), family)) {
- throw MakeException(new SocketException((int)SocketError.AddressFamilyNotSupported));
- }
- ProtocolType proto = (ProtocolType)Enum.ToObject(typeof(ProtocolType), protocolType);
- if (!Enum.IsDefined(typeof(ProtocolType), proto)) {
- throw MakeException(new SocketException((int)SocketError.ProtocolNotSupported));
- }
-
- Socket newSocket;
- try {
- newSocket = new Socket(family, type, proto);
- } catch (SocketException e) {
- throw MakeException(e);
- }
- Initialize(context, newSocket);
- }
-
- [Documentation("accept() -> (conn, address)\n\n"
- + "Accept a connection. The socket must be bound and listening before calling\n"
- + "accept(). conn is a new socket object connected to the remote host, and\n"
- + "address is the remote host's address (e.g. a (host, port) tuple for IPv4).\n"
- + "\n"
- + "Difference from CPython: accept() does not support timeouts in blocking mode.\n"
- + "If a timeout is set and the socket is in blocking mode, accept() will block\n"
- + "indefinitely until a connection is ready."
- )]
- public PythonTuple accept(CodeContext/*!*/ context) {
- socket wrappedRemoteSocket;
- Socket realRemoteSocket;
- try {
- realRemoteSocket = _socket.Accept();
- } catch (Exception e) {
- throw MakeException(e);
- }
- wrappedRemoteSocket = new socket(context, realRemoteSocket);
- return PythonTuple.MakeTuple(wrappedRemoteSocket, wrappedRemoteSocket.getpeername());
- }
-
- [Documentation("bind(address) -> None\n\n"
- + "Bind to an address. If the socket is already bound, socket.error is raised.\n"
- + "For IP sockets, address is a (host, port) tuple. Raw sockets are not\n"
- + "supported.\n"
- + "\n"
- + "If you do not care which local address is assigned, set host to INADDR_ANY and\n"
- + "the system will assign the most appropriate network address. Similarly, if you\n"
- + "set port to 0, the system will assign an available port number between 1024\n"
- + "and 5000."
- )]
- public void bind(PythonTuple address) {
- IPEndPoint localEP = TupleToEndPoint(address, _socket.AddressFamily);
- try {
- _socket.Bind(localEP);
- } catch (Exception e) {
- throw MakeException(e);
- }
- }
-
- [Documentation("close() -> None\n\nClose the socket. It cannot be used after being closed.")]
- public void close() {
- RemoveHandleSocketMapping(this);
- }
-
- internal static void RemoveHandleSocketMapping(socket socket) {
- lock (handleToSocket) {
- List<Socket> sockets;
- if (handleToSocket.TryGetValue((IntPtr)socket._socket.Handle, out sockets)) {
- for (int i = sockets.Count-1; i >= 0; i--) {
- if (sockets[i] == socket._socket) {
- sockets.RemoveAt(i);
- break;
- }
- }
- if (sockets.Count == 0) {
- handleToSocket.Remove(socket._socket.Handle);
- try {
- socket._socket.Close();
- } catch (Exception e) {
- throw MakeException(e);
- }
- }
- }
- }
- }
-
- [Documentation("connect(address) -> None\n\n"
- + "Connect to a remote socket at the given address. IP addresses are expressed\n"
- + "as (host, port).\n"
- + "\n"
- + "Raises socket.error if the socket has been closed, the socket is listening, or\n"
- + "another connection error occurred."
- + "\n"
- + "Difference from CPython: connect() does not support timeouts in blocking mode.\n"
- + "If a timeout is set and the socket is in blocking mode, connect() will block\n"
- + "indefinitely until a connection is made or an error occurs."
- )]
- public void connect(PythonTuple address) {
- IPEndPoint remoteEP = TupleToEndPoint(address, _socket.AddressFamily);
- try {
- _socket.Connect(remoteEP);
- } catch (Exception e) {
- throw MakeException(e);
- }
- }
-
- [Documentation("connect_ex(address) -> error_code\n\n"
- + "Like connect(), but return an error code insted of raising an exception for\n"
- + "socket exceptions raised by the underlying system Connect() call. Note that\n"
- + "exceptions other than SocketException generated by the system Connect() call\n"
- + "will still be raised.\n"
- + "\n"
- + "A return value of 0 indicates that the connect call was successful."
- + "\n"
- + "Difference from CPython: connect_ex() does not support timeouts in blocking\n"
- + "mode. If a timeout is set and the socket is in blocking mode, connect_ex() will\n"
- + "block indefinitely until a connection is made or an error occurs."
- )]
- public int connect_ex(PythonTuple address) {
- IPEndPoint remoteEP = TupleToEndPoint(address, _socket.AddressFamily);
- try {
- _socket.Connect(remoteEP);
- } catch (SocketException e) {
- return e.ErrorCode;
- }
- return (int)SocketError.Success;
- }
-
- [Documentation("fileno() -> file_handle\n\n"
- + "Return the underlying system handle for this socket (a 64-bit integer)."
- )]
- public Int64 fileno() {
- try {
- return _socket.Handle.ToInt64();
- } catch (Exception e) {
- throw MakeException(e);
- }
- }
-
- [Documentation("getpeername() -> address\n\n"
- + "Return the address of the remote end of this socket. The address format is\n"
- + "family-dependent (e.g. a (host, port) tuple for IPv4)."
- )]
- public PythonTuple getpeername() {
- try {
- IPEndPoint remoteEP = _socket.RemoteEndPoint as IPEndPoint;
- if (remoteEP == null) {
- throw MakeException(new SocketException((int)SocketError.AddressFamilyNotSupported));
- }
- return EndPointToTuple(remoteEP);
- } catch (Exception e) {
- throw MakeException(e);
- }
- }
-
- [Documentation("getsockname() -> address\n\n"
- + "Return the address of the local end of this socket. The address format is\n"
- + "family-dependent (e.g. a (host, port) tuple for IPv4)."
- )]
- public PythonTuple getsockname() {
- try {
- IPEndPoint localEP = _socket.LocalEndPoint as IPEndPoint;
- if (localEP == null) {
- throw MakeException(new SocketException((int)SocketError.InvalidArgument));
- }
- return EndPointToTuple(localEP);
- } catch (Exception e) {
- throw MakeException(e);
- }
- }
-
- [Documentation("getsockopt(level, optname[, buflen]) -> value\n\n"
- + "Return the value of a socket option. level is one of the SOL_* constants\n"
- + "defined in this module, and optname is one of the SO_* constants. If buflen is\n"
- + "omitted or zero, an integer value is returned. If it is present, a byte string\n"
- + "whose maximum length is buflen bytes) is returned. The caller must the decode\n"
- + "the resulting byte string."
- )]
- public object getsockopt(int optionLevel, int optionName, [DefaultParameterValue(0)] int optionLength) {
- SocketOptionLevel level = (SocketOptionLevel)Enum.ToObject(typeof(SocketOptionLevel), optionLevel);
- if (!Enum.IsDefined(typeof(SocketOptionLevel), level)) {
- throw MakeException(new SocketException((int)SocketError.InvalidArgument));
- }
- SocketOptionName name = (SocketOptionName)Enum.ToObject(typeof(SocketOptionName), optionName);
- if (!Enum.IsDefined(typeof(SocketOptionName), name)) {
- throw MakeException(new SocketException((int)SocketError.ProtocolOption));
- }
-
- try {
- if (optionLength == 0) {
- // Integer return value
- return (int)_socket.GetSocketOption(level, name);
- } else {
- // Byte string return value
- return StringOps.FromByteArray(_socket.GetSocketOption(level, name, optionLength));
- }
- } catch (Exception e) {
- throw MakeException(e);
- }
- }
-
- [Documentation("listen(backlog) -> None\n\n"
- + "Listen for connections on the socket. Backlog is the maximum length of the\n"
- + "pending connections queue. The maximum value is system-dependent."
- )]
- public void listen(int backlog) {
- try {
- _socket.Listen(backlog);
- } catch (Exception e) {
- throw MakeException(e);
- }
- }
-
- [Documentation("makefile([mode[, bufsize]]) -> file object\n\n"
- + "Return a regular file object corresponding to the socket. The mode\n"
- + "and bufsize arguments are as for the built-in open() function.")]
- public PythonFile makefile(CodeContext/*!*/ context, [DefaultParameterValue("r")]string mode, [DefaultParameterValue(8192)]int bufSize) {
- AddHandleMapping(this); // dup our handle
- return new _fileobject(context, this, mode, bufSize);
- }
-
- [Documentation("recv(bufsize[, flags]) -> string\n\n"
- + "Receive data from the socket, up to bufsize bytes. For connection-oriented\n"
- + "protocols (e.g. SOCK_STREAM), you must first call either connect() or\n"
- + "accept(). Connectionless protocols (e.g. SOCK_DGRAM) may also use recvfrom().\n"
- + "\n"
- + "recv() blocks until data is available, unless a timeout was set using\n"
- + "settimeout(). If the timeout was exceeded, socket.timeout is raised."
- + "recv() returns immediately with zero bytes when the connection is closed."
- )]
- public string recv(int maxBytes, [DefaultParameterValue(0)] int flags) {
- int bytesRead;
- byte[] buffer = new byte[maxBytes];
- try {
- bytesRead = _socket.Receive(buffer, (SocketFlags)flags);
- } catch (Exception e) {
- throw MakeException(e);
- }
- return StringOps.FromByteArray(buffer, bytesRead);
- }
-
- [Documentation("recvfrom(bufsize[, flags]) -> (string, address)\n\n"
- + "Receive data from the socket, up to bufsize bytes. string is the data\n"
- + "received, and address (whose format is protocol-dependent) is the address of\n"
- + "the socket from which the data was received."
- )]
- public PythonTuple recvfrom(int maxBytes, [DefaultParameterValue(0)] int flags) {
- int bytesRead;
- byte[] buffer = new byte[maxBytes];
- IPEndPoint remoteIPEP = new IPEndPoint(IPAddress.Any, 0);
- EndPoint remoteEP = remoteIPEP;
- try {
- bytesRead = _socket.ReceiveFrom(buffer, (SocketFlags)flags, ref remoteEP);
- } catch (Exception e) {
- throw MakeException(e);
- }
- string data = StringOps.FromByteArray(buffer, bytesRead);
- PythonTuple remoteAddress = EndPointToTuple((IPEndPoint)remoteEP);
- return PythonTuple.MakeTuple(data, remoteAddress);
- }
-
- [Documentation("send(string[, flags]) -> bytes_sent\n\n"
- + "Send data to the remote socket. The socket must be connected to a remote\n"
- + "socket (by calling either connect() or accept(). Returns the number of bytes\n"
- + "sent to the remote socket.\n"
- + "\n"
- + "Note that the successful completion of a send() call does not mean that all of\n"
- + "the data was sent. The caller must keep track of the number of bytes sent and\n"
- + "retry the operation until all of the data has been sent.\n"
- + "\n"
- + "Also note that there is no guarantee that the data you send will appear on the\n"
- + "network immediately. To increase network efficiency, the underlying system may\n"
- + "delay transmission until a significant amount of outgoing data is collected. A\n"
- + "successful completion of the Send method means that the underlying system has\n"
- + "had room to buffer your data for a network send"
- )]
- public int send(string data, [DefaultParameterValue(0)] int flags) {
- byte[] buffer = StringOps.ToByteArray(data);
- try {
- return _socket.Send(buffer, (SocketFlags)flags);
- } catch (Exception e) {
- throw MakeException(e);
- }
- }
-
- [Documentation("sendall(string[, flags]) -> None\n\n"
- + "Send data to the remote socket. The socket must be connected to a remote\n"
- + "socket (by calling either connect() or accept().\n"
- + "\n"
- + "Unlike send(), sendall() blocks until all of the data has been sent or until a\n"
- + "timeout or an error occurs. None is returned on success. If an error occurs,\n"
- + "there is no way to tell how much data, if any, was sent.\n"
- + "\n"
- + "Difference from CPython: timeouts do not function as you would expect. The\n"
- + "function is implemented using multiple calls to send(), so the timeout timer\n"
- + "is reset after each of those calls. That means that the upper bound on the\n"
- + "time that it will take for sendall() to return is the number of bytes in\n"
- + "string times the timeout interval.\n"
- + "\n"
- + "Also note that there is no guarantee that the data you send will appear on the\n"
- + "network immediately. To increase network efficiency, the underlying system may\n"
- + "delay transmission until a significant amount of outgoing data is collected. A\n"
- + "successful completion of the Send method means that the underlying system has\n"
- + "had room to buffer your data for a network send"
- )]
- public void sendall(string data, [DefaultParameterValue(0)] int flags) {
- byte[] buffer = StringOps.ToByteArray(data);
- try {
- int bytesTotal = buffer.Length;
- int bytesRemaining = bytesTotal;
- while (bytesRemaining > 0) {
- bytesRemaining -= _socket.Send(buffer, bytesTotal - bytesRemaining, bytesRemaining, (SocketFlags)flags);
- }
- } catch (Exception e) {
- throw MakeException(e);
- }
- }
-
- [Documentation("sendto(string[, flags], address) -> bytes_sent\n\n"
- + "Send data to the remote socket. The socket does not need to be connected to a\n"
- + "remote socket since the address is specified in the call to sendto(). Returns\n"
- + "the number of bytes sent to the remote socket.\n"
- + "\n"
- + "Blocking sockets will block until the all of the bytes in the buffer are sent.\n"
- + "Since a nonblocking Socket completes immediately, it might not send all of the\n"
- + "bytes in the buffer. It is your application's responsibility to keep track of\n"
- + "the number of bytes sent and to retry the operation until the application sends\n"
- + "all of the bytes in the buffer.\n"
- + "\n"
- + "Note that there is no guarantee that the data you send will appear on the\n"
- + "network immediately. To increase network efficiency, the underlying system may\n"
- + "delay transmission until a significant amount of outgoing data is collected. A\n"
- + "successful completion of the Send method means that the underlying system has\n"
- + "had room to buffer your data for a network send"
- )]
- public int sendto(string data, int flags, PythonTuple address) {
- byte[] buffer = StringOps.ToByteArray(data);
- EndPoint remoteEP = TupleToEndPoint(address, _socket.AddressFamily);
- try {
- return _socket.SendTo(buffer, (SocketFlags)flags, remoteEP);
- } catch (Exception e) {
- throw MakeException(e);
- }
- }
-
- [Documentation("")]
- public int sendto(string data, PythonTuple address) {
- return sendto(data, 0, address);
- }
-
- [Documentation("setblocking(flag) -> None\n\n"
- + "Set the blocking mode of the socket. If flag is 0, the socket will be set to\n"
- + "non-blocking mode; otherwise, it will be set to blocking mode. If the socket is\n"
- + "in blocking mode, and a method is called (such as send() or recv() which does\n"
- + "not complete immediately, the caller will block execution until the requested\n"
- + "operation completes. In non-blocking mode, a socket.timeout exception would\n"
- + "would be raised in this case.\n"
- + "\n"
- + "Note that changing blocking mode also affects the timeout setting:\n"
- + "setblocking(0) is equivalent to settimeout(0), and setblocking(1) is equivalent\n"
- + "to settimeout(None)."
- )]
- public void setblocking(int shouldBlock) {
- if (shouldBlock == 0) {
- settimeout(0);
- } else {
- settimeout(null);
- }
- }
-
- [Documentation("settimeout(value) -> None\n\n"
- + "Set a timeout on blocking socket methods. value may be either None or a\n"
- + "non-negative float, with one of the following meanings:\n"
- + " - None: disable timeouts and block indefinitely"
- + " - 0.0: don't block at all (return immediately if the operation can be\n"
- + " completed; raise socket.error otherwise)\n"
- + " - float > 0.0: block for up to the specified number of seconds; raise\n"
- + " socket.timeout if the operation cannot be completed in time\n"
- + "\n"
- + "settimeout(None) is equivalent to setblocking(1), and settimeout(0.0) is\n"
- + "equivalent to setblocking(0)."
- + "\n"
- + "If the timeout is non-zero and is less than 0.5, it will be set to 0.5. This\n"
- + "limitation is specific to IronPython.\n"
- )]
- public void settimeout(object timeout) {
- try {
- if (timeout == null) {
- _socket.Blocking = true;
- _socket.SendTimeout = 0;
- } else {
- double seconds;
- seconds = Converter.ConvertToDouble(timeout);
- if (seconds < 0) {
- throw PythonOps.TypeError("a non-negative float is required");
- }
- _socket.Blocking = seconds > 0; // 0 timeout means non-blocking mode
- _socket.SendTimeout = (int)(seconds * MillisecondsPerSecond);
- }
- } finally {
- _socket.ReceiveTimeout = _socket.SendTimeout;
- }
- }
-
- [Documentation("gettimeout() -> value\n\n"
- + "Return the timeout duration in seconds for this socket as a float. If no\n"
- + "timeout is set, return None. For more details on timeouts and blocking, see the\n"
- + "Python socket module documentation."
- )]
- public object gettimeout() {
- try {
- if (_socket.Blocking && _socket.SendTimeout == 0) {
- return null;
- } else {
- return (double)_socket.SendTimeout / MillisecondsPerSecond;
- }
- } catch (Exception e) {
- throw MakeException(e);
- }
- }
-
- [Documentation("setsockopt(level, optname[, value]) -> None\n\n"
- + "Set the value of a socket option. level is one of the SOL_* constants defined\n"
- + "in this module, and optname is one of the SO_* constants. value may be either\n"
- + "an integer or a string containing a binary structure. The caller is responsible\n"
- + "for properly encoding the byte string."
- )]
- public void setsockopt(int optionLevel, int optionName, object value) {
- SocketOptionLevel level = (SocketOptionLevel)Enum.ToObject(typeof(SocketOptionLevel), optionLevel);
- if (!Enum.IsDefined(typeof(SocketOptionLevel), level)) {
- throw MakeException(new SocketException((int)SocketError.InvalidArgument));
- }
- SocketOptionName name = (SocketOptionName)Enum.ToObject(typeof(SocketOptionName), optionName);
- if (!Enum.IsDefined(typeof(SocketOptionName), name)) {
- throw MakeException(new SocketException((int)SocketError.ProtocolOption));
- }
-
- try {
- int intValue;
- if (Converter.TryConvertToInt32(value, out intValue)) {
- _socket.SetSocketOption(level, name, intValue);
- return;
- }
-
- string strValue;
- if (Converter.TryConvertToString(value, out strValue)) {
- _socket.SetSocketOption(level, name, StringOps.ToByteArray(strValue));
- return;
- }
- } catch (Exception e) {
- throw MakeException(e);
- }
-
- throw PythonOps.TypeError("setsockopt() argument 3 must be int or string");
- }
-
- [Documentation("shutdown() -> None\n\n"
- + "Return the timeout duration in seconds for this socket as a float. If no\n"
- + "timeout is set, return None. For more details on timeouts and blocking, see the\n"
- + "Python socket module documentation."
- )]
- public void shutdown(int how) {
- SocketShutdown howValue = (SocketShutdown)Enum.ToObject(typeof(SocketShutdown), how);
- if (!Enum.IsDefined(typeof(SocketShutdown), howValue)) {
- throw MakeException(new SocketException((int)SocketError.InvalidArgument));
- }
- try {
- _socket.Shutdown(howValue);
- } catch (Exception e) {
- throw MakeException(e);
- }
- }
-
- public override string ToString() {
- try {
- return "<socket object, fd=" + fileno().ToString()
- + ", family=" + ((int)_socket.AddressFamily).ToString()
- + ", type=" + ((int)_socket.SocketType).ToString()
- + ", protocol=" + ((int)_socket.ProtocolType).ToString()
- + ">"
- ;
- } catch {
- return "<socket object, fd=?, family=?, type=, protocol=>";
- }
- }
-
- /// <summary>
- /// Return the internal System.Net.Sockets.Socket socket object associated with the given
- /// handle (as returned by GetHandle()), or null if no corresponding socket exists. This is
- /// primarily intended to be used by other modules (such as select) that implement
- /// networking primitives. User code should not normally need to call this function.
- /// </summary>
- internal static Socket HandleToSocket(Int64 handle) {
- List<Socket> sockets;
- lock (handleToSocket) {
- if (handleToSocket.TryGetValue((IntPtr)handle, out sockets)) {
- return sockets[sockets.Count - 1];
- }
- }
- return null;
- }
-
- #endregion
-
- #region IWeakReferenceable Implementation
-
- WeakRefTracker IWeakReferenceable.GetWeakRef() {
- return weakRefTracker;
- }
-
- bool IWeakReferenceable.SetWeakRef(WeakRefTracker value) {
- weakRefTracker = value;
- return true;
- }
-
- void IWeakReferenceable.SetFinalizer(WeakRefTracker value) {
- weakRefTracker = value;
- }
-
- #endregion
-
- #region Private Implementation
-
- /// <summary>
- /// Create a Python socket object from an existing .NET socket object
- /// (like one returned from Socket.Accept())
- /// </summary>
- private socket(CodeContext/*!*/ context, Socket socket) {
- Initialize(context, socket);
- }
-
- /// <summary>
- /// Perform initialization common to all constructors
- /// </summary>
- private void Initialize(CodeContext/*!*/ context, Socket socket) {
- this._socket = socket;
- int? defaultTimeout = GetDefaultTimeout(context);
- if (defaultTimeout == null) {
- settimeout(null);
- } else {
- settimeout((double)defaultTimeout / MillisecondsPerSecond);
- }
- AddHandleMapping(this);
- }
-
- private static void AddHandleMapping(socket socket) {
- lock (handleToSocket) {
- if (!handleToSocket.ContainsKey(socket._socket.Handle)) {
- handleToSocket[socket._socket.Handle] = new List<Socket>(1);
- }
- handleToSocket[socket._socket.Handle].Add(socket._socket);
- }
- }
-
- #endregion
- }
-
- #endregion
-
- #region Fields
-
- public static PythonType error = PythonExceptions.CreateSubType(PythonExceptions.Exception, "error", "socket", "");
- public static PythonType herror = PythonExceptions.CreateSubType(error, "herror", "socket", "");
- public static PythonType gaierror = PythonExceptions.CreateSubType(error, "gaierror", "socket", "");
- public static PythonType timeout = PythonExceptions.CreateSubType(error, "timeout", "socket", "");
-
- private const string AnyAddrToken = "";
- private const string BroadcastAddrToken = "<broadcast>";
- private const string LocalhostAddrToken = "";
- private const int IPv4AddrBytes = 4;
- private const int IPv6AddrBytes = 16;
- private const double MillisecondsPerSecond = 1000.0;
-
- #endregion
-
- #region Public API
-
- [Documentation("")]
- public static List getaddrinfo(
- string host,
- object port,
- [DefaultParameterValue((int)AddressFamily.Unspecified)] int family,
- [DefaultParameterValue(0)] int socktype,
- [DefaultParameterValue((int)ProtocolType.IP)] int proto,
- [DefaultParameterValue((int)SocketFlags.None)] int flags
- ) {
- int numericPort;
-
- if (port == null) {
- numericPort = 0;
- } else if (port is int) {
- numericPort = (int)port;
- } else if (port is Extensible<int>) {
- numericPort = ((Extensible<int>)port).Value;
- } else if (port is string) {
- if (!Int32.TryParse((string)port, out numericPort)) {
- // TODO: also should consult GetServiceByName
- throw PythonExceptions.CreateThrowable(gaierror, "getaddrinfo failed");
- }
- } else if (port is ExtensibleString) {
- if (!Int32.TryParse(((ExtensibleString)port).Value, out numericPort)) {
- // TODO: also should consult GetServiceByName
- throw PythonExceptions.CreateThrowable(gaierror, "getaddrinfo failed");
- }
- } else {
- throw PythonExceptions.CreateThrowable(gaierror, "getaddrinfo failed");
- }
-
- if (socktype != 0) {
- // we just use this to validate; socketType isn't actually used
- System.Net.Sockets.SocketType socketType = (System.Net.Sockets.SocketType)Enum.ToObject(typeof(System.Net.Sockets.SocketType), socktype);
- if (socketType == System.Net.Sockets.SocketType.Unknown || !Enum.IsDefined(typeof(System.Net.Sockets.SocketType), socketType)) {
- throw PythonExceptions.CreateThrowable(gaierror, PythonTuple.MakeTuple((int)SocketError.SocketNotSupported, "getaddrinfo failed"));
- }
- }
-
- AddressFamily addressFamily = (AddressFamily)Enum.ToObject(typeof(AddressFamily), family);
- if (!Enum.IsDefined(typeof(AddressFamily), addressFamily)) {
- throw PythonExceptions.CreateThrowable(gaierror, PythonTuple.MakeTuple((int)SocketError.AddressFamilyNotSupported, "getaddrinfo failed"));
- }
-
- // Again, we just validate, but don't actually use protocolType
- ProtocolType protocolType = (ProtocolType)Enum.ToObject(typeof(ProtocolType), proto);
-
- IPAddress[] ips = HostToAddresses(host, addressFamily);
-
- List results = new List();
-
- foreach (IPAddress ip in ips) {
- results.append(PythonTuple.MakeTuple(
- (int)ip.AddressFamily,
- socktype,
- proto,
- "",
- EndPointToTuple(new IPEndPoint(ip, numericPort))
- ));
- }
-
- return results;
- }
-
- [Documentation("getfqdn([hostname_or_ip]) -> hostname\n\n"
- + "Return the fully-qualified domain name for the specified hostname or IP\n"
- + "address. An unspecified or empty name is interpreted as the local host. If the\n"
- + "name lookup fails, the passed-in name is returned as-is."
- )]
- public static string getfqdn(string host) {
- host = host.Trim();
- if (host == BroadcastAddrToken) {
- return host;
- }
- try {
- IPHostEntry hostEntry = Dns.GetHostEntry(host);
- if (hostEntry.HostName.Contains(".")) {
- return hostEntry.HostName;
- } else {
- foreach (string addr in hostEntry.Aliases) {
- if (addr.Contains(".")) {
- return addr;
- }
- }
- }
- } catch (SocketException) {
- // ignore and return host below
- }
- // seems to match CPython behavior, although docs say gethostname() should be returned
- return host;
- }
-
- [Documentation("")]
- public static string getfqdn() {
- return getfqdn(LocalhostAddrToken);
- }
-
- [Documentation("gethostbyname(hostname) -> ip address\n\n"
- + "Return the string IPv4 address associated with the given hostname (e.g.\n"
- + "'10.10.0.1'). The hostname is returned as-is if it an IPv4 address. The empty\n"
- + "string is treated as the local host.\n"
- + "\n"
- + "gethostbyname() doesn't support IPv6; for IPv4/IPv6 support, use getaddrinfo()."
- )]
- public static string gethostbyname(string host) {
- return HostToAddress(host, AddressFamily.InterNetwork).ToString();
- }
-
- [Documentation("gethostbyname_ex(hostname) -> (hostname, aliases, ip_addresses)\n\n"
- + "Return the real host name, a list of aliases, and a list of IP addresses\n"
- + "associated with the given hostname. If the hostname is an IPv4 address, the\n"
- + "tuple ([hostname, [], [hostname]) is returned without doing a DNS lookup.\n"
- + "\n"
- + "gethostbyname_ex() doesn't support IPv6; for IPv4/IPv6 support, use\n"
- + "getaddrinfo()."
- )]
- public static PythonTuple gethostbyname_ex(string host) {
- string hostname;
- List aliases;
- List ips = PythonOps.MakeList();
-
- IPAddress addr;
- if (IPAddress.TryParse(host, out addr)) {
- if (AddressFamily.InterNetwork == addr.AddressFamily) {
- hostname = host;
- aliases = PythonOps.MakeEmptyList(0);
- ips.append(host);
- } else {
- throw PythonExceptions.CreateThrowable(gaierror, (int)SocketError.HostNotFound, "no IPv4 addresses associated with host");
- }
- } else {
- IPHostEntry hostEntry;
- try {
- hostEntry = Dns.GetHostEntry(host);
- } catch (SocketException e) {
- throw PythonExceptions.CreateThrowable(gaierror, e.ErrorCode, "no IPv4 addresses associated with host");
- }
- hostname = hostEntry.HostName;
- aliases = PythonOps.MakeList(hostEntry.Aliases);
- foreach (IPAddress ip in hostEntry.AddressList) {
- if (AddressFamily.InterNetwork == ip.AddressFamily) {
- ips.append(ip.ToString());
- }
- }
- }
-
- return PythonTuple.MakeTuple(hostname, aliases, ips);
- }
-
- [Documentation("gethostname() -> hostname\nReturn this machine's hostname")]
- public static string gethostname() {
- return Dns.GetHostName();
- }
-
- [Documentation("gethostbyaddr(host) -> (hostname, aliases, ipaddrs)\n\n"
- + "Return a tuple of (primary hostname, alias hostnames, ip addresses). host may\n"
- + "be either a hostname or an IP address."
- )]
- public static object gethostbyaddr(string host) {
- if (host == "") {
- host = gethostname();
- }
- // This conversion seems to match CPython behavior
- host = gethostbyname(host);
-
- IPAddress[] ips = null;
- IPHostEntry hostEntry = null;
- try {
- ips = Dns.GetHostAddresses(host);
- hostEntry = Dns.GetHostEntry(host);
- } catch (Exception e) {
- throw MakeException(e);
- }
-
- List ipStrings = PythonOps.MakeList();
- foreach (IPAddress ip in ips) {
- ipStrings.append(ip.ToString());
- }
-
- return PythonTuple.MakeTuple(hostEntry.HostName, PythonOps.MakeList(hostEntry.Aliases), ipStrings);
- }
-
- [Documentation("getnameinfo(socketaddr, flags) -> (host, port)\n"
- + "Given a socket address, the return a tuple of the corresponding hostname and\n"
- + "port. Available flags:\n"
- + " - NI_NOFQDN: Return only the hostname part of the domain name for hosts on the\n"
- + " same domain as the executing machine.\n"
- + " - NI_NUMERICHOST: return the numeric form of the host (e.g. '127.0.0.1' or\n"
- + " '::1' rather than 'localhost').\n"
- + " - NI_NAMEREQD: Raise an error if the hostname cannot be looked up.\n"
- + " - NI_NUMERICSERV: Return string containing the numeric form of the port (e.g.\n"
- + " '80' rather than 'http'). This flag is required (see below).\n"
- + " - NI_DGRAM: Silently ignored (see below).\n"
- + "\n"
- + "Difference from CPython: the following flag behavior differs from CPython\n"
- + "because the .NET framework libraries offer no name-to-port conversion APIs:\n"
- + " - NI_NUMERICSERV: This flag is required because the .NET framework libraries\n"
- + " offer no port-to-name mapping APIs. If it is omitted, getnameinfo() will\n"
- + " raise a NotImplementedError.\n"
- + " - The NI_DGRAM flag is ignored because it only applies when NI_NUMERICSERV is\n"
- + " omitted. It it were supported, it would return the UDP-based port name\n"
- + " rather than the TCP-based port name.\n"
- )]
- public static object getnameinfo(PythonTuple socketAddr, int flags) {
- if (socketAddr.__len__() < 2 || socketAddr.__len__() > 4) {
- throw PythonOps.TypeError("socket address must be a 2-tuple (IPv4 or IPv6) or 4-tuple (IPv6)");
- }
-
- if ((flags & (int)NI_NUMERICSERV) == 0) {
- throw PythonOps.NotImplementedError("getnameinfo() required the NI_NUMERICSERV flag (see docstring)");
- }
-
- string host = Converter.ConvertToString(socketAddr[0]);
- if (host == null) throw PythonOps.TypeError("argument 1 must be string");
- int port = 0;
- try {
- port = (int)socketAddr[1];
- } catch (InvalidCastException) {
- throw PythonOps.TypeError("an integer is required");
- }
-
- string resultHost = null;
- string resultPort = null;
-
- // Host
- IPHostEntry hostEntry = null;
- try {
- // Do double lookup to force reverse DNS lookup to match CPython behavior
- hostEntry = Dns.GetHostEntry(host);
- if (hostEntry.AddressList.Length < 1) {
- throw PythonExceptions.CreateThrowable(error, "sockaddr resolved to zero addresses");
- }
- hostEntry = Dns.GetHostEntry(hostEntry.AddressList[0]);
- } catch (SocketException e) {
- throw PythonExceptions.CreateThrowable(gaierror, e.ErrorCode, e.Message);
- } catch (IndexOutOfRangeException) {
- throw PythonExceptions.CreateThrowable(gaierror, "sockaddr resolved to zero addresses");
- }
-
- IList<IPAddress> addrs = hostEntry.AddressList;
- if (addrs.Count > 1) {
- // ignore non-IPV4 addresses
- List<IPAddress> newAddrs = new List<IPAddress>(addrs.Count);
- foreach (IPAddress addr in hostEntry.AddressList) {
- if (addr.AddressFamily == AddressFamily.InterNetwork) {
- newAddrs.Add(addr);
- }
- }
- if (newAddrs.Count > 1) {
- throw PythonExceptions.CreateThrowable(error, "sockaddr resolved to multiple addresses");
- }
- addrs = newAddrs;
- }
-
- if (addrs.Count < 1) {
- throw PythonExceptions.CreateThrowable(error, "sockaddr resolved to zero addresses");
- }
-
- if ((flags & (int)NI_NUMERICHOST) != 0) {
- resultHost = addrs[0].ToString();
- } else if ((flags & (int)NI_NOFQDN) != 0) {
- resultHost = RemoveLocalDomain(hostEntry.HostName);
- } else {
- resultHost = hostEntry.HostName;
- }
-
- // Port
- // We don't branch on NI_NUMERICSERV here since we throw above if it's not set
- resultPort = port.ToString();
-
- return PythonTuple.MakeTuple(resultHost, resultPort);
- }
-
- [Documentation("getprotobyname(protoname) -> integer proto\n\n"
- + "Given a string protocol name (e.g. \"udp\"), return the associated integer\n"
- + "protocol number, suitable for passing to socket(). The name is case\n"
- + "insensitive.\n"
- + "\n"
- + "Raises socket.error if no protocol number can be found."
- )]
- public static object getprotobyname(string protocolName) {
- switch (protocolName.ToLower()) {
- case "ah": return IPPROTO_AH;
- case "esp": return IPPROTO_ESP;
- case "dstopts": return IPPROTO_DSTOPTS;
- case "fragment": return IPPROTO_FRAGMENT;
- case "ggp": return IPPROTO_GGP;
- case "icmp": return IPPROTO_ICMP;
- case "icmpv6": return IPPROTO_ICMPV6;
- case "ip": r…
Large files files are truncated, but you can click here to view the full file