PageRenderTime 60ms CodeModel.GetById 17ms app.highlight 32ms RepoModel.GetById 0ms app.codeStats 1ms

/CoolEngine/IronPython/Src/IronPython.Modules/socket.cs

#
C# | 1715 lines | 1422 code | 172 blank | 121 comment | 186 complexity | 636155de9ac01bb0d7edad2af9e81902 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1/* ****************************************************************************
  2 *
  3 * Copyright (c) Microsoft Corporation. 
  4 *
  5 * This source code is subject to terms and conditions of the Microsoft Public License. A 
  6 * copy of the license can be found in the License.html file at the root of this distribution. If 
  7 * you cannot locate the  Microsoft Public License, please send an email to 
  8 * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
  9 * by the terms of the Microsoft Public License.
 10 *
 11 * You must not remove this notice, or any other, from this software.
 12 *
 13 *
 14 * ***************************************************************************/
 15
 16#if !SILVERLIGHT // System.NET
 17
 18using System;
 19using System.Collections;
 20using System.Collections.Generic;
 21using System.Diagnostics;
 22using System.IO;
 23using System.Net;
 24using System.Net.Sockets;
 25using System.Runtime.InteropServices;
 26using System.Text;
 27
 28using Microsoft.Scripting;
 29using Microsoft.Scripting.Actions;
 30using Microsoft.Scripting.Math;
 31using Microsoft.Scripting.Runtime;
 32
 33using IronPython.Runtime;
 34using IronPython.Runtime.Calls;
 35using IronPython.Runtime.Types;
 36using IronPython.Runtime.Operations;
 37using IronPython.Runtime.Exceptions;
 38
 39using SpecialNameAttribute = System.Runtime.CompilerServices.SpecialNameAttribute;
 40
 41[assembly: PythonModule("socket", typeof(IronPython.Modules.PythonSocket))]
 42namespace IronPython.Modules {
 43    public static class PythonSocket {
 44        private static readonly object _defaultTimeoutKey = new object();
 45        private static readonly object _defaultBufsizeKey = new object();
 46        private const int DefaultBufferSize = 8192;
 47
 48        [SpecialName]
 49        public static void PerformModuleReload(PythonContext/*!*/ context, IAttributesCollection/*!*/ dict) {
 50            if (!context.HasModuleState(_defaultTimeoutKey)) {
 51                context.SetModuleState(_defaultTimeoutKey, null);
 52            }
 53
 54            context.SetModuleState(_defaultBufsizeKey, DefaultBufferSize);
 55        }
 56
 57        public const string __doc__ = "Implementation module for socket operations.\n\n"
 58            + "This module is a loose wrapper around the .NET System.Net.Sockets API, so you\n"
 59            + "may find the corresponding MSDN documentation helpful in decoding error\n"
 60            + "messages and understanding corner cases.\n"
 61            + "\n"
 62            + "This implementation of socket differs slightly from the standard CPython\n"
 63            + "socket module. Many of these differences are due to the implementation of the\n"
 64            + ".NET socket libraries. These differences are summarized below. For full\n"
 65            + "details, check the docstrings of the functions mentioned.\n"
 66            + " - s.accept(), s.connect(), and s.connect_ex() do not support timeouts.\n"
 67            + " - Timeouts in s.sendall() don't work correctly.\n"
 68            + " - s.dup() is not implemented.\n"
 69            + " - getservbyname() and getservbyport() are not implemented.\n"
 70            + " - SSL support is not implemented."
 71            + "\n"
 72            + "An Extra IronPython-specific function is exposed only if the clr module is\n"
 73            + "imported:\n"
 74            + " - s.HandleToSocket() returns the System.Net.Sockets.Socket object associated\n"
 75            + "   with a particular \"file descriptor number\" (as returned by s.fileno()).\n"
 76            ;
 77
 78        #region Socket object
 79
 80        public static PythonType SocketType = DynamicHelpers.GetPythonTypeFromType(typeof(socket));
 81
 82        [PythonSystemType]
 83        [Documentation("socket([family[, type[, proto]]]) -> socket object\n\n"
 84                + "Create a socket (a network connection endpoint) of the given family, type,\n"
 85                + "and protocol. socket() accepts keyword arguments.\n"
 86                + " - family (address family) defaults to AF_INET\n"
 87                + " - type (socket type) defaults to SOCK_STREAM\n"
 88                + " - proto (protocol type) defaults to 0, which specifies the default protocol\n"
 89                + "\n"
 90                + "This module supports only IP sockets. It does not support raw or Unix sockets.\n"
 91                + "Both IPv4 and IPv6 are supported.")]
 92        public class socket : IWeakReferenceable {
 93            #region Fields
 94
 95            /// <summary>
 96            /// handleToSocket allows us to translate from Python's idea of a socket resource (file
 97            /// descriptor numbers) to .NET's idea of a socket resource (System.Net.Socket objects).
 98            /// In particular, this allows the select module to convert file numbers (as returned by
 99            /// fileno()) and convert them to Socket objects so that it can do something useful with them.
100            /// </summary>
101            private static readonly Dictionary<IntPtr, List<Socket>> handleToSocket = new Dictionary<IntPtr, List<Socket>>();
102
103            private const int DefaultAddressFamily = (int)AddressFamily.InterNetwork;
104            private const int DefaultSocketType = (int)System.Net.Sockets.SocketType.Stream;
105            private const int DefaultProtocolType = (int)ProtocolType.Unspecified;
106
107            internal Socket _socket;
108            private WeakRefTracker weakRefTracker = null;
109
110            #endregion
111
112            #region Public API
113
114            public socket(CodeContext/*!*/ context, [DefaultParameterValue(DefaultAddressFamily)] int addressFamily,
115                [DefaultParameterValue(DefaultSocketType)] int socketType,
116                [DefaultParameterValue(DefaultProtocolType)] int protocolType) {
117
118                System.Net.Sockets.SocketType type = (System.Net.Sockets.SocketType)Enum.ToObject(typeof(System.Net.Sockets.SocketType), socketType);
119                if (!Enum.IsDefined(typeof(System.Net.Sockets.SocketType), type)) {
120                    throw MakeException(new SocketException((int)SocketError.SocketNotSupported));
121                }
122                AddressFamily family = (AddressFamily)Enum.ToObject(typeof(AddressFamily), addressFamily);
123                if (!Enum.IsDefined(typeof(AddressFamily), family)) {
124                    throw MakeException(new SocketException((int)SocketError.AddressFamilyNotSupported));
125                }
126                ProtocolType proto = (ProtocolType)Enum.ToObject(typeof(ProtocolType), protocolType);
127                if (!Enum.IsDefined(typeof(ProtocolType), proto)) {
128                    throw MakeException(new SocketException((int)SocketError.ProtocolNotSupported));
129                }
130
131                Socket newSocket;
132                try {
133                    newSocket = new Socket(family, type, proto);
134                } catch (SocketException e) {
135                    throw MakeException(e);
136                }
137                Initialize(context, newSocket);
138            }
139
140            [Documentation("accept() -> (conn, address)\n\n"
141                + "Accept a connection. The socket must be bound and listening before calling\n"
142                + "accept(). conn is a new socket object connected to the remote host, and\n"
143                + "address is the remote host's address (e.g. a (host, port) tuple for IPv4).\n"
144                + "\n"
145                + "Difference from CPython: accept() does not support timeouts in blocking mode.\n"
146                + "If a timeout is set and the socket is in blocking mode, accept() will block\n"
147                + "indefinitely until a connection is ready."
148                )]
149            public PythonTuple accept(CodeContext/*!*/ context) {
150                socket wrappedRemoteSocket;
151                Socket realRemoteSocket;
152                try {
153                    realRemoteSocket = _socket.Accept();
154                } catch (Exception e) {
155                    throw MakeException(e);
156                }
157                wrappedRemoteSocket = new socket(context, realRemoteSocket);
158                return PythonTuple.MakeTuple(wrappedRemoteSocket, wrappedRemoteSocket.getpeername());
159            }
160
161            [Documentation("bind(address) -> None\n\n"
162                + "Bind to an address. If the socket is already bound, socket.error is raised.\n"
163                + "For IP sockets, address is a (host, port) tuple. Raw sockets are not\n"
164                + "supported.\n"
165                + "\n"
166                + "If you do not care which local address is assigned, set host to INADDR_ANY and\n"
167                + "the system will assign the most appropriate network address. Similarly, if you\n"
168                + "set port to 0, the system will assign an available port number between 1024\n"
169                + "and 5000."
170                )]
171            public void bind(PythonTuple address) {
172                IPEndPoint localEP = TupleToEndPoint(address, _socket.AddressFamily);
173                try {
174                    _socket.Bind(localEP);
175                } catch (Exception e) {
176                    throw MakeException(e);
177                }
178            }
179
180            [Documentation("close() -> None\n\nClose the socket. It cannot be used after being closed.")]
181            public void close() {
182                RemoveHandleSocketMapping(this);
183            }
184
185            internal static void RemoveHandleSocketMapping(socket socket) {
186                lock (handleToSocket) {
187                    List<Socket> sockets;
188                    if (handleToSocket.TryGetValue((IntPtr)socket._socket.Handle, out sockets)) {
189                        for (int i = sockets.Count-1; i >= 0; i--) {
190                            if (sockets[i] == socket._socket) {
191                                sockets.RemoveAt(i);
192                                break;
193                            }
194                        }
195                        if (sockets.Count == 0) {
196                            handleToSocket.Remove(socket._socket.Handle);
197                            try {
198                                socket._socket.Close();
199                            } catch (Exception e) {
200                                throw MakeException(e);
201                            }
202                        }
203                    }
204                }
205            }
206
207            [Documentation("connect(address) -> None\n\n"
208                + "Connect to a remote socket at the given address. IP addresses are expressed\n"
209                + "as (host, port).\n"
210                + "\n"
211                + "Raises socket.error if the socket has been closed, the socket is listening, or\n"
212                + "another connection error occurred."
213                + "\n"
214                + "Difference from CPython: connect() does not support timeouts in blocking mode.\n"
215                + "If a timeout is set and the socket is in blocking mode, connect() will block\n"
216                + "indefinitely until a connection is made or an error occurs."
217                )]
218            public void connect(PythonTuple address) {
219                IPEndPoint remoteEP = TupleToEndPoint(address, _socket.AddressFamily);
220                try {
221                    _socket.Connect(remoteEP);
222                } catch (Exception e) {
223                    throw MakeException(e);
224                }
225            }
226
227            [Documentation("connect_ex(address) -> error_code\n\n"
228                + "Like connect(), but return an error code insted of raising an exception for\n"
229                + "socket exceptions raised by the underlying system Connect() call. Note that\n"
230                + "exceptions other than SocketException generated by the system Connect() call\n"
231                + "will still be raised.\n"
232                + "\n"
233                + "A return value of 0 indicates that the connect call was successful."
234                + "\n"
235                + "Difference from CPython: connect_ex() does not support timeouts in blocking\n"
236                + "mode. If a timeout is set and the socket is in blocking mode, connect_ex() will\n"
237                + "block indefinitely until a connection is made or an error occurs."
238                )]
239            public int connect_ex(PythonTuple address) {
240                IPEndPoint remoteEP = TupleToEndPoint(address, _socket.AddressFamily);
241                try {
242                    _socket.Connect(remoteEP);
243                } catch (SocketException e) {
244                    return e.ErrorCode;
245                }
246                return (int)SocketError.Success;
247            }
248
249            [Documentation("fileno() -> file_handle\n\n"
250                + "Return the underlying system handle for this socket (a 64-bit integer)."
251                )]
252            public Int64 fileno() {
253                try {
254                    return _socket.Handle.ToInt64();
255                } catch (Exception e) {
256                    throw MakeException(e);
257                }
258            }
259
260            [Documentation("getpeername() -> address\n\n"
261                + "Return the address of the remote end of this socket. The address format is\n"
262                + "family-dependent (e.g. a (host, port) tuple for IPv4)."
263                )]
264            public PythonTuple getpeername() {
265                try {
266                    IPEndPoint remoteEP = _socket.RemoteEndPoint as IPEndPoint;
267                    if (remoteEP == null) {
268                        throw MakeException(new SocketException((int)SocketError.AddressFamilyNotSupported));
269                    }
270                    return EndPointToTuple(remoteEP);
271                } catch (Exception e) {
272                    throw MakeException(e);
273                }
274            }
275
276            [Documentation("getsockname() -> address\n\n"
277                + "Return the address of the local end of this socket. The address format is\n"
278                + "family-dependent (e.g. a (host, port) tuple for IPv4)."
279                )]
280            public PythonTuple getsockname() {
281                try {
282                    IPEndPoint localEP = _socket.LocalEndPoint as IPEndPoint;
283                    if (localEP == null) {
284                        throw MakeException(new SocketException((int)SocketError.InvalidArgument));
285                    }
286                    return EndPointToTuple(localEP);
287                } catch (Exception e) {
288                    throw MakeException(e);
289                }
290            }
291
292            [Documentation("getsockopt(level, optname[, buflen]) -> value\n\n"
293                + "Return the value of a socket option. level is one of the SOL_* constants\n"
294                + "defined in this module, and optname is one of the SO_* constants. If buflen is\n"
295                + "omitted or zero, an integer value is returned. If it is present, a byte string\n"
296                + "whose maximum length is buflen bytes) is returned. The caller must the decode\n"
297                + "the resulting byte string."
298                )]
299            public object getsockopt(int optionLevel, int optionName, [DefaultParameterValue(0)] int optionLength) {
300                SocketOptionLevel level = (SocketOptionLevel)Enum.ToObject(typeof(SocketOptionLevel), optionLevel);
301                if (!Enum.IsDefined(typeof(SocketOptionLevel), level)) {
302                    throw MakeException(new SocketException((int)SocketError.InvalidArgument));
303                }
304                SocketOptionName name = (SocketOptionName)Enum.ToObject(typeof(SocketOptionName), optionName);
305                if (!Enum.IsDefined(typeof(SocketOptionName), name)) {
306                    throw MakeException(new SocketException((int)SocketError.ProtocolOption));
307                }
308
309                try {
310                    if (optionLength == 0) {
311                        // Integer return value
312                        return (int)_socket.GetSocketOption(level, name);
313                    } else {
314                        // Byte string return value
315                        return StringOps.FromByteArray(_socket.GetSocketOption(level, name, optionLength));
316                    }
317                } catch (Exception e) {
318                    throw MakeException(e);
319                }
320            }
321
322            [Documentation("listen(backlog) -> None\n\n"
323                + "Listen for connections on the socket. Backlog is the maximum length of the\n"
324                + "pending connections queue. The maximum value is system-dependent."
325                )]
326            public void listen(int backlog) {
327                try {
328                    _socket.Listen(backlog);
329                } catch (Exception e) {
330                    throw MakeException(e);
331                }
332            }
333            
334            [Documentation("makefile([mode[, bufsize]]) -> file object\n\n"
335                + "Return a regular file object corresponding to the socket.  The mode\n"
336                + "and bufsize arguments are as for the built-in open() function.")]
337            public PythonFile makefile(CodeContext/*!*/ context, [DefaultParameterValue("r")]string mode, [DefaultParameterValue(8192)]int bufSize) {
338                AddHandleMapping(this); // dup our handle
339                return new _fileobject(context, this, mode, bufSize);
340            }
341
342            [Documentation("recv(bufsize[, flags]) -> string\n\n"
343                + "Receive data from the socket, up to bufsize bytes. For connection-oriented\n"
344                + "protocols (e.g. SOCK_STREAM), you must first call either connect() or\n"
345                + "accept(). Connectionless protocols (e.g. SOCK_DGRAM) may also use recvfrom().\n"
346                + "\n"
347                + "recv() blocks until data is available, unless a timeout was set using\n"
348                + "settimeout(). If the timeout was exceeded, socket.timeout is raised."
349                + "recv() returns immediately with zero bytes when the connection is closed."
350                )]
351            public string recv(int maxBytes, [DefaultParameterValue(0)] int flags) {
352                int bytesRead;
353                byte[] buffer = new byte[maxBytes];
354                try {
355                    bytesRead = _socket.Receive(buffer, (SocketFlags)flags);
356                } catch (Exception e) {
357                    throw MakeException(e);
358                }
359                return StringOps.FromByteArray(buffer, bytesRead);
360            }
361
362            [Documentation("recvfrom(bufsize[, flags]) -> (string, address)\n\n"
363                + "Receive data from the socket, up to bufsize bytes. string is the data\n"
364                + "received, and address (whose format is protocol-dependent) is the address of\n"
365                + "the socket from which the data was received."
366                )]
367            public PythonTuple recvfrom(int maxBytes, [DefaultParameterValue(0)] int flags) {
368                int bytesRead;
369                byte[] buffer = new byte[maxBytes];
370                IPEndPoint remoteIPEP = new IPEndPoint(IPAddress.Any, 0);
371                EndPoint remoteEP = remoteIPEP;
372                try {
373                    bytesRead = _socket.ReceiveFrom(buffer, (SocketFlags)flags, ref remoteEP);
374                } catch (Exception e) {
375                    throw MakeException(e);
376                }
377                string data = StringOps.FromByteArray(buffer, bytesRead);
378                PythonTuple remoteAddress = EndPointToTuple((IPEndPoint)remoteEP);
379                return PythonTuple.MakeTuple(data, remoteAddress);
380            }
381
382            [Documentation("send(string[, flags]) -> bytes_sent\n\n"
383                + "Send data to the remote socket. The socket must be connected to a remote\n"
384                + "socket (by calling either connect() or accept(). Returns the number of bytes\n"
385                + "sent to the remote socket.\n"
386                + "\n"
387                + "Note that the successful completion of a send() call does not mean that all of\n"
388                + "the data was sent. The caller must keep track of the number of bytes sent and\n"
389                + "retry the operation until all of the data has been sent.\n"
390                + "\n"
391                + "Also note that there is no guarantee that the data you send will appear on the\n"
392                + "network immediately. To increase network efficiency, the underlying system may\n"
393                + "delay transmission until a significant amount of outgoing data is collected. A\n"
394                + "successful completion of the Send method means that the underlying system has\n"
395                + "had room to buffer your data for a network send"
396                )]
397            public int send(string data, [DefaultParameterValue(0)] int flags) {
398                byte[] buffer = StringOps.ToByteArray(data);
399                try {
400                    return _socket.Send(buffer, (SocketFlags)flags);
401                } catch (Exception e) {
402                    throw MakeException(e);
403                }
404            }
405
406            [Documentation("sendall(string[, flags]) -> None\n\n"
407                + "Send data to the remote socket. The socket must be connected to a remote\n"
408                + "socket (by calling either connect() or accept().\n"
409                + "\n"
410                + "Unlike send(), sendall() blocks until all of the data has been sent or until a\n"
411                + "timeout or an error occurs. None is returned on success. If an error occurs,\n"
412                + "there is no way to tell how much data, if any, was sent.\n"
413                + "\n"
414                + "Difference from CPython: timeouts do not function as you would expect. The\n"
415                + "function is implemented using multiple calls to send(), so the timeout timer\n"
416                + "is reset after each of those calls. That means that the upper bound on the\n"
417                + "time that it will take for sendall() to return is the number of bytes in\n"
418                + "string times the timeout interval.\n"
419                + "\n"
420                + "Also note that there is no guarantee that the data you send will appear on the\n"
421                + "network immediately. To increase network efficiency, the underlying system may\n"
422                + "delay transmission until a significant amount of outgoing data is collected. A\n"
423                + "successful completion of the Send method means that the underlying system has\n"
424                + "had room to buffer your data for a network send"
425                )]
426            public void sendall(string data, [DefaultParameterValue(0)] int flags) {
427                byte[] buffer = StringOps.ToByteArray(data);
428                try {
429                    int bytesTotal = buffer.Length;
430                    int bytesRemaining = bytesTotal;
431                    while (bytesRemaining > 0) {
432                        bytesRemaining -= _socket.Send(buffer, bytesTotal - bytesRemaining, bytesRemaining, (SocketFlags)flags);
433                    }
434                } catch (Exception e) {
435                    throw MakeException(e);
436                }
437            }
438
439            [Documentation("sendto(string[, flags], address) -> bytes_sent\n\n"
440                + "Send data to the remote socket. The socket does not need to be connected to a\n"
441                + "remote socket since the address is specified in the call to sendto(). Returns\n"
442                + "the number of bytes sent to the remote socket.\n"
443                + "\n"
444                + "Blocking sockets will block until the all of the bytes in the buffer are sent.\n"
445                + "Since a nonblocking Socket completes immediately, it might not send all of the\n"
446                + "bytes in the buffer. It is your application's responsibility to keep track of\n"
447                + "the number of bytes sent and to retry the operation until the application sends\n"
448                + "all of the bytes in the buffer.\n"
449                + "\n"
450                + "Note that there is no guarantee that the data you send will appear on the\n"
451                + "network immediately. To increase network efficiency, the underlying system may\n"
452                + "delay transmission until a significant amount of outgoing data is collected. A\n"
453                + "successful completion of the Send method means that the underlying system has\n"
454                + "had room to buffer your data for a network send"
455                )]
456            public int sendto(string data, int flags, PythonTuple address) {
457                byte[] buffer = StringOps.ToByteArray(data);
458                EndPoint remoteEP = TupleToEndPoint(address, _socket.AddressFamily);
459                try {
460                    return _socket.SendTo(buffer, (SocketFlags)flags, remoteEP);
461                } catch (Exception e) {
462                    throw MakeException(e);
463                }
464            }
465
466            [Documentation("")]
467            public int sendto(string data, PythonTuple address) {
468                return sendto(data, 0, address);
469            }
470
471            [Documentation("setblocking(flag) -> None\n\n"
472                + "Set the blocking mode of the socket. If flag is 0, the socket will be set to\n"
473                + "non-blocking mode; otherwise, it will be set to blocking mode. If the socket is\n"
474                + "in blocking mode, and a method is called (such as send() or recv() which does\n"
475                + "not complete immediately, the caller will block execution until the requested\n"
476                + "operation completes. In non-blocking mode, a socket.timeout exception would\n"
477                + "would be raised in this case.\n"
478                + "\n"
479                + "Note that changing blocking mode also affects the timeout setting:\n"
480                + "setblocking(0) is equivalent to settimeout(0), and setblocking(1) is equivalent\n"
481                + "to settimeout(None)."
482                )]
483            public void setblocking(int shouldBlock) {
484                if (shouldBlock == 0) {
485                    settimeout(0);
486                } else {
487                    settimeout(null);
488                }
489            }
490
491            [Documentation("settimeout(value) -> None\n\n"
492                + "Set a timeout on blocking socket methods. value may be either None or a\n"
493                + "non-negative float, with one of the following meanings:\n"
494                + " - None: disable timeouts and block indefinitely"
495                + " - 0.0: don't block at all (return immediately if the operation can be\n"
496                + "   completed; raise socket.error otherwise)\n"
497                + " - float > 0.0: block for up to the specified number of seconds; raise\n"
498                + "   socket.timeout if the operation cannot be completed in time\n"
499                + "\n"
500                + "settimeout(None) is equivalent to setblocking(1), and settimeout(0.0) is\n"
501                + "equivalent to setblocking(0)."
502                + "\n"
503                + "If the timeout is non-zero and is less than 0.5, it will be set to 0.5. This\n"
504                + "limitation is specific to IronPython.\n"
505                )]
506            public void settimeout(object timeout) {
507                try {
508                    if (timeout == null) {
509                        _socket.Blocking = true;
510                        _socket.SendTimeout = 0;
511                    } else {
512                        double seconds;
513                        seconds = Converter.ConvertToDouble(timeout);
514                        if (seconds < 0) {
515                            throw PythonOps.TypeError("a non-negative float is required");
516                        }
517                        _socket.Blocking = seconds > 0; // 0 timeout means non-blocking mode
518                        _socket.SendTimeout = (int)(seconds * MillisecondsPerSecond);
519                    }
520                } finally {
521                    _socket.ReceiveTimeout = _socket.SendTimeout;
522                }
523            }
524
525            [Documentation("gettimeout() -> value\n\n"
526                + "Return the timeout duration in seconds for this socket as a float. If no\n"
527                + "timeout is set, return None. For more details on timeouts and blocking, see the\n"
528                + "Python socket module documentation."
529                )]
530            public object gettimeout() {
531                try {
532                    if (_socket.Blocking && _socket.SendTimeout == 0) {
533                        return null;
534                    } else {
535                        return (double)_socket.SendTimeout / MillisecondsPerSecond;
536                    }
537                } catch (Exception e) {
538                    throw MakeException(e);
539                }
540            }
541
542            [Documentation("setsockopt(level, optname[, value]) -> None\n\n"
543                + "Set the value of a socket option. level is one of the SOL_* constants defined\n"
544                + "in this module, and optname is one of the SO_* constants. value may be either\n"
545                + "an integer or a string containing a binary structure. The caller is responsible\n"
546                + "for properly encoding the byte string."
547                )]
548            public void setsockopt(int optionLevel, int optionName, object value) {
549                SocketOptionLevel level = (SocketOptionLevel)Enum.ToObject(typeof(SocketOptionLevel), optionLevel);
550                if (!Enum.IsDefined(typeof(SocketOptionLevel), level)) {
551                    throw MakeException(new SocketException((int)SocketError.InvalidArgument));
552                }
553                SocketOptionName name = (SocketOptionName)Enum.ToObject(typeof(SocketOptionName), optionName);
554                if (!Enum.IsDefined(typeof(SocketOptionName), name)) {
555                    throw MakeException(new SocketException((int)SocketError.ProtocolOption));
556                }
557
558                try {
559                    int intValue;
560                    if (Converter.TryConvertToInt32(value, out intValue)) {
561                        _socket.SetSocketOption(level, name, intValue);
562                        return;
563                    }
564
565                    string strValue;
566                    if (Converter.TryConvertToString(value, out strValue)) {
567                        _socket.SetSocketOption(level, name, StringOps.ToByteArray(strValue));
568                        return;
569                    }
570                } catch (Exception e) {
571                    throw MakeException(e);
572                }
573
574                throw PythonOps.TypeError("setsockopt() argument 3 must be int or string");
575            }
576
577            [Documentation("shutdown() -> None\n\n"
578                + "Return the timeout duration in seconds for this socket as a float. If no\n"
579                + "timeout is set, return None. For more details on timeouts and blocking, see the\n"
580                + "Python socket module documentation."
581                )]
582            public void shutdown(int how) {
583                SocketShutdown howValue = (SocketShutdown)Enum.ToObject(typeof(SocketShutdown), how);
584                if (!Enum.IsDefined(typeof(SocketShutdown), howValue)) {
585                    throw MakeException(new SocketException((int)SocketError.InvalidArgument));
586                }
587                try {
588                    _socket.Shutdown(howValue);
589                } catch (Exception e) {
590                    throw MakeException(e);
591                }
592            }
593
594            public override string ToString() {
595                try {
596                    return "<socket object, fd=" + fileno().ToString()
597                        + ", family=" + ((int)_socket.AddressFamily).ToString()
598                        + ", type=" + ((int)_socket.SocketType).ToString()
599                        + ", protocol=" + ((int)_socket.ProtocolType).ToString()
600                        + ">"
601                    ;
602                } catch {
603                    return "<socket object, fd=?, family=?, type=, protocol=>";
604                }
605            }
606
607            /// <summary>
608            /// Return the internal System.Net.Sockets.Socket socket object associated with the given
609            /// handle (as returned by GetHandle()), or null if no corresponding socket exists. This is
610            /// primarily intended to be used by other modules (such as select) that implement
611            /// networking primitives. User code should not normally need to call this function.
612            /// </summary>
613            internal static Socket HandleToSocket(Int64 handle) {
614                List<Socket> sockets;
615                lock (handleToSocket) {
616                    if (handleToSocket.TryGetValue((IntPtr)handle, out sockets)) {
617                        return sockets[sockets.Count - 1];
618                    }
619                }
620                return null;
621            }
622
623            #endregion
624
625            #region IWeakReferenceable Implementation
626
627            WeakRefTracker IWeakReferenceable.GetWeakRef() {
628                return weakRefTracker;
629            }
630
631            bool IWeakReferenceable.SetWeakRef(WeakRefTracker value) {
632                weakRefTracker = value;
633                return true;
634            }
635
636            void IWeakReferenceable.SetFinalizer(WeakRefTracker value) {
637                weakRefTracker = value;
638            }
639
640            #endregion
641
642            #region Private Implementation
643
644            /// <summary>
645            /// Create a Python socket object from an existing .NET socket object
646            /// (like one returned from Socket.Accept())
647            /// </summary>
648            private socket(CodeContext/*!*/ context, Socket socket) {
649                Initialize(context, socket);
650            }
651
652            /// <summary>
653            /// Perform initialization common to all constructors
654            /// </summary>
655            private void Initialize(CodeContext/*!*/ context, Socket socket) {
656                this._socket = socket;
657                int? defaultTimeout = GetDefaultTimeout(context);
658                if (defaultTimeout == null) {
659                    settimeout(null);
660                } else {
661                    settimeout((double)defaultTimeout / MillisecondsPerSecond);
662                }
663                AddHandleMapping(this);
664            }
665
666            private static void AddHandleMapping(socket socket) {
667                lock (handleToSocket) {
668                    if (!handleToSocket.ContainsKey(socket._socket.Handle)) {
669                        handleToSocket[socket._socket.Handle] = new List<Socket>(1);
670                    }
671                    handleToSocket[socket._socket.Handle].Add(socket._socket);
672                }
673            }
674
675            #endregion
676        }
677
678        #endregion
679
680        #region Fields
681
682        public static PythonType error = PythonExceptions.CreateSubType(PythonExceptions.Exception, "error", "socket", "");
683        public static PythonType herror = PythonExceptions.CreateSubType(error, "herror", "socket", "");
684        public static PythonType gaierror = PythonExceptions.CreateSubType(error, "gaierror", "socket", "");
685        public static PythonType timeout = PythonExceptions.CreateSubType(error, "timeout", "socket", "");       
686
687        private const string AnyAddrToken = "";
688        private const string BroadcastAddrToken = "<broadcast>";
689        private const string LocalhostAddrToken = "";
690        private const int IPv4AddrBytes = 4;
691        private const int IPv6AddrBytes = 16;
692        private const double MillisecondsPerSecond = 1000.0;
693
694        #endregion
695
696        #region Public API
697
698        [Documentation("")]
699        public static List getaddrinfo(
700            string host,
701            object port,
702            [DefaultParameterValue((int)AddressFamily.Unspecified)] int family,
703            [DefaultParameterValue(0)] int socktype,
704            [DefaultParameterValue((int)ProtocolType.IP)] int proto,
705            [DefaultParameterValue((int)SocketFlags.None)] int flags
706        ) {
707            int numericPort;
708            
709            if (port == null) {
710                numericPort = 0;
711            } else if (port is int) {
712                numericPort = (int)port;
713            } else if (port is Extensible<int>) {
714                numericPort = ((Extensible<int>)port).Value;
715            } else if (port is string) {
716                if (!Int32.TryParse((string)port, out numericPort)) {
717                    // TODO: also should consult GetServiceByName                    
718                    throw PythonExceptions.CreateThrowable(gaierror, "getaddrinfo failed");
719                }
720            } else if (port is ExtensibleString) {
721                if (!Int32.TryParse(((ExtensibleString)port).Value, out numericPort)) {
722                    // TODO: also should consult GetServiceByName                    
723                    throw PythonExceptions.CreateThrowable(gaierror, "getaddrinfo failed");
724                }
725            } else {
726                throw PythonExceptions.CreateThrowable(gaierror, "getaddrinfo failed");
727            }
728
729            if (socktype != 0) {
730                // we just use this to validate; socketType isn't actually used
731                System.Net.Sockets.SocketType socketType = (System.Net.Sockets.SocketType)Enum.ToObject(typeof(System.Net.Sockets.SocketType), socktype);
732                if (socketType == System.Net.Sockets.SocketType.Unknown || !Enum.IsDefined(typeof(System.Net.Sockets.SocketType), socketType)) {
733                    throw PythonExceptions.CreateThrowable(gaierror, PythonTuple.MakeTuple((int)SocketError.SocketNotSupported, "getaddrinfo failed"));
734                }
735            }
736
737            AddressFamily addressFamily = (AddressFamily)Enum.ToObject(typeof(AddressFamily), family);
738            if (!Enum.IsDefined(typeof(AddressFamily), addressFamily)) {
739                throw PythonExceptions.CreateThrowable(gaierror, PythonTuple.MakeTuple((int)SocketError.AddressFamilyNotSupported, "getaddrinfo failed"));
740            }
741
742            // Again, we just validate, but don't actually use protocolType
743            ProtocolType protocolType = (ProtocolType)Enum.ToObject(typeof(ProtocolType), proto);
744
745            IPAddress[] ips = HostToAddresses(host, addressFamily);
746
747            List results = new List();
748
749            foreach (IPAddress ip in ips) {
750                results.append(PythonTuple.MakeTuple(
751                    (int)ip.AddressFamily,
752                    socktype,
753                    proto,
754                    "",
755                    EndPointToTuple(new IPEndPoint(ip, numericPort))
756                ));
757            }
758
759            return results;
760        }
761
762        [Documentation("getfqdn([hostname_or_ip]) -> hostname\n\n"
763            + "Return the fully-qualified domain name for the specified hostname or IP\n"
764            + "address. An unspecified or empty name is interpreted as the local host. If the\n"
765            + "name lookup fails, the passed-in name is returned as-is."
766            )]
767        public static string getfqdn(string host) {
768            host = host.Trim();
769            if (host == BroadcastAddrToken) {
770                return host;
771            }
772            try {
773                IPHostEntry hostEntry = Dns.GetHostEntry(host);
774                if (hostEntry.HostName.Contains(".")) {
775                    return hostEntry.HostName;
776                } else {
777                    foreach (string addr in hostEntry.Aliases) {
778                        if (addr.Contains(".")) {
779                            return addr;
780                        }
781                    }
782                }
783            } catch (SocketException) {
784                // ignore and return host below
785            }
786            // seems to match CPython behavior, although docs say gethostname() should be returned
787            return host;
788        }
789
790        [Documentation("")]
791        public static string getfqdn() {
792            return getfqdn(LocalhostAddrToken);
793        }
794
795        [Documentation("gethostbyname(hostname) -> ip address\n\n"
796            + "Return the string IPv4 address associated with the given hostname (e.g.\n"
797            + "'10.10.0.1'). The hostname is returned as-is if it an IPv4 address. The empty\n"
798            + "string is treated as the local host.\n"
799            + "\n"
800            + "gethostbyname() doesn't support IPv6; for IPv4/IPv6 support, use getaddrinfo()."
801            )]
802        public static string gethostbyname(string host) {
803            return HostToAddress(host, AddressFamily.InterNetwork).ToString();
804        }
805
806        [Documentation("gethostbyname_ex(hostname) -> (hostname, aliases, ip_addresses)\n\n"
807            + "Return the real host name, a list of aliases, and a list of IP addresses\n"
808            + "associated with the given hostname. If the hostname is an IPv4 address, the\n"
809            + "tuple ([hostname, [], [hostname]) is returned without doing a DNS lookup.\n"
810            + "\n"
811            + "gethostbyname_ex() doesn't support IPv6; for IPv4/IPv6 support, use\n"
812            + "getaddrinfo()."
813            )]
814        public static PythonTuple gethostbyname_ex(string host) {
815            string hostname;
816            List aliases;
817            List ips = PythonOps.MakeList();
818
819            IPAddress addr;
820            if (IPAddress.TryParse(host, out addr)) {
821                if (AddressFamily.InterNetwork == addr.AddressFamily) {
822                    hostname = host;
823                    aliases = PythonOps.MakeEmptyList(0);
824                    ips.append(host);
825                } else {
826                    throw PythonExceptions.CreateThrowable(gaierror, (int)SocketError.HostNotFound, "no IPv4 addresses associated with host");
827                }
828            } else {
829                IPHostEntry hostEntry;
830                try {
831                    hostEntry = Dns.GetHostEntry(host);
832                } catch (SocketException e) {
833                    throw PythonExceptions.CreateThrowable(gaierror, e.ErrorCode, "no IPv4 addresses associated with host");
834                }
835                hostname = hostEntry.HostName;
836                aliases = PythonOps.MakeList(hostEntry.Aliases);
837                foreach (IPAddress ip in hostEntry.AddressList) {
838                    if (AddressFamily.InterNetwork == ip.AddressFamily) {
839                        ips.append(ip.ToString());
840                    }
841                }
842            }
843
844            return PythonTuple.MakeTuple(hostname, aliases, ips);
845        }
846
847        [Documentation("gethostname() -> hostname\nReturn this machine's hostname")]
848        public static string gethostname() {
849            return Dns.GetHostName();
850        }
851
852        [Documentation("gethostbyaddr(host) -> (hostname, aliases, ipaddrs)\n\n"
853            + "Return a tuple of (primary hostname, alias hostnames, ip addresses). host may\n"
854            + "be either a hostname or an IP address."
855            )]
856        public static object gethostbyaddr(string host) {
857            if (host == "") {
858                host = gethostname();
859            }
860            // This conversion seems to match CPython behavior
861            host = gethostbyname(host);
862
863            IPAddress[] ips = null;
864            IPHostEntry hostEntry = null;
865            try {
866                ips = Dns.GetHostAddresses(host);
867                hostEntry = Dns.GetHostEntry(host);
868            } catch (Exception e) {
869                throw MakeException(e);
870            }
871
872            List ipStrings = PythonOps.MakeList();
873            foreach (IPAddress ip in ips) {
874                ipStrings.append(ip.ToString());
875            }
876
877            return PythonTuple.MakeTuple(hostEntry.HostName, PythonOps.MakeList(hostEntry.Aliases), ipStrings);
878        }
879
880        [Documentation("getnameinfo(socketaddr, flags) -> (host, port)\n"
881            + "Given a socket address, the return a tuple of the corresponding hostname and\n"
882            + "port. Available flags:\n"
883            + " - NI_NOFQDN: Return only the hostname part of the domain name for hosts on the\n"
884            + "   same domain as the executing machine.\n"
885            + " - NI_NUMERICHOST: return the numeric form of the host (e.g. '127.0.0.1' or\n"
886            + "   '::1' rather than 'localhost').\n"
887            + " - NI_NAMEREQD: Raise an error if the hostname cannot be looked up.\n"
888            + " - NI_NUMERICSERV: Return string containing the numeric form of the port (e.g.\n"
889            + "   '80' rather than 'http'). This flag is required (see below).\n"
890            + " - NI_DGRAM: Silently ignored (see below).\n"
891            + "\n"
892            + "Difference from CPython: the following flag behavior differs from CPython\n"
893            + "because the .NET framework libraries offer no name-to-port conversion APIs:\n"
894            + " - NI_NUMERICSERV: This flag is required because the .NET framework libraries\n"
895            + "   offer no port-to-name mapping APIs. If it is omitted, getnameinfo() will\n"
896            + "   raise a NotImplementedError.\n"
897            + " - The NI_DGRAM flag is ignored because it only applies when NI_NUMERICSERV is\n"
898            + "   omitted. It it were supported, it would return the UDP-based port name\n"
899            + "   rather than the TCP-based port name.\n"
900            )]
901        public static object getnameinfo(PythonTuple socketAddr, int flags) {
902            if (socketAddr.__len__() < 2 || socketAddr.__len__() > 4) {
903                throw PythonOps.TypeError("socket address must be a 2-tuple (IPv4 or IPv6) or 4-tuple (IPv6)");
904            }
905
906            if ((flags & (int)NI_NUMERICSERV) == 0) {
907                throw PythonOps.NotImplementedError("getnameinfo() required the NI_NUMERICSERV flag (see docstring)");
908            }
909
910            string host = Converter.ConvertToString(socketAddr[0]);
911            if (host == null) throw PythonOps.TypeError("argument 1 must be string");
912            int port = 0;
913            try {
914                port = (int)socketAddr[1];
915            } catch (InvalidCastException) {
916                throw PythonOps.TypeError("an integer is required");
917            }
918
919            string resultHost = null;
920            string resultPort = null;
921
922            // Host
923            IPHostEntry hostEntry = null;
924            try {
925                // Do double lookup to force reverse DNS lookup to match CPython behavior
926                hostEntry = Dns.GetHostEntry(host);
927                if (hostEntry.AddressList.Length < 1) {
928                    throw PythonExceptions.CreateThrowable(error, "sockaddr resolved to zero addresses");
929                }
930                hostEntry = Dns.GetHostEntry(hostEntry.AddressList[0]);
931            } catch (SocketException e) {
932                throw PythonExceptions.CreateThrowable(gaierror, e.ErrorCode, e.Message);
933            } catch (IndexOutOfRangeException) {
934                throw PythonExceptions.CreateThrowable(gaierror, "sockaddr resolved to zero addresses");
935            }
936
937            IList<IPAddress> addrs = hostEntry.AddressList;
938            if (addrs.Count > 1) {
939                // ignore non-IPV4 addresses
940                List<IPAddress> newAddrs = new List<IPAddress>(addrs.Count);
941                foreach (IPAddress addr in hostEntry.AddressList) {
942                    if (addr.AddressFamily == AddressFamily.InterNetwork) {
943                        newAddrs.Add(addr);
944                    }
945                }
946                if (newAddrs.Count > 1) {
947                    throw PythonExceptions.CreateThrowable(error, "sockaddr resolved to multiple addresses");
948                }
949                addrs = newAddrs;
950            }
951
952            if (addrs.Count < 1) {
953                throw PythonExceptions.CreateThrowable(error, "sockaddr resolved to zero addresses");
954            }
955
956            if ((flags & (int)NI_NUMERICHOST) != 0) {
957                resultHost = addrs[0].ToString();
958            } else if ((flags & (int)NI_NOFQDN) != 0) {
959                resultHost = RemoveLocalDomain(hostEntry.HostName);
960            } else {
961                resultHost = hostEntry.HostName;
962            }
963
964            // Port
965            // We don't branch on NI_NUMERICSERV here since we throw above if it's not set
966            resultPort = port.ToString();
967
968            return PythonTuple.MakeTuple(resultHost, resultPort);
969        }
970
971        [Documentation("getprotobyname(protoname) -> integer proto\n\n"
972            + "Given a string protocol name (e.g. \"udp\"), return the associated integer\n"
973            + "protocol number, suitable for passing to socket(). The name is case\n"
974            + "insensitive.\n"
975            + "\n"
976            + "Raises socket.error if no protocol number can be found."
977            )]
978        public static object getprotobyname(string protocolName) {
979            switch (protocolName.ToLower()) {
980                case "ah": return IPPROTO_AH;
981                case "esp": return IPPROTO_ESP;
982                case "dstopts": return IPPROTO_DSTOPTS;
983                case "fragment": return IPPROTO_FRAGMENT;
984                case "ggp": return IPPROTO_GGP;
985                case "icmp": return IPPROTO_ICMP;
986                case "icmpv6": return IPPROTO_ICMPV6;
987                case "ip": r

Large files files are truncated, but you can click here to view the full file