Skip to content

Instantly share code, notes, and snippets.

@ironpythonbot
Created December 9, 2014 16:37
Show Gist options
  • Save ironpythonbot/b9c46e37bfa854cbb720 to your computer and use it in GitHub Desktop.
Save ironpythonbot/b9c46e37bfa854cbb720 to your computer and use it in GitHub Desktop.
CodePlex Issue #5814 Plain Text Attachments
/* **********************************************************************************
*
* 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 "&lt;broadcast&gt;" 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