Пример #1
0
        //public static PhpArray stream_socket_pair(ProtocolFamily protocolFamily, SocketType type, ProtocolType protocol)
        //{
        //    PhpException.FunctionNotSupported();
        //    return null;
        //}

        #endregion

        #region Connect

        /// <summary>
        /// Opens a new SocketStream
        /// </summary>
        internal static PhpStream Connect(Context ctx, string remoteSocket, int port, out int errno, out string errstr, double timeout, SocketOptions flags, StreamContext /*!*/ context)
        {
            errno  = 0;
            errstr = null;

            if (remoteSocket == null)
            {
                PhpException.ArgumentNull("remoteSocket");
                return(null);
            }

            bool IsSsl = false;

            // TODO: extract schema (tcp://, udp://) and port from remoteSocket
            // Uri uri = Uri.TryCreate(remoteSocket);
            const string protoSeparator = "://";
            var          protocol       = ProtocolType.Tcp;
            var          protoIdx       = remoteSocket.IndexOf(protoSeparator, StringComparison.Ordinal);

            if (protoIdx >= 0)
            {
                var protoStr = remoteSocket.AsSpan(0, protoIdx);
                if (protoStr.Equals("udp".AsSpan(), StringComparison.Ordinal))
                {
                    protocol = ProtocolType.Udp;
                }
                else if (protoStr.Equals("ssl".AsSpan(), StringComparison.Ordinal))
                {
                    // use SSL encryption
                    IsSsl = true;
                }

                remoteSocket = remoteSocket.Substring(protoIdx + protoSeparator.Length);
            }

            var colonIdx = remoteSocket.IndexOf(':');

            if (colonIdx >= 0)
            {
                var portStr = remoteSocket.AsSpan(colonIdx + 1);
                if (portStr.Length != 0 &&
                    int.TryParse(portStr.ToString(), out var n) &&    // TODO: (perf) ReadOnlySpan<char>
                    n > 0 && n <= 0xffff)
                {
                    port = n;
                }

                remoteSocket = remoteSocket.Remove(colonIdx);
            }

            if (double.IsNaN(timeout))
            {
                timeout = ctx.Configuration.Core.DefaultSocketTimeout;
            }

            // TODO:
            if (flags != SocketOptions.None && flags != SocketOptions.Asynchronous)
            {
                PhpException.ArgumentValueNotSupported("flags", (int)flags);
            }

            try
            {
                // workitem 299181; for remoteSocket as IPv4 address it results in IPv6 address
                //IPAddress address = System.Net.Dns.GetHostEntry(remoteSocket).AddressList[0];

                IPAddress address;
                if (!IPAddress.TryParse(remoteSocket, out address)) // if remoteSocket is not a valid IP address then lookup the DNS
                {
                    var addresses = System.Net.Dns.GetHostAddressesAsync(remoteSocket).Result;
                    if (addresses != null && addresses.Length != 0)
                    {
                        address = addresses[0];
                    }
                    else
                    {
                        throw new ArgumentException(nameof(remoteSocket));
                    }
                }

                var socket = new Socket(address.AddressFamily, SocketType.Stream, protocol);

                // socket.Connect(new IPEndPoint(address, port));
                if (socket.ConnectAsync(address, port).Wait((int)(timeout * 1000)))
                {
                    if (IsSsl)
                    {
                        var options = context.GetOptions("ssl");
                        if (options != null)
                        {
                            // TODO: provide parameters based on context[ssl][verify_peer|verify_peer_name|allow_self_signed|cafile]

                            options.TryGetValue("verify_peer", out var vpvalue);
                            options.TryGetValue("verify_peer_name", out var vpnvalue);
                            options.TryGetValue("allow_self_signed", out var assvalue);
                            options.TryGetValue("cafile", out var cafilevalue);

                            Debug.WriteLineIf(vpvalue.IsSet && !vpvalue, "ssl: verify_peer not supported");
                            Debug.WriteLineIf(vpnvalue.IsSet && (bool)vpnvalue, "ssl: verify_peer_name not supported");
                            Debug.WriteLineIf(assvalue.IsSet && !assvalue, "ssl: allow_self_signed not supported");
                            Debug.WriteLineIf(cafilevalue.IsSet, "ssl: cafile not supported");
                        }

                        var sslstream = new SslStream(new NetworkStream(socket, System.IO.FileAccess.ReadWrite, true), false,
                                                      null, //(sender, certificate, chain, sslPolicyErrors) => true,
                                                      null, //(sender, targetHost, localCertificates, remoteCertificate, acceptableIssuers) => ??,
                                                      EncryptionPolicy.AllowNoEncryption);

                        sslstream.AuthenticateAsClient(remoteSocket);

                        return(new NativeStream(ctx, sslstream, null, StreamAccessOptions.Read | StreamAccessOptions.Write, remoteSocket, context)
                        {
                            IsWriteBuffered = false,
                            IsReadBuffered = false,
                        });
                    }
                    else
                    {
                        return(new SocketStream(ctx, socket, remoteSocket, context, (flags & SocketOptions.Asynchronous) != 0));
                    }
                }
                else
                {
                    Debug.Assert(!socket.Connected);
                    PhpException.Throw(PhpError.Warning, string.Format(Resources.LibResources.socket_open_timeout, FileSystemUtils.StripPassword(remoteSocket)));
                    return(null);
                }
            }
            catch (SocketException e)
            {
                errno  = (int)e.SocketErrorCode;
                errstr = e.Message;
            }
            catch (System.Exception e)
            {
                errno  = -1;
                errstr = e.Message;
            }

            PhpException.Throw(PhpError.Warning, string.Format(Resources.LibResources.socket_open_error, FileSystemUtils.StripPassword(remoteSocket), errstr));
            return(null);
        }