Giter Club home page Giter Club logo

sockets's Introduction

sockets's People

Contributors

brettrtoomey avatar codetalks-new avatar czechboy0 avatar gwynne avatar hpux735 avatar imcotton avatar joannis avatar justmaku avatar lgaches avatar loganwright avatar matthiaskreileder avatar tanner0101 avatar tg908 avatar vzsg avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sockets's Issues

Create a "Sendable" and a "Receivable" protocol

This issue comprises the following steps

  • Create a "Sendable" protocol + extension which defines a function that serializes an object (so that it can be passed to the Socket write function)
  • Create a "Receivable" protocol + extension which defines a function that deserializes an object (so that it can be created from the bytes received by a socket read call)

One thread per port?

Is the current implementation use one thread per port/connection in sync rather than async or use DispatchSource/epoll to handle more workload?

WebSocket and Socks

So I've started up a TCP server with socks and a TCP Client with straight Javascript.

I get the initial request from my javascript client to my Socks server, but however I have no idea what to send back to the javascript client to allow it to upgrade and begin sending and receiving data. I've had this issue with various WebSocket clients of not being able to complete the connection...

In Socks what should I do? my server is basically the sample code you've provided, and the client is the sample code provided here: http://www.tutorialspoint.com/html5/html5_websocket.htm

ironically, I do get the disconnect message when I turn off my server.

I also tried using the Socks TCPSocket Client and I get roughly the same issue, I can send the first message, and then nothing works after that.

KeepAliveServer - quit when connection interrupted

There is definitely a problem in this function.
func closeClient(client: TCPClient) throws {
print("Closing client: (client.socket.address)")
connections.removeValue(forKey: client.socket.descriptor)
try client.close()

The client.socket descriptor is not recognized properly.

Some Help?

errno values aren't portable

In Error.swift, a lookup table errorDescriptions from errno value to error string is defined. It seems like it's a copy from Linux as there is

        11: "Resource temporarily unavailable",

which corresponds to EAGAIN I think. However, on the different Unices, the errno values aren't the same. for example on Linux

$ uname -srvmp
Linux 4.4.0-36-generic #55-Ubuntu SMP Thu Aug 11 18:01:55 UTC 2016 x86_64 x86_64
$ find /usr/include/ -name 'errno*' -exec grep EAGAIN '{}' \;
#define EWOULDBLOCK EAGAIN  /* Operation would block */
#define EAGAIN      11  /* Try again */

but on macOS:

$ uname -srvmp
 Darwin 16.1.0 Darwin Kernel Version 16.1.0: Sat Sep 17 23:18:50 PDT 2016; root:xnu-3789.20.47~8/DEVELOPMENT_X86_64 x86_64 i386
$ find /usr/include/ -name 'errno*' -exec grep EAGAIN '{}' \;
                    /* 11 was EAGAIN */
 #define    EAGAIN      35      /* Resource temporarily unavailable */
 #define    EWOULDBLOCK EAGAIN      /* Operation would block */

On macOS, 11 is EDEADLK:

#define EDEADLK     11      /* Resource deadlock avoided */
                    /* 11 was EAGAIN */

The correct way of generating this table would be to use strerror(3) or maybe include a platform specific version.

Address handling

  • create swift wrapper for address object
  • conversion from IP
  • conversion from hostname (both can be pulled from Redbird)
  • whole conversion from string (IP/hostname) to a valid address object

Adding join/leave to UDPSocket

It would be nice to add join/lease operations to the UDPSocket to facilitate the joining/leaving of multicast groups. Today the operation can be achieved by using setsockopt and could be already made a bit more user friendly by addressing the Issue #125. However, the most user-friendly solution would be to add join/leave operations straight on the UDPSocket class.

Below a possible implementation for the functionality:

  private func createMembershipRequest(_ ip: String, _ ifaddr: String) -> ip_mreq {
        let group = inet_addr(ip)
        let iface = inet_addr(ifaddr)
        var imr =  ip_mreq()
        imr.imr_multiaddr.s_addr = group;
        imr.imr_interface.s_addr = iface
        return imr
    }
    
    public func join(group mcastAddr: String, iface ifname: String) throws {
        let imr = self.createMembershipRequest(mcastAddr, ifname)
        try self.descriptor.setOption(level: IPPROTO_IP, name: IP_ADD_MEMBERSHIP, value: imr)
    
    }
    
    public func leave(group mcastAddr: String, iface ifname: String) throws {
        let imr = self.createMembershipRequest(mcastAddr, ifname)
        try self.descriptor.setOption(level: IPPROTO_IP, name: IP_ADD_MEMBERSHIP, value: imr)
    
    }

Ciao, Kydos

Make all of the SocksCoreExample* optional or move to example folders

Will need to simplify the dependencies when compiling Vapor and move those "example" to a separate folder will helps to reduce compiling time if that is not need.

In macOS 10.11.6 beta
Error on Xcode 8 beta with Toolchain on Swift June 20.

Compile Swift Module 'libc' (1 sources)
/Users/yanli/Downloads/vapor-0.13.0/Packages/Socks-0.8.1/Sources/SocksCore/Address.swift:94:45: warning: missing '.self' for reference to metatype of type 'sockaddr_in'
        case .inet: return socklen_t(sizeof(sockaddr_in))
                                            ^
                                                       .self
/Users/yanli/Downloads/vapor-0.13.0/Packages/Socks-0.8.1/Sources/SocksCore/Address.swift:95:46: warning: missing '.self' for reference to metatype of type 'sockaddr_in6'
        case .inet6: return socklen_t(sizeof(sockaddr_in6))
                                             ^
                                                         .self
/Users/yanli/Downloads/vapor-0.13.0/Packages/Socks-0.8.1/Sources/SocksCore/Address.swift:175:39: warning: missing '.self' for reference to metatype of type 'sockaddr_storage'
        var length = socklen_t(sizeof(sockaddr_storage))
                                      ^
                                                      .self
/Users/yanli/Downloads/vapor-0.13.0/Packages/Socks-0.8.1/Sources/SocksCore/Address.swift:188:39: warning: missing '.self' for reference to metatype of type 'sockaddr_storage'
        var length = socklen_t(sizeof(sockaddr_storage))
                                      ^
                                                      .self
/Users/yanli/Downloads/vapor-0.13.0/Packages/Socks-0.8.1/Sources/SocksCore/SocketOptions.swift:132:76: warning: missing '.self' for reference to metatype of type 'T'
        guard setsockopt(descriptor, level, name, &val, socklen_t(strideof(T))) != -1 else {
                                                                           ^
                                                                            .self
/Users/yanli/Downloads/vapor-0.13.0/Packages/Socks-0.8.1/Sources/SocksCore/SocketOptions.swift:138:41: warning: missing '.self' for reference to metatype of type 'T'
        var length = socklen_t(strideof(T))
                                        ^
                                         .self
/Users/yanli/Downloads/vapor-0.13.0/Packages/Socks-0.8.1/Sources/SocksCore/SocketOptions.swift:145:81: warning: missing '.self' for reference to metatype of type 'T'
            throw Error(.optionGetFailed(level: level, name: name, type: String(T)))
                                                                                ^
                                                                                 .self
/Users/yanli/Downloads/vapor-0.13.0/Packages/Socks-0.8.1/Sources/SocksCore/TCPSocket.swift:146:39: warning: missing '.self' for reference to metatype of type 'sockaddr_storage'
        var length = socklen_t(sizeof(sockaddr_storage))
                                      ^
                                                      .self
/Users/yanli/Downloads/vapor-0.13.0/Packages/Socks-0.8.1/Sources/SocksCore/UDPSocket.swift:48:39: warning: missing '.self' for reference to metatype of type 'sockaddr_storage'
        var length = socklen_t(sizeof(sockaddr_storage))
                                      ^
                                                      .self
Compile Swift Module 'Socks' (5 sources)
Compile Swift Module 'SocksCoreExampleTCPServer' (1 sources)
Compile Swift Module 'SocksCoreExampleTCPClient' (1 sources)
Compile Swift Module 'SocksExampleUDPServer' (1 sources)
Compile Swift Module 'SocksExampleUDPClient' (1 sources)
Compile Swift Module 'SocksExampleTCPServer' (1 sources)
Compile Swift Module 'SocksExampleTCPClient' (1 sources)
Compile Swift Module 'SocksCoreExampleTCPKeepAliveServer' (1 sources)
Linking .build/release/SocksCoreExampleTCPClient
ld: unexpected token: !tapi-tbd-v2 file '/Applications/Xcode-beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/usr/lib/libobjc.tbd' for architecture x86_64
Linking .build/release/SocksCoreExampleTCPServer
Linking .build/release/SocksExampleUDPClient
ld: unexpected token: !tapi-tbd-v2 file '/Applications/Xcode-beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/usr/lib/libobjc.tbd' for architecture x86_64
Linking .build/release/SocksExampleUDPServer
ld: unexpected token: !tapi-tbd-v2 file '/Applications/Xcode-beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/usr/lib/libobjc.tbd' for architecture x86_64
ld: unexpected token: !tapi-tbd-v2 file '/Applications/Xcode-beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/usr/lib/libobjc.tbd' for architecture x86_64
<unknown>:0: error: build had 4 command failures
error: exit(1): /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2016-06-20-a.xctoolchain/usr/bin/swift-build-tool -f /Users/yanli/Downloads/vapor-0.13.0/.build/release.yaml

`closed` property on socket

To perform HTTP parsing, serialization, and keep-alive on sockets it's important to know whether or not the socket has closed.

Most socket libs include a closed: Bool property on the socket.

This property may or may not need to be updated if the socket is closed by the client or closed in other ways. It might not be enough to set the flag to true when the socket is closed by calling .close()

How to enable UDP broadcast ?

When I send message to 192.18.1.255, I get this error: Error Socket failed with code 13 ("Permission denied") [sendFailedToSendAllBytes]. I believe it's because UDP broadcast is disabled by default, so how can I enable it ?

Making example executables private

Is there something we could do to make the example modules not show up in Xcode or be built during swift build?

Maybe .gitignore?

It's a bit overwhelming both during the build process and in Xcode.

screen shot 2016-06-06 at 10 17 38 am

Access control on setOption and resolve

Hello,

I wonder why the operations listed below have internal access control:

  • InternetAddress.resolve
  • Descriptor.(setOption | getOption | setBoolOption | getBoolOption)

The get/set Option operations are handy to change manipulate the socket options, for instance to join/leave a multicast group as well as to deal with the ADDR/PORT reuse.

Likewise the resolve operation on the InternetAddress is kind of essential to send a datagram over an unconnected UDP socket.

Ciao, Kydos

Support UNIX socket

This would eventually allow a Client to be initialised with a UNIX socket to perform the curl equivalent:

$ curl --unix-socket /var/run/docker.sock -H "Content-Type: application/json" \
  -d '{"Image": "alpine", "Cmd": ["echo", "hello world"]}' \
  -X POST http:/v1.24/containers/create
{"Id":"1c6594faf5","Warnings":null}

$ curl --unix-socket /var/run/docker.sock -X POST http:/v1.24/containers/1c6594faf5/start

$ curl --unix-socket /var/run/docker.sock -X POST http:/v1.24/containers/1c6594faf5/wait
{"StatusCode":0}

$ curl --unix-socket /var/run/docker.sock "http:/v1.24/containers/1c6594faf5/logs?stdout=1"
hello world

From the Vapor slack help channel:

  • Support AF_UNIX here
  • Make a stream AF_UNIX socket

Add Timeout functionality to Socket

When the client waited a reasonable amount of time to get a response from the server the client should be notified by a time-out signal and have the option to act on that event (e.g. retransmission or throwing an error).
The amount of time before a time-out happens should be configurable because it will vary depending on what the server side does and be set to a reasonable default value, e.g. 2 seconds.

StreamError: send(“There was a problem while sending data.“, Socket failed with code 9 (“Bad file descriptor”) [sendFailedToSendAllBytes])

I have a webpage that once the document is ready, starts a socket connection. The client only receives messages on a continuous basis and does not send any. I noticed an issue where if I refresh the page, I.E. closing the current socket and instantly opening a new one, I would get the error stated in the title on any attempts to send data to client.

Upon doing some research I found this example which locks onClose: https://github.com/IBM-Swift/Kitura-Chat-Server/blob/master/Sources/KituraChatServer/ChatService.swift

This resolved the issue. FYI this was using Vapor 1. Not sure if something has already been put in place with Vapor 2 to handle this situation. Sample code I used is below.

private let connectionsLock = DispatchSemaphore(value: 1)

ws.onClose = { [weak self] ws,_,_,_ in
			self?.lockConnectionsLock()
			  //remove connection logic
			  self?.unlockConnectionsLock()
}

  private func lockConnectionsLock() {
    _ = connectionsLock.wait(timeout: DispatchTime.distantFuture)
  }
  
  private func unlockConnectionsLock() {
    connectionsLock.signal()
  }

rare `UnsafeMutablePointer.initializeFrom` error

    let _raw: UnsafeMutablePointer<sockaddr_storage>
    var raw: UnsafeMutablePointer<sockaddr> {
        return UnsafeMutablePointer<sockaddr>(_raw)
    }

    init(raw: UnsafeMutablePointer<sockaddr_storage>) {
        let ptr = UnsafeMutablePointer<sockaddr_storage>.init(allocatingCapacity: 1)
        >>>>>> ptr.initializeFrom(raw, count: 1)
        self._raw = ptr
    }

fatal error: UnsafeMutablePointer.initializeFrom non-following overlapping range

This happens every once in a while. I can't reproduce, but putting it here in case anyone finds more information about it.

swift build error on armv7l debian

in trying to add your library as a swift package, i cannot successfully $ swift build.
i'm not sure if this is an issue due to the architecture i'm using, or if i'm doing something else completely innane. in any case, just wanted to report it. thanks in advance for any assistance!

-- mack

here is the output:
root@chip:~/macks-projects/blink/blink-package/Sources# swift build Compile Swift Module 'SocksCore' (14 sources) /root/macks-projects/blink/blink-package/Packages/Socks-0.7.0/Sources/SocksCore/FDSet.swift:34:20: error: cannot assign value of type '(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)' to type '(__fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask)' (aka '(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)') set.__fds_bits = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ <unknown>:0: error: build had 1 command failures error: exit(1): /root/macks-projects/pre-built-swift/swift-3.0/usr/bin/swift-build-tool -f /root/macks-projects/blink/blink-package/.build/debug.yaml

Cocoapods support

Hello there! You library is awesome but it's hard to use it in big iOS project without dependency manager support. Do you have plans to add Cocoapods support?

Make `Port` public in `Socks` module

In order to create an InternetAddress, Port needs to be accessible. Currently this is done by import SocksCore.

If Port had a public type alias like InternetAddress, SocksCore would not need to be imported.

Do you plan to support select ?

Hi @czechboy0 and @MatthiasKreileder !

Thanks for open sourcing this lib.
I love to read and study other people code it's a great way to learn.

Do you plan to support something like select(POSIX) which is crucial when building a server with socket ? Or libevent which work on Windows, OS X, Linux ?

Cheers,

Tests verification/finalize

More tests need to be added, specifically testing public APIs first (creating pipes could be useful for testing)

add `C7.Stream` support

Adding C7 Stream support would allow people to easily import Socks and plug it into their existing Stream code.

If this is something @czechboy0 thinks should be included in Socks, it should be pretty easy.

extension Socks.TCPClient: Stream {
    public enum Error: ErrorProtocol {
        case unsupported
    }

    public var closed: Bool {
        return socket.isClosed
    }

    public func send(_ data: Data, timingOut deadline: Double) throws {
        try send(bytes: data.bytes)
    }

    public func flush(timingOut deadline: Double) throws {
        throw Error.unsupported
    }

    public func receive(upTo byteCount: Int, timingOut deadline: Double) throws -> Data {
        let bytes = try receive(maxBytes: byteCount)
        return Data(bytes)
    }
}

This would also rely on #35 since C7 stream includes a closed property.

Wrong readAll() implementation?

I stumbled over the readAll() implementation of ReadableStream while reading through the code and for me it looks like the implementation is wrong as the documentation says:

Reads all bytes from the stream using a chunk size.

The current implementation reads bytes until one read call returns chunkSize or more bytes as lastSize will always be set to the byte count of the current read call.

So currently the following happens:

Example 1:

Socket state: --- 1024 Bytes ---
-> readAll()
-> first read(max: chunkSize) call
-> lastSize == 512
-> return 512 read bytes

512 bytes will be returned and not all

Example 2:

Socket state: --- 2 Bytes ---
-> readAll()
-> first read(max: chunkSize) call
-> lastSize == 2
-> try to read more, no more bytes left, lastSize == 0
-> lastSize stays 0 until there are more bytes to read. Will continue to read until there is one block (one read call) which returns 512 bytes.

Conclusion:

The current readAll() doesn't seem right to me. It looks like in many situations readAll() will never return (as there, for example, the socket won't ever have chunkSize bytes but far less).

If this behaviour is intended, sorry for the inconvenience, but if not, the following proposed solution should work.

Proposed solution:

public func readAll(chunkSize: Int = 512) throws -> Bytes {
    var lastSize = 1
    var bytes: Bytes = []

    while lastSize > 0 {
        let chunk = try read(max: chunkSize)
        bytes += chunk
        lastSize = chunk.count
    }

    return bytes
}

Failure to compile on Raspberry Pi

FD_ZERO is apparently defined as 32 bytes on an R/Pi 3:

uname -a
Linux ubuntu 4.4.0-1029-raspi2 #36-Ubuntu SMP Wed Oct 19 14:38:48 UTC 2016 armv7l armv7l armv7l GNU/Linux

I get the following compile error on an R/Pi 3 running latest version of ubuntu:

swift build
Cloning https://github.com/czechboy0/Redbird.git
HEAD is now at bb2ac0e Updated Swift to 08-15 (#38)
Resolved version: 0.10.0
Cloning https://github.com/czechboy0/Socks.git
HEAD is now at 460dc7f Merge pull request #73 from vapor/gm
Resolved version: 0.12.2
Compile Swift Module 'SocksCore' (14 sources)
/home/ubuntu/BlueCycles/linux/Packages/Socks-0.12.2/Sources/SocksCore/FDSet.swift:34:20: error: cannot assign value of type '(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)' to type '(__fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask, __fd_mask)' (aka '(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)')
  set.__fds_bits = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<unknown>:0: error: build had 1 command failures
error: exit(1): /usr/bin/swift-build-tool -f /home/ubuntu/BlueCycles/linux/.build/debug.yaml

Wrapping socket functions

  • socket() creates a new socket of a certain socket type, identified by an integer number, and allocates system resources to it
  • bind() is typically used on the server side, and associates a socket with a socket address structure, i.e. a specified local port number and IP address
  • listen() is used on the server side, and causes a bound TCP socket to enter listening state.
  • connect() is used on the client side, and assigns a free local port number to a socket. In case of a TCP socket, it causes an attempt to establish a new TCP connection
  • accept() is used on the server side. It accepts a received incoming attempt to create a new TCP connection from the remote client, and creates a new socket associated with the socket address pair of this connection
  • send() and recv(), or write() and read(), or sendto() and recvfrom(), are used for sending and receiving data to/from a remote socket
  • close() causes the system to release resources allocated to a socket. In case of TCP, the connection is terminated
  • gethostbyname() and gethostbyaddr() are used to resolve host names and addresses. IPv4 only.
  • select() is used to pend, waiting for one or more of a provided list of sockets to be ready to read, ready to write, or that have errors
  • poll() is used to check on the state of a socket in a set of sockets. The set can be tested to see if any socket can be written to, read from or if an error occurred
  • getsockopt() is used to retrieve the current value of a particular socket option for the specified socket
  • setsockopt() is used to set a particular socket option for the specified socket

Handle interruption (e.g. debugger signals) in socket calls

Currently when you e.g. set a breakpoint in Xcode while the code is blocking on select (or any other blocking socket call), it'll fail with error interrupted. We should check for this error and retry, or even better look into using the SA_RESTART flag so that the system calls are resumed, instead of aborted, when a signal arrives.

/cc @tannernelson

Adding set/getReusePort to RawSocket

Hello,

The semantics of SO_REUSEADDR and SO_REUSEPORT  is different and as a consequence both options are required when dealing with different cases. 

Below the code:

  public func setReusePort(_ newValue: Bool) throws {
      if isClosed { throw SocketsError(.socketIsClosed) }
      try descriptor.setBoolOption(level: SOL_SOCKET, name: SO_REUSEPORT, value: newValue)
  }
  
  public func getReusePort(_ newValue: Bool) throws -> Bool {
      if isClosed { throw SocketsError(.socketIsClosed) }
      return try descriptor.getBoolOption(level: SOL_SOCKET, name: SO_REUSEPORT)
  }

Ciao, Kydos

Crash when setsockopt fails

I've seen the following crash in a Vapor project I'm working on, running on OS X 10.11.6:
fatal error: 'try!' expression unexpectedly raised an error: Socket failed with code 22 ("Invalid argument") [optionSetFailed(65535, 4, "1")]

Here's part of the relevant stack trace:

0   libswiftCore.dylib              0x000000010a46a3c0 _TFs16_assertionFailedFTVs12StaticStringSSS_Su5flagsVs6UInt32_Os5Never + 144
1   libswiftCore.dylib              0x000000010a48e249 swift_unexpectedError_merged + 585
2   SocksCore                       0x000000010a33da7a _TFE9SocksCorePS_9RawSockets12reuseAddressSb + 234
3   SocksCore                       0x000000010a33feda _TFC9SocksCore17TCPInternetSocketcfzT10descriptorGSqVs5Int32_6configVS_12SocketConfig7addressCS_23ResolvedInternetAddress_S0_ + 394
4   SocksCore                       0x000000010a33fcbf _TFC9SocksCore17TCPInternetSocketCfzT10descriptorGSqVs5Int32_6configVS_12SocketConfig7addressCS_23ResolvedInternetAddress_S0_ + 159
5   SocksCore                       0x000000010a341300 _TFC9SocksCore17TCPInternetSocket6acceptfzT_S0_ + 448
6   Transport                       0x00000001092306c5 _TFC9Transport15TCPServerStream6acceptfzT_PS_6Stream_ + 149
7   Transport                       0x0000000109230ad3 _TTWC9Transport15TCPServerStreamS_12ServerStreamS_FS1_6acceptfzT_PS_6Stream_ + 51
8   HTTP                            0x0000000109f135a2 _TFC4HTTP6Server5startfzT9responderPS_9Responder_6errorsFOS_11ServerErrorT__T_ + 322
9   HTTP                            0x0000000109f149b3 _TTWu1_Rx9Transport12ServerStream_S_14TransferParser0_S_18TransferSerializerw_11MessageTypezC4HTTP7Requestw0_11MessageTypezCS4_8ResponserGCS4_6Serverxq_q0__S4_14ServerProtocolS4_FS9_5startfzT9responderPS4_9Responder_6errorsFOS4_11ServerErrorT__T_ + 67
10  HTTP                            0x0000000109efa0b0 _TZFE4HTTPPS_14ServerProtocol5startfzT4hostGSqSS_4portGSqSi_13securityLayerO9Transport13SecurityLayer9responderPS_9Responder_6errorsFOS_11ServerErrorT__T_ + 800
11  Vapor                           0x0000000109ff0cfb _TFC5Vapor7Droplet10bootServerfzT4nameSS12isLastServerSb_T_ + 7099
12  Vapor                           0x0000000109fec949 _TFC5Vapor7Droplet11bootServersfzT_T_ + 2425

The corresponding call is here in SocketOptions.swift:

try! Self.setBoolOption(descriptor: descriptor,
                   level: SOL_SOCKET,
                    name: SO_REUSEADDR,
                    value: newValue)

For some reason, setsockopt fails under certain circumstances, setOption throws and since the result of setBoolOption is forcibly unwrapped, I end up with a EXC_BAD_INSTRUCTION .

There's the following comment in the same file: When we have throwing property setters, remove the bangs below
Since throwing setters will not be available in the near future, would you be interested in a pull request where the properties are replaced by functions?

I will investigate the reason why setsockopt sometimes fails in my environment, but this might take a while.

Making the TCPClient class final

Making the TCPClient class final could potentially give some performance benefits since the compiler could use direct method calls.

Socks times out when receiving all data where the `messagedata.count % 512 == 0`

extension TCPReadableSocket {

    public func recv(maxBytes: Int = BufferCapacity) throws -> [UInt8] {
        let data = Bytes(capacity: maxBytes)
        let flags: Int32 = 0 //FIXME: allow setting flags with a Swift enum
        let receivedBytes = socket_recv(self.descriptor, data.rawBytes, data.capacity, flags)
        guard receivedBytes > -1 else { throw SocksError(.readFailed) }
        let finalBytes = data.characters[0..<receivedBytes]
        let out = Array(finalBytes.map({ UInt8($0) }))
        return out
    }

    public func recvAll() throws -> [UInt8] {
        var buffer: [UInt8] = []
        let chunkSize = 512
        while true {
            let newData = try self.recv(maxBytes: chunkSize)
            buffer.append(contentsOf: newData)
            if newData.count < chunkSize {
                break
            }
        }
        return buffer
    }
}

Here at the line if newData.count < chunkSize every user will get a timeout when the last chunk received (newData) has a count of 512 bytes. This has been haunting us for days until we realised the socket library might be at fault.

0 is a valid file descriptor

In Socket.swift, we find the following code:

    let descriptor = s_socket(cProtocolFam, cType, cProtocol)
    guard descriptor > 0 else { throw SocksError(.createSocketFailed) }

That's not quite correct as 0 is a valid file descriptor. It should be guard descriptor >= 0 .... This problem might appear elsewhere.

Add timeout to send/recv

Socks should expose a way to configure the SO_RCVTIMEO and SO_SNDTIMEO.

This will allow us to conform to the C7 spec:

public func send(_ data: Data, timingOut deadline: Double) throws

And it will help frameworks using Socks to avoid memory leaks from dead connections that never close.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.