Created
December 9, 2014 16:37
-
-
Save ironpythonbot/b9c46e37bfa854cbb720 to your computer and use it in GitHub Desktop.
CodePlex Issue #5814 Plain Text Attachments
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* ********************************************************************************** | |
* | |
* Copyright (c) Microsoft Corporation. All rights reserved. | |
* | |
* This source code is subject to terms and conditions of the Shared Source License | |
* for IronPython. A copy of the license can be found in the License.html file | |
* at the root of this distribution. If you can not locate the Shared Source License | |
* for IronPython, 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 Shared Source License for IronPython. | |
* | |
* You must not remove this notice, or any other, from this software. | |
* | |
* **********************************************************************************/ | |
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Diagnostics; | |
using System.Net; | |
using System.Net.Sockets; | |
using System.Net.Security; | |
using System.Runtime.InteropServices; | |
using System.Runtime.Serialization; | |
using System.Text; | |
using System.Threading; | |
using IronPython.Runtime; | |
using IronPython.Runtime.Calls; | |
using IronPython.Runtime.Types; | |
using IronPython.Runtime.Operations; | |
using IronPython.Runtime.Exceptions; | |
[assembly: PythonModule("socket", typeof(IronPython.Modules.PythonSocket))] | |
namespace IronPython.Modules { | |
[PythonType("socket")] | |
public static class PythonSocket { | |
public static 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" | |
+ " - makefile() and s.dup() are 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 DynamicType socket = Ops.GetDynamicTypeFromType(typeof(SocketObj)); | |
public static DynamicType SocketType = socket; | |
[PythonType("socket")] | |
public class SocketObj : IWeakReferenceable { | |
public static string __doc__ = "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."; | |
#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 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; | |
private Socket socket; | |
private WeakRefTracker weakRefTracker = null; | |
#endregion | |
#region Public API | |
public SocketObj([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(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." | |
)] | |
[PythonName("accept")] | |
public Tuple Accept() { | |
SocketObj wrappedRemoteSocket; | |
Socket realRemoteSocket; | |
try { | |
realRemoteSocket = socket.Accept(); | |
} catch (Exception e) { | |
throw MakeException(e); | |
} | |
wrappedRemoteSocket = new SocketObj(realRemoteSocket); | |
return Tuple.MakeTuple(wrappedRemoteSocket, wrappedRemoteSocket.GetRemoteAddress()); | |
} | |
[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." | |
)] | |
[PythonName("bind")] | |
public void Bind(Tuple 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.")] | |
[PythonName("close")] | |
public void Close() { | |
lock (handleToSocket) { | |
List<Socket> sockets; | |
if (handleToSocket.TryGetValue((IntPtr)socket.Handle, out sockets)) { | |
if (sockets.Contains(socket)) { | |
sockets.Remove(socket); | |
} | |
if (sockets.Count == 0) { | |
handleToSocket.Remove(socket.Handle); | |
} | |
} | |
} | |
try { | |
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." | |
)] | |
[PythonName("connect")] | |
public void Connect(Tuple 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." | |
)] | |
[PythonName("connect_ex")] | |
public int ConnectEx(Tuple 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)." | |
)] | |
[PythonName("fileno")] | |
public Int64 GetHandle() { | |
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)." | |
)] | |
[PythonName("getpeername")] | |
public Tuple GetRemoteAddress() { | |
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)." | |
)] | |
[PythonName("getsockname")] | |
public Tuple GetLocalAddress() { | |
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." | |
)] | |
[PythonName("getsockopt")] | |
public object GetSocketOption(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." | |
)] | |
[PythonName("listen")] | |
public void Listen(int backlog) { | |
try { | |
socket.Listen(backlog); | |
} catch (Exception e) { | |
throw MakeException(e); | |
} | |
} | |
[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." | |
)] | |
[PythonName("recv")] | |
public string Receive(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." | |
)] | |
[PythonName("recvfrom")] | |
public Tuple ReceiveFrom(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); | |
remoteIPEP = (IPEndPoint) remoteEP; | |
Tuple remoteAddress = EndPointToTuple(remoteIPEP); | |
return Tuple.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" | |
)] | |
[PythonName("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" | |
)] | |
[PythonName("sendall")] | |
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" | |
)] | |
[PythonName("sendto")] | |
public int SendTo(string data, int flags, Tuple 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("")] | |
[PythonName("sendto")] | |
public int SendTo(string data, Tuple 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)." | |
)] | |
[PythonName("setblocking")] | |
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" | |
)] | |
[PythonName("settimeout")] | |
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 Ops.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." | |
)] | |
[PythonName("gettimeout")] | |
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." | |
)] | |
[PythonName("setsockopt")] | |
public void SetSocketOption(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 Ops.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." | |
)] | |
[PythonName("shutdown")] | |
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); | |
} | |
} | |
[PythonName("__repr__")] | |
public override string ToString() { | |
try { | |
return "<socket object, fd=" + GetHandle().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> | |
public static Socket HandleToSocket(Int64 handle) { | |
List<Socket> sockets; | |
lock (handleToSocket) { | |
if (handleToSocket.TryGetValue((IntPtr)handle, out sockets)) { | |
if (sockets.Count > 0) { | |
return sockets[0]; | |
} | |
} | |
} | |
return null; | |
} | |
#endregion | |
#region IWeakReferenceable Implementation | |
public WeakRefTracker GetWeakRef() { | |
return weakRefTracker; | |
} | |
public bool SetWeakRef(WeakRefTracker value) { | |
weakRefTracker = value; | |
return true; | |
} | |
public void 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 SocketObj(Socket socket) { | |
Initialize(socket); | |
} | |
/// <summary> | |
/// Perform initialization common to all constructors | |
/// </summary> | |
private void Initialize(Socket socket) { | |
this.socket = socket; | |
if (DefaultTimeout == null) { | |
SetTimeout(null); | |
} else { | |
SetTimeout((double)DefaultTimeout / MillisecondsPerSecond); | |
} | |
lock (handleToSocket) { | |
if (!handleToSocket.ContainsKey(socket.Handle)) { | |
handleToSocket[socket.Handle] = new List<Socket>(1); | |
} | |
if (!handleToSocket[socket.Handle].Contains(socket)) { | |
handleToSocket[socket.Handle].Add(socket); | |
} | |
} | |
} | |
#endregion | |
} | |
#endregion | |
#region Fields | |
public static IPythonType error = ExceptionConverter.CreatePythonException("error", "socket"); | |
public static IPythonType herror = ExceptionConverter.CreatePythonException("herror", "socket", error); | |
public static IPythonType gaierror = ExceptionConverter.CreatePythonException("gaierror", "socket", error); | |
public static IPythonType timeout = ExceptionConverter.CreatePythonException("timeout", "socket", error); | |
private static int? DefaultTimeout = null; // in milliseconds | |
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("")] | |
[PythonName("getaddrinfo")] | |
public static List GetAddrInfo( | |
string host, | |
object port, | |
[DefaultParameterValue((int)AddressFamily.InterNetwork)] int family, | |
[DefaultParameterValue(0)] int socktype, | |
[DefaultParameterValue((int)ProtocolType.IP)] int proto, | |
[DefaultParameterValue((int)SocketFlags.None)] int flags | |
) { | |
int numericPort; | |
/* Disabled because GetServiceByName is not implemented | |
string serviceName; | |
if (port == null) { | |
numericPort = 0; | |
} else if (Converter.TryConvertToInt32(port, out numericPort)) { | |
// nop | |
} else if (Converter.TryConvertToString(port, out serviceName)) { | |
numericPort = GetServiceByName(serviceName, null); | |
} | |
*/ | |
if (!Converter.TryConvertToInt32(port, out numericPort)) { | |
numericPort = 0; | |
} | |
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 (!Enum.IsDefined(typeof(System.Net.Sockets.SocketType), socketType)) { | |
throw MakeException(gaierror, Tuple.MakeTuple((int)SocketError.SocketNotSupported, "getaddrinfo failed")); | |
} | |
} | |
AddressFamily addressFamily = (AddressFamily)Enum.ToObject(typeof(AddressFamily), family); | |
if (!Enum.IsDefined(typeof(AddressFamily), addressFamily)) { | |
throw MakeException(gaierror, Tuple.MakeTuple((int)SocketError.AddressFamilyNotSupported, "getaddrinfo failed")); | |
} | |
if (addressFamily == AddressFamily.Unspecified) { | |
addressFamily = AddressFamily.InterNetwork; | |
} | |
// Again, we just validate, but don't actually use protocolType | |
ProtocolType protocolType = (ProtocolType)Enum.ToObject(typeof(ProtocolType), proto); | |
if (!Enum.IsDefined(typeof(ProtocolType), protocolType)) { | |
throw MakeException(gaierror, Tuple.MakeTuple((int)SocketError.ProtocolNotSupported, "getaddrinfo failed")); | |
} | |
IPAddress[] ips = HostToAddresses(host, addressFamily); | |
List results = new List(); | |
foreach (IPAddress ip in ips) { | |
results.Add(Tuple.MakeTuple( | |
(int)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." | |
)] | |
[PythonName("getfqdn")] | |
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("")] | |
[PythonName("getfqdn")] | |
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()." | |
)] | |
[PythonName("gethostbyname")] | |
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()." | |
)] | |
[PythonName("gethostbyname_ex")] | |
public static Tuple GetHostByNameEx(string host) { | |
string hostname; | |
List aliases; | |
List ips = List.Make(); | |
IPAddress addr; | |
if (IPAddress.TryParse(host, out addr)) { | |
if (AddressFamily.InterNetwork == addr.AddressFamily) { | |
hostname = host; | |
aliases = List.MakeEmptyList(0); | |
ips.Append(host); | |
} else { | |
throw MakeException(gaierror, (int)SocketError.HostNotFound, "no IPv4 addresses associated with host"); | |
} | |
} else { | |
IPHostEntry hostEntry; | |
try { | |
hostEntry = Dns.GetHostEntry(host); | |
} catch (SocketException e) { | |
throw MakeException(gaierror, e.ErrorCode, "no IPv4 addresses associated with host"); | |
} | |
hostname = hostEntry.HostName; | |
aliases = List.Make(hostEntry.Aliases); | |
foreach (IPAddress ip in hostEntry.AddressList) { | |
if (AddressFamily.InterNetwork == ip.AddressFamily) { | |
ips.Append(ip.ToString()); | |
} | |
} | |
} | |
return Tuple.MakeTuple(hostname, aliases, ips); | |
} | |
[Documentation("gethostname() -> hostname\nReturn this machine's hostname")] | |
[PythonName("gethostname")] | |
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." | |
)] | |
[PythonName("gethostbyaddr")] | |
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 = List.Make(); | |
foreach (IPAddress ip in ips) { | |
ipStrings.Append(ip.ToString()); | |
} | |
return Tuple.MakeTuple(hostEntry.HostName, List.Make(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" | |
)] | |
[PythonName("getnameinfo")] | |
public static object GetNameInfo(Tuple socketAddr, int flags) { | |
if (socketAddr.GetLength() < 2 || socketAddr.GetLength() > 4) { | |
throw Ops.TypeError("socket address must be a 2-tuple (IPv4 or IPv6) or 4-tuple (IPv6)"); | |
} | |
if ((flags & (int)NI_NUMERICSERV) == 0) { | |
throw Ops.NotImplementedError("getnameinfo() required the NI_NUMERICSERV flag (see docstring)"); | |
} | |
string host = Converter.ConvertToString(socketAddr[0]); | |
if (host == null) throw Ops.TypeError("argument 1 must be string"); | |
int port = 0; | |
try { | |
port = (int)socketAddr[1]; | |
} catch (InvalidCastException) { | |
throw Ops.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 MakeException(error, "sockaddr resolved to zero addresses"); | |
} | |
hostEntry = Dns.GetHostEntry(hostEntry.AddressList[0]); | |
} catch (SocketException e) { | |
throw MakeException(gaierror, e.ErrorCode, e.Message); | |
} catch (IndexOutOfRangeException) { | |
throw MakeException(gaierror, "sockaddr resolved to zero addresses"); | |
} | |
if (hostEntry.AddressList.Length > 1) { | |
throw MakeException(error, "sockaddr resolved to multiple addresses"); | |
} else if (hostEntry.AddressList.Length < 1) { | |
throw MakeException(error, "sockaddr resolved to zero addresses"); | |
} | |
if ((flags & (int)NI_NUMERICHOST) != 0) { | |
resultHost = hostEntry.AddressList[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 Tuple.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." | |
)] | |
[PythonName("getprotobyname")] | |
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": return IPPROTO_IP; | |
case "ipv4": return IPPROTO_IPV4; | |
case "ipv6": return IPPROTO_IPV6; | |
case "nd": return IPPROTO_ND; | |
case "none": return IPPROTO_NONE; | |
case "pup": return IPPROTO_PUP; | |
case "raw": return IPPROTO_RAW; | |
case "routing": return IPPROTO_ROUTING; | |
case "tcp": return IPPROTO_TCP; | |
case "udp": return IPPROTO_UDP; | |
default: | |
throw MakeException(error, "protocol not found"); | |
} | |
} | |
[Documentation("getservbyname(service_name[, protocol_name]) -> port\n\n" | |
+ "Not implemented." | |
//+ "Given a service name (e.g. 'domain') return the associated protocol number (e.g.\n" | |
//+ "53). The protocol name (if specified) must be either 'tcp' or 'udp'." | |
)] | |
[PythonName("getservbyname")] | |
public static int GetServiceByName(string serviceName, [DefaultParameterValue(null)] string protocolName) { | |
// !!! .NET networking libraries don't support this, so we don't either | |
throw Ops.NotImplementedError("name to service conversion not supported"); | |
} | |
[Documentation("getservbyport(port[, protocol_name]) -> service_name\n\n" | |
+ "Not implemented." | |
//+ "Given a port number (e.g. 53), return the associated protocol name (e.g.\n" | |
//+ "'domain'). The protocol name (if specified) must be either 'tcp' or 'udp'." | |
)] | |
[PythonName("getservbyport")] | |
public static string GetServiceByPort(int port, [DefaultParameterValue(null)] string protocolName) { | |
// !!! .NET networking libraries don't support this, so we don't either | |
throw Ops.NotImplementedError("service to name conversion not supported"); | |
} | |
[Documentation("ntohl(x) -> integer\n\nConvert a 32-bit integer from network byte order to host byte order.")] | |
[PythonName("ntohl")] | |
public static int NetworkToHostOrder32(object x) { | |
return IPAddress.NetworkToHostOrder(SignInsenstitiveToInt32(x)); | |
} | |
[Documentation("ntohs(x) -> integer\n\nConvert a 16-bit integer from network byte order to host byte order.")] | |
[PythonName("ntohs")] | |
public static short NetworkToHostOrder16(object x) { | |
return IPAddress.NetworkToHostOrder(SignInsenstitiveToInt16(x)); | |
} | |
[Documentation("htonl(x) -> integer\n\nConvert a 32bit integer from host byte order to network byte order.")] | |
[PythonName("htonl")] | |
public static int HostToNetworkOrder32(object x) { | |
return IPAddress.HostToNetworkOrder(SignInsenstitiveToInt32(x)); | |
} | |
/// <summary> | |
/// Convert an object to a 32-bit integer. This adds two features to Converter.ToInt32: | |
/// 1. Sign is ignored. For example, 0xffff0000 converts to 4294901760, where Convert.ToInt32 | |
/// would throw because 0xffff000 is less than zero. | |
/// 2. Overflow exceptions are thrown. Converter.ToInt32 throws TypeError if x is | |
/// an integer, but is bigger than 32 bits. Instead, we throw OverflowException. | |
/// </summary> | |
private static int SignInsenstitiveToInt32(object x) { | |
IronMath.BigInteger bigValue = Converter.ConvertToBigInteger(x); | |
try { | |
return bigValue.ToInt32(null); | |
} catch (OverflowException) { | |
return (int)bigValue.ToUInt32(null); | |
} | |
} | |
/// <summary> | |
/// Convert an object to a 16-bit integer. This adds two features to Converter.ToInt16: | |
/// 1. Sign is ignored. For example, 0xff00 converts to 65280, where Convert.ToInt16 | |
/// would throw because signed 0xff00 is -256. | |
/// 2. Overflow exceptions are thrown. Converter.ToInt16 throws TypeError if x is | |
/// an integer, but is bigger than 16 bits. Instead, we throw OverflowException. | |
/// </summary> | |
private static short SignInsenstitiveToInt16(object x) { | |
IronMath.BigInteger bigValue = Converter.ConvertToBigInteger(x); | |
try { | |
return bigValue.ToInt16(null); | |
} catch (OverflowException) { | |
return (short)bigValue.ToUInt16(null); | |
} | |
} | |
[Documentation("htons(x) -> integer\n\nConvert a 16-bit integer from host byte order to network byte order.")] | |
[PythonName("htons")] | |
public static short HostToNetworkOrder16(object x) { | |
return IPAddress.HostToNetworkOrder(SignInsenstitiveToInt16(x)); | |
} | |
[Documentation("inet_pton(addr_family, ip_string) -> packed_ip\n\n" | |
+ "Convert an IP address (in string format, e.g. '127.0.0.1' or '::1') to a 32-bit\n" | |
+ "packed binary format, as 4-byte (IPv4) or 16-byte (IPv6) string. The return\n" | |
+ "format matches the format of the standard C library's in_addr or in6_addr\n" | |
+ "struct.\n" | |
+ "\n" | |
+ "If the address format is invalid, socket.error will be raised. Validity is\n" | |
+ "determined by the .NET System.Net.IPAddress.Parse() method.\n" | |
+ "\n" | |
+ "inet_pton() supports IPv4 and IPv6." | |
)] | |
[PythonName("inet_pton")] | |
public static string IPStringToPackedBytes(int addressFamily, string ipString) { | |
if (addressFamily != (int)AddressFamily.InterNetwork && addressFamily != (int)AddressFamily.InterNetworkV6) { | |
throw MakeException(new SocketException((int)SocketError.AddressFamilyNotSupported)); | |
} | |
IPAddress ip; | |
try { | |
ip = IPAddress.Parse(ipString); | |
if (addressFamily != (int)ip.AddressFamily) { | |
throw MakeException(new SocketException((int)SocketError.AddressFamilyNotSupported)); | |
} | |
} catch (FormatException) { | |
throw MakeException(error, "illegal IP address passed to inet_pton"); | |
} | |
return StringOps.FromByteArray(ip.GetAddressBytes()); | |
} | |
[Documentation("inet_ntop(address_family, packed_ip) -> ip_string\n\n" | |
+ "Convert a packed IP address (a 4-byte [IPv4] or 16-byte [IPv6] string) to a\n" | |
+ "string IP address (e.g. '127.0.0.1' or '::1').\n" | |
+ "\n" | |
+ "The input format matches the format of the standard C library's in_addr or\n" | |
+ "in6_addr struct. If the input string is not exactly 4 bytes or 16 bytes,\n" | |
+ "socket.error will be raised.\n" | |
+ "\n" | |
+ "inet_ntop() supports IPv4 and IPv6." | |
)] | |
[PythonName("inet_ntop")] | |
public static string IPPackedBytesToString(int addressFamily, string packedIP) { | |
if (!( | |
(packedIP.Length == IPv4AddrBytes && addressFamily == (int)AddressFamily.InterNetwork) | |
|| (packedIP.Length == IPv6AddrBytes && addressFamily == (int)AddressFamily.InterNetworkV6) | |
)) { | |
throw MakeException(error, "invalid length of packed IP address string"); | |
} | |
byte[] ipBytes = StringOps.ToByteArray(packedIP); | |
if (addressFamily == (int)AddressFamily.InterNetworkV6) { | |
return IPv6BytesToColonHex(ipBytes); | |
} | |
return (new IPAddress(ipBytes)).ToString(); | |
} | |
[Documentation("inet_aton(ip_string) -> packed_ip\n" | |
+ "Convert an IP address (in string dotted quad format, e.g. '127.0.0.1') to a\n" | |
+ "32-bit packed binary format, as four-character string. The return format\n" | |
+ "matches the format of the standard C library's in_addr struct.\n" | |
+ "\n" | |
+ "If the address format is invalid, socket.error will be raised. Validity is\n" | |
+ "determined by the .NET System.Net.IPAddress.Parse() method.\n" | |
+ "\n" | |
+ "inet_aton() supports only IPv4." | |
)] | |
[PythonName("inet_aton")] | |
public static string IPStringToPackedBytes(string ipString) { | |
return IPStringToPackedBytes((int)AddressFamily.InterNetwork, ipString); | |
} | |
[Documentation("inet_ntoa(packed_ip) -> ip_string\n\n" | |
+ "Convert a packed IP address (a 4-byte string) to a string IP address (in dotted\n" | |
+ "quad format, e.g. '127.0.0.1'). The input format matches the format of the\n" | |
+ "standard C library's in_addr struct.\n" | |
+ "\n" | |
+ "If the input string is not exactly 4 bytes, socket.error will be raised.\n" | |
+ "\n" | |
+ "inet_ntoa() supports only IPv4." | |
)] | |
[PythonName("inet_ntoa")] | |
public static string IPPackedBytesToString(string packedIP) { | |
return IPPackedBytesToString((int)AddressFamily.InterNetwork, packedIP); | |
} | |
[Documentation("getdefaulttimeout() -> timeout\n\n" | |
+ "Return the default timeout for new socket objects in seconds as a float. A\n" | |
+ "value of None means that sockets have no timeout and begin in blocking mode.\n" | |
+ "The default value when the module is imported is None." | |
)] | |
[PythonName("getdefaulttimeout")] | |
public static object GetDefaultTimeout() { | |
if (DefaultTimeout == null) { | |
return null; | |
} else { | |
return (double)(DefaultTimeout.Value) / MillisecondsPerSecond; | |
} | |
} | |
[Documentation("setdefaulttimeout(timeout) -> None\n\n" | |
+ "Set the default timeout for new socket objects. timeout must be either None,\n" | |
+ "meaning that sockets have no timeout and start in blocking mode, or a\n" | |
+ "non-negative float that specifies the default timeout in seconds." | |
)] | |
[PythonName("setdefaulttimeout")] | |
public static void SetDefaultTimeout(object timeout) { | |
if (timeout == null) { | |
DefaultTimeout = null; | |
} else { | |
double seconds; | |
seconds = Converter.ConvertToDouble(timeout); | |
if (seconds < 0) { | |
throw Ops.ValueError("a non-negative float is required"); | |
} | |
DefaultTimeout = (int)(seconds * MillisecondsPerSecond); | |
} | |
} | |
#endregion | |
#region Exported constants | |
public static object AF_APPLETALK = (int)AddressFamily.AppleTalk; | |
public static object AF_DECnet = (int)AddressFamily.DecNet; | |
public static object AF_INET = (int)AddressFamily.InterNetwork; | |
public static object AF_INET6 = (int)AddressFamily.InterNetworkV6; | |
public static object AF_IPX = (int)AddressFamily.Ipx; | |
public static object AF_IRDA = (int)AddressFamily.Irda; | |
public static object AF_SNA = (int)AddressFamily.Sna; | |
public static object AF_UNSPEC = (int)AddressFamily.Unspecified; | |
public static object AI_CANONNAME = (int)0x2; | |
public static object AI_NUMERICHOST = (int)0x4; | |
public static object AI_PASSIVE = (int)0x1; | |
public static object EAI_AGAIN = (int)SocketError.TryAgain; | |
public static object EAI_BADFLAGS = (int)SocketError.InvalidArgument; | |
public static object EAI_FAIL = (int)SocketError.NoRecovery; | |
public static object EAI_FAMILY = (int)SocketError.AddressFamilyNotSupported; | |
public static object EAI_MEMORY = (int)SocketError.NoBufferSpaceAvailable; | |
public static object EAI_NODATA = (int)SocketError.HostNotFound; // not SocketError.NoData, like you would think | |
public static object EAI_NONAME = (int)SocketError.HostNotFound; | |
public static object EAI_SERVICE = (int)SocketError.TypeNotFound; | |
public static object EAI_SOCKTYPE = (int)SocketError.SocketNotSupported; | |
public static object EAI_SYSTEM = (int)SocketError.SocketError; | |
public static object EBADF = (int)0x9; | |
public static object INADDR_ALLHOSTS_GROUP = unchecked((int)0xe0000001); | |
public static object INADDR_ANY = (int)0x00000000; | |
public static object INADDR_BROADCAST = unchecked((int)0xFFFFFFFF); | |
public static object INADDR_LOOPBACK = unchecked((int)0x7F000001); | |
public static object INADDR_MAX_LOCAL_GROUP = unchecked((int)0xe00000FF); | |
public static object INADDR_NONE = unchecked((int)0xFFFFFFFF); | |
public static object INADDR_UNSPEC_GROUP = unchecked((int)0xE0000000); | |
public static object IPPORT_RESERVED = 1024; | |
public static object IPPORT_USERRESERVED = 5000; | |
public static object IPPROTO_AH = (int)ProtocolType.IPSecAuthenticationHeader; | |
public static object IPPROTO_DSTOPTS = (int)ProtocolType.IPv6DestinationOptions; | |
public static object IPPROTO_ESP = (int)ProtocolType.IPSecEncapsulatingSecurityPayload; | |
public static object IPPROTO_FRAGMENT = (int)ProtocolType.IPv6FragmentHeader; | |
public static object IPPROTO_GGP = (int)ProtocolType.Ggp; | |
public static object IPPROTO_HOPOPTS = (int)ProtocolType.IPv6HopByHopOptions; | |
public static object IPPROTO_ICMP = (int)ProtocolType.Icmp; | |
public static object IPPROTO_ICMPV6 = (int)ProtocolType.IcmpV6; | |
public static object IPPROTO_IDP = (int)ProtocolType.Idp; | |
public static object IPPROTO_IGMP = (int)ProtocolType.Igmp; | |
public static object IPPROTO_IP = (int)ProtocolType.IP; | |
public static object IPPROTO_IPV4 = (int)ProtocolType.IPv4; | |
public static object IPPROTO_IPV6 = (int)ProtocolType.IPv6; | |
public static object IPPROTO_MAX = 256; | |
public static object IPPROTO_ND = (int)ProtocolType.ND; | |
public static object IPPROTO_NONE = (int)ProtocolType.IPv6NoNextHeader; | |
public static object IPPROTO_PUP = (int)ProtocolType.Pup; | |
public static object IPPROTO_RAW = (int)ProtocolType.Raw; | |
public static object IPPROTO_ROUTING = (int)ProtocolType.IPv6RoutingHeader; | |
public static object IPPROTO_TCP = (int)ProtocolType.Tcp; | |
public static object IPPROTO_UDP = (int)ProtocolType.Udp; | |
public static object IPV6_HOPLIMIT = (int)SocketOptionName.HopLimit; | |
public static object IPV6_JOIN_GROUP = (int)SocketOptionName.AddMembership; | |
public static object IPV6_LEAVE_GROUP = (int)SocketOptionName.DropMembership; | |
public static object IPV6_MULTICAST_HOPS = (int)SocketOptionName.MulticastTimeToLive; | |
public static object IPV6_MULTICAST_IF = (int)SocketOptionName.MulticastInterface; | |
public static object IPV6_MULTICAST_LOOP = (int)SocketOptionName.MulticastLoopback; | |
public static object IPV6_PKTINFO = (int)SocketOptionName.PacketInformation; | |
public static object IPV6_UNICAST_HOPS = (int)SocketOptionName.IpTimeToLive; | |
public static object IP_ADD_MEMBERSHIP = (int)SocketOptionName.AddMembership; | |
public static object IP_DROP_MEMBERSHIP = (int)SocketOptionName.DropMembership; | |
public static object IP_HDRINCL = (int)SocketOptionName.HeaderIncluded; | |
public static object IP_MULTICAST_IF = (int)SocketOptionName.MulticastInterface; | |
public static object IP_MULTICAST_LOOP = (int)SocketOptionName.MulticastLoopback; | |
public static object IP_MULTICAST_TTL = (int)SocketOptionName.MulticastTimeToLive; | |
public static object IP_OPTIONS = (int)SocketOptionName.IPOptions; | |
public static object IP_TOS = (int)SocketOptionName.TypeOfService; | |
public static object IP_TTL = (int)SocketOptionName.IpTimeToLive; | |
public static object MSG_DONTROUTE = (int)SocketFlags.DontRoute; | |
public static object MSG_OOB = (int)SocketFlags.OutOfBand; | |
public static object MSG_PEEK = (int)SocketFlags.Peek; | |
public static object NI_DGRAM = 0x0010; | |
public static object NI_MAXHOST = 1025; | |
public static object NI_MAXSERV = 32; | |
public static object NI_NAMEREQD = 0x0004; | |
public static object NI_NOFQDN = 0x0001; | |
public static object NI_NUMERICHOST = 0x0002; | |
public static object NI_NUMERICSERV = 0x0008; | |
public static object SHUT_RD = (int)SocketShutdown.Receive; | |
public static object SHUT_RDWR = (int)SocketShutdown.Both; | |
public static object SHUT_WR = (int)SocketShutdown.Send; | |
public static object SOCK_DGRAM = (int)System.Net.Sockets.SocketType.Dgram; | |
public static object SOCK_RAW = (int)System.Net.Sockets.SocketType.Raw; | |
public static object SOCK_RDM = (int)System.Net.Sockets.SocketType.Rdm; | |
public static object SOCK_SEQPACKET = (int)System.Net.Sockets.SocketType.Seqpacket; | |
public static object SOCK_STREAM = (int)System.Net.Sockets.SocketType.Stream; | |
public static object SOL_IP = (int)SocketOptionLevel.IP; | |
public static object SOL_IPV6 = (int)SocketOptionLevel.IPv6; | |
public static object SOL_SOCKET = (int)SocketOptionLevel.Socket; | |
public static object SOL_TCP = (int)SocketOptionLevel.Tcp; | |
public static object SOL_UDP = (int)SocketOptionLevel.Udp; | |
public static object SOMAXCONN = (int)SocketOptionName.MaxConnections; | |
public static object SO_ACCEPTCONN = (int)SocketOptionName.AcceptConnection; | |
public static object SO_BROADCAST = (int)SocketOptionName.Broadcast; | |
public static object SO_DEBUG = (int)SocketOptionName.Debug; | |
public static object SO_DONTROUTE = (int)SocketOptionName.DontRoute; | |
public static object SO_ERROR = (int)SocketOptionName.Error; | |
public static object SO_EXCLUSIVEADDRUSE = (int)SocketOptionName.ExclusiveAddressUse; | |
public static object SO_KEEPALIVE = (int)SocketOptionName.KeepAlive; | |
public static object SO_LINGER = (int)SocketOptionName.Linger; | |
public static object SO_OOBINLINE = (int)SocketOptionName.OutOfBandInline; | |
public static object SO_RCVBUF = (int)SocketOptionName.ReceiveBuffer; | |
public static object SO_RCVLOWAT = (int)SocketOptionName.ReceiveLowWater; | |
public static object SO_RCVTIMEO = (int)SocketOptionName.ReceiveTimeout; | |
public static object SO_REUSEADDR = (int)SocketOptionName.ReuseAddress; | |
public static object SO_SNDBUF = (int)SocketOptionName.SendBuffer; | |
public static object SO_SNDLOWAT = (int)SocketOptionName.SendLowWater; | |
public static object SO_SNDTIMEO = (int)SocketOptionName.SendTimeout; | |
public static object SO_TYPE = (int)SocketOptionName.Type; | |
public static object SO_USELOOPBACK = (int)SocketOptionName.UseLoopback; | |
public static object TCP_NODELAY = (int)SocketOptionName.NoDelay; | |
public static object has_ipv6 = (int)1; | |
#endregion | |
#region Private implementation | |
/// <summary> | |
/// Return a standard socket exception (socket.error) whose message and error code come from a SocketException | |
/// This will eventually be enhanced to generate the correct error type (error, herror, gaierror) based on the error code. | |
/// </summary> | |
private static Exception MakeException(Exception exception) { | |
// !!! this shouldn't just blindly set the type to error (see summary) | |
if (exception is SocketException) { | |
SocketException se = (SocketException)exception; | |
switch (se.SocketErrorCode) { | |
case SocketError.TimedOut: | |
return MakeException(timeout, se.ErrorCode, se.Message); | |
default: | |
return MakeException(error, se.ErrorCode, se.Message); | |
} | |
} else if (exception is ObjectDisposedException) { | |
return MakeException(error, (int)EBADF, "the socket is closed"); | |
} else if (exception is InvalidOperationException) { | |
return MakeException(new SocketException((int)SocketError.InvalidArgument)); | |
} else { | |
return exception; | |
} | |
} | |
/// <summary> | |
/// Return an exception of a specified type with a specified message | |
/// </summary> | |
private static Exception MakeException(IPythonType type, params object[] args) { | |
if (args.Length == 1) { | |
return ExceptionConverter.ToClr(Ops.Call(type, args[0])); | |
} else { | |
return ExceptionConverter.ToClr(Ops.Call(type, Tuple.MakeTuple(args))); | |
} | |
} | |
/// <summary> | |
/// Convert an IPv6 address byte array to a string in standard colon-hex notation. | |
/// The .NET IPAddress.ToString() method uses dotted-quad for the last 32 bits, | |
/// which differs from the normal Python implementation (but is allowed by the IETF); | |
/// this method returns the standard (no dotted-quad) colon-hex form. | |
/// </summary> | |
private static string IPv6BytesToColonHex(byte[] ipBytes) { | |
Debug.Assert(ipBytes.Length == IPv6AddrBytes); | |
const int bytesPerWord = 2; // in bytes | |
const int bitsPerByte = 8; | |
int[] words = new int[IPv6AddrBytes / bytesPerWord]; | |
// Convert to array of 16-bit words | |
for (int i = 0; i < words.Length; i++) { | |
for (int j = 0; j < bytesPerWord; j++) { | |
words[i] <<= bitsPerByte; | |
words[i] += ipBytes[i * bytesPerWord + j]; | |
} | |
} | |
// Find longest series of 0-valued words (to collapse to ::) | |
int longestStart = 0; | |
int longestLen = 0; | |
for (int i = 0; i < words.Length; i++) { | |
if (words[i] == 0) { | |
for (int j = i; j < words.Length; j++) { | |
if (words[j] != 0) { | |
i += longestLen; | |
break; | |
} | |
if (j - i + 1 > longestLen) { | |
longestStart = i; | |
longestLen = j - i + 1; | |
} | |
} | |
} | |
} | |
// Build colon-hex string | |
StringBuilder result = new StringBuilder(IPv6AddrBytes * 3); | |
for (int i = 0; i < words.Length; i++) { | |
if (i != 0) result.Append(':'); | |
if (longestLen > 0 && i == longestStart) { | |
if (longestStart == 0) result.Append(':'); | |
if (longestStart + longestLen == words.Length) result.Append(':'); | |
i += longestLen - 1; | |
continue; | |
} else { | |
result.Append(words[i].ToString("x")); | |
} | |
} | |
return result.ToString(); | |
} | |
/// <summary> | |
/// Handle conversion of "" to INADDR_ANY and "<broadcast>" to INADDR_BROADCAST. | |
/// Otherwise returns host unchanged. | |
/// </summary> | |
private static string ConvertSpecialAddresses(string host) { | |
switch (host) { | |
case AnyAddrToken: | |
return IPAddress.Any.ToString(); | |
case BroadcastAddrToken: | |
return IPAddress.Broadcast.ToString(); | |
default: | |
return host; | |
} | |
} | |
/// <summary> | |
/// Return the IP address associated with host, with optional address family checking. | |
/// host may be either a name or an IP address (in string form). | |
/// | |
/// If family is non-null, a gaierror will be thrown if the host's address family is | |
/// not the same as the specified family. gaierror is also raised if the hostname cannot be | |
/// converted to an IP address (e.g. through a name lookup failure). | |
/// </summary> | |
private static IPAddress HostToAddress(string host, AddressFamily? family) { | |
return HostToAddresses(host, family)[0]; | |
} | |
/// <summary> | |
/// Return the IP address associated with host, with optional address family checking. | |
/// host may be either a name or an IP address (in string form). | |
/// | |
/// If family is non-null, a gaierror will be thrown if the host's address family is | |
/// not the same as the specified family. gaierror is also raised if the hostname cannot be | |
/// converted to an IP address (e.g. through a name lookup failure). | |
/// </summary> | |
private static IPAddress[] HostToAddresses(string host, AddressFamily? family) { | |
host = ConvertSpecialAddresses(host); | |
IPAddress addr; | |
try { | |
if (IPAddress.TryParse(host, out addr)) { | |
if (family == null || addr.AddressFamily == family.Value) { | |
return new IPAddress[]{ addr }; | |
} | |
// Incorrect family will raise exception below | |
} else { | |
IPHostEntry hostEntry = Dns.GetHostEntry(host); | |
List<IPAddress> addrs = new List<IPAddress>(); | |
foreach (IPAddress ip in hostEntry.AddressList) { | |
if (family == null || ip.AddressFamily == family.Value) { | |
addrs.Add(ip); | |
} | |
} | |
if (addrs.Count > 0) return addrs.ToArray(); | |
} | |
throw new SocketException((int)SocketError.HostNotFound); | |
} catch (SocketException e) { | |
throw MakeException(gaierror, e.ErrorCode, "no addresses of the specified family associated with host"); | |
} | |
} | |
/// <summary> | |
/// Return fqdn, but with its domain removed if it's on the same domain as the local machine. | |
/// </summary> | |
private static string RemoveLocalDomain(string fqdn) { | |
char[] DNS_SEP = new char[] { '.' }; | |
string[] myName = GetFQDN().Split(DNS_SEP, 2); | |
string[] otherName = fqdn.Split(DNS_SEP, 2); | |
if (myName.Length < 2 || otherName.Length < 2) return fqdn; | |
if (myName[1] == otherName[1]) { | |
return otherName[0]; | |
} else { | |
return fqdn; | |
} | |
} | |
/// <summary> | |
/// Convert a (host, port) tuple [IPv4] (host, port, flowinfo, scopeid) tuple [IPv6] | |
/// to its corresponding IPEndPoint. | |
/// | |
/// Throws gaierror if host is not a valid address. | |
/// Throws ArgumentTypeException if any of the following are true: | |
/// - address does not have exactly two elements | |
/// - address[0] is not a string | |
/// - address[1] is not an int | |
/// </summary> | |
private static IPEndPoint TupleToEndPoint(Tuple address, AddressFamily family) { | |
if (address.Count != 2 && address.Count != 4) { | |
throw Ops.TypeError("address tuple must have exactly 2 (IPv4) or exactly 4 (IPv6) elements"); | |
} | |
string host; | |
try { | |
host = Converter.ConvertToString(address[0]); | |
} catch (ArgumentTypeException) { | |
throw Ops.TypeError("host must be string"); | |
} | |
int port; | |
try { | |
port = Converter.ConvertToInt32(address[1]); | |
} catch (ArgumentTypeException) { | |
throw Ops.TypeError("port must be integer"); | |
} | |
IPAddress ip = HostToAddress(host, family); | |
if (address.Count == 2) { | |
return new IPEndPoint(ip, port); | |
} else { | |
long flowInfo; | |
try { | |
flowInfo = Converter.ConvertToInt64(address[2]); | |
} catch (ArgumentTypeException) { | |
throw Ops.TypeError("flowinfo must be integer"); | |
} | |
// We don't actually do anything with flowinfo right now, but we validate it | |
// in case we want to do something in the future. | |
long scopeId; | |
try { | |
scopeId = Converter.ConvertToInt64(address[3]); | |
} catch (ArgumentTypeException) { | |
throw Ops.TypeError("scopeid must be integer"); | |
} | |
IPEndPoint endPoint = new IPEndPoint(ip, port); | |
endPoint.Address.ScopeId = scopeId; | |
return endPoint; | |
} | |
} | |
/// <summary> | |
/// Convert an IPEndPoint to its corresponding (host, port) [IPv4] or (host, port, flowinfo, scopeid) [IPv6] tuple. | |
/// Throws SocketException if the address family is other than IPv4 or IPv6. | |
/// </summary> | |
private static Tuple EndPointToTuple(IPEndPoint endPoint) { | |
string ip = endPoint.Address.ToString(); | |
int port = endPoint.Port; | |
switch (endPoint.Address.AddressFamily) { | |
case AddressFamily.InterNetwork: | |
return Tuple.MakeTuple(ip, port); | |
case AddressFamily.InterNetworkV6: | |
long flowInfo = 0; // RFC 3493 p. 7 | |
long scopeId = endPoint.Address.ScopeId; | |
return Tuple.MakeTuple(ip, port, flowInfo, scopeId); | |
default: | |
throw new SocketException((int)SocketError.AddressFamilyNotSupported); | |
} | |
} | |
#endregion | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment