├── .gitignore ├── .travis.yml ├── LICENSE ├── Package.swift ├── README.md ├── Sources ├── CTide │ ├── include │ │ └── module.modulemap │ ├── ip.c │ ├── ip.h │ ├── tcp.c │ ├── tcp.h │ ├── tide_swift.h │ ├── utils.c │ └── utils.h └── TCPIP │ ├── IP.swift │ ├── IPError.swift │ ├── TCPClientSocket.swift │ ├── TCPError.swift │ └── TCPServerSocket.swift └── Tests ├── IPTests.swift ├── TCPClientSocketTests.swift └── TCPTests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/xcode 2 | 3 | ### Xcode ### 4 | # Xcode 5 | # 6 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 7 | 8 | ## Build generated 9 | build/ 10 | DerivedData 11 | Carthage/ 12 | 13 | ## Various settings 14 | *.pbxuser 15 | !default.pbxuser 16 | *.mode1v3 17 | !default.mode1v3 18 | *.mode2v3 19 | !default.mode2v3 20 | *.perspectivev3 21 | !default.perspectivev3 22 | xcuserdata 23 | 24 | ## Other 25 | *.xccheckout 26 | *.moved-aside 27 | *.xcuserstate 28 | *.xcscmblueprint 29 | .build 30 | *.xcodeproj 31 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode7.1 3 | env: 4 | global: 5 | - LC_CTYPE=en_US.UTF-8 6 | - LANG=en_US.UTF-8 7 | matrix: 8 | - DESTINATION="OS=8.1,name=iPhone 4S" SCHEME="TCPIP iOS" SDK=iphonesimulator9.1 9 | - DESTINATION="OS=8.2,name=iPhone 5" SCHEME="TCPIP iOS" SDK=iphonesimulator9.1 10 | - DESTINATION="OS=8.3,name=iPhone 5S" SCHEME="TCPIP iOS" SDK=iphonesimulator9.1 11 | - DESTINATION="OS=8.4,name=iPhone 6" SCHEME="TCPIP iOS" SDK=iphonesimulator9.1 12 | - DESTINATION="OS=9.0,name=iPhone 6 Plus" SCHEME="TCPIP iOS" SDK=iphonesimulator9.1 13 | - DESTINATION="arch=x86_64" SCHEME="TCPIP OSX" SDK=macosx10.11 14 | before_install: 15 | - gem install xcpretty --no-rdoc --no-ri --no-document --quiet 16 | script: 17 | - set -o pipefail 18 | - xcodebuild -version 19 | - xcodebuild -project TCPIP.xcodeproj -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" 20 | -configuration Debug ONLY_ACTIVE_ARCH=NO GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES 21 | GCC_GENERATE_TEST_COVERAGE_FILES=YES test | xcpretty -c 22 | - xcodebuild -project TCPIP.xcodeproj -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" 23 | -configuration Release ONLY_ACTIVE_ARCH=NO GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES 24 | GCC_GENERATE_TEST_COVERAGE_FILES=YES test | xcpretty -c 25 | after_success: 26 | - bash <(curl -s https://codecov.io/bash) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Zewo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | import PackageDescription 2 | 3 | let package = Package( 4 | name: "TCPIP", 5 | targets: [Target(name: "TCPIP", dependencies: ["CTide"])] 6 | ) 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [Deprecated] TCPIP 2 | ===== 3 | 4 | [![Swift][swift-badge]][swift-url] 5 | [![Platform][platform-badge]][platform-url] 6 | [![License][mit-badge]][mit-url] 7 | [![Slack][slack-badge]][slack-url] 8 | 9 | **TCPIP** is a TCP/IP library for **Swift 3.0**. 10 | 11 | ## Features 12 | 13 | - [x] No `Foundation` dependency (**Linux ready**) 14 | - [x] Local IP 15 | - [x] Remote IP 16 | - [x] TCP Server Socket 17 | - [x] TCP Client Socket 18 | 19 | ## Usage 20 | 21 | `IP` 22 | ---- 23 | 24 | ```swift 25 | // local 26 | do { 27 | // all network interfaces 28 | let ip1 = try IP(port: 5555, mode: .IPV4) 29 | 30 | // specific network interface 31 | let ip2 = try IP(networkInterface: "en0", port: 5555, mode: .IPV6) 32 | } catch { 33 | // something bad happened :( 34 | } 35 | 36 | // remote 37 | do { 38 | let ip3 = try IP(address: "127.0.0.1", port: 5555, mode: .IPV4) 39 | } catch { 40 | // something bad happened :( 41 | } 42 | ``` 43 | 44 | `TCP` 45 | ---- 46 | 47 | ```swift 48 | // server 49 | do { 50 | let ip = try IP(port: 5555) 51 | let serverSocket = try TCPServerSocket(ip: ip) 52 | let clientSocket = try serverSocket.accept() 53 | 54 | let yo = try clientSocket.receiveString(untilDelimiter: "\n") 55 | } catch { 56 | // something bad happened :( 57 | } 58 | 59 | // client 60 | do { 61 | let ip = try IP(address: "127.0.0.1", port: 5555) 62 | let clientSocket = try TCPClientSocket(ip: ip) 63 | 64 | // calls to send append the data to an internal 65 | // buffer to minimize system calls 66 | try clientSocket.sendString("yo\n") 67 | // flush actually sends all data in the buffer 68 | try clientSocket.flush() 69 | } catch { 70 | // something bad happened :( 71 | } 72 | ``` 73 | 74 | ### Package 75 | 76 | ```swift 77 | import PackageDescription 78 | 79 | let package = Package( 80 | dependencies: [ 81 | .Package(url: "https://github.com/Zewo/TCPIP.git", majorVersion: 0, minor: 4) 82 | ] 83 | ) 84 | 85 | ``` 86 | 87 | ### Command Line Application 88 | 89 | To use **TCPIP** in a command line application: 90 | 91 | - Install the [Swift Command Line Application](https://github.com/Zewo/Swift-Command-Line-Application-Template) Xcode template 92 | 93 | ## Community 94 | 95 | [![Slack](http://s13.postimg.org/ybwy92ktf/Slack.png)](https://zewo-slackin.herokuapp.com) 96 | 97 | Join us on [Slack](https://zewo-slackin.herokuapp.com). 98 | 99 | License 100 | ------- 101 | 102 | **TCPIP** is released under the MIT license. See LICENSE for details. 103 | 104 | [swift-badge]: https://img.shields.io/badge/Swift-3.0-orange.svg?style=flat 105 | [swift-url]: https://swift.org 106 | [platform-badge]: https://img.shields.io/badge/Platform-Mac%20%26%20Linux-lightgray.svg?style=flat 107 | [platform-url]: https://swift.org 108 | [mit-badge]: https://img.shields.io/badge/License-MIT-blue.svg?style=flat 109 | [mit-url]: https://tldrlegal.com/license/mit-license 110 | [slack-image]: http://s13.postimg.org/ybwy92ktf/Slack.png 111 | [slack-badge]: https://zewo-slackin.herokuapp.com/badge.svg 112 | [slack-url]: http://slack.zewo.io 113 | -------------------------------------------------------------------------------- /Sources/CTide/include/module.modulemap: -------------------------------------------------------------------------------- 1 | module CTide { 2 | header "../tide_swift.h" 3 | link "Tide" 4 | export * 5 | } -------------------------------------------------------------------------------- /Sources/CTide/ip.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2015 Martin Sustrik 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), 7 | to deal in the Software without restriction, including without limitation 8 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | and/or sell copies of the Software, and to permit persons to whom 10 | the Software is furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included 13 | in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | IN THE SOFTWARE. 22 | 23 | */ 24 | 25 | #if defined __linux__ 26 | #define _GNU_SOURCE 27 | #include 28 | #endif 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #if !defined __sun 38 | #include 39 | #endif 40 | #include 41 | 42 | #include "ip.h" 43 | #include "tcp.h" 44 | #include "utils.h" 45 | 46 | CT_ASSERT(sizeof(ipaddr) >= sizeof(struct sockaddr_in)); 47 | CT_ASSERT(sizeof(ipaddr) >= sizeof(struct sockaddr_in6)); 48 | 49 | static ipaddr tide_ipany(int port, int mode) 50 | { 51 | ipaddr addr; 52 | if(tide_slow(port < 0 || port > 0xffff)) { 53 | ((struct sockaddr*)&addr)->sa_family = AF_UNSPEC; 54 | errno = EINVAL; 55 | return addr; 56 | } 57 | if (mode == 0 || mode == IPADDR_IPV4 || mode == IPADDR_PREF_IPV4) { 58 | struct sockaddr_in *ipv4 = (struct sockaddr_in*)&addr; 59 | ipv4->sin_family = AF_INET; 60 | ipv4->sin_addr.s_addr = htonl(INADDR_ANY); 61 | ipv4->sin_port = htons((uint16_t)port); 62 | } 63 | else { 64 | struct sockaddr_in6 *ipv6 = (struct sockaddr_in6*)&addr; 65 | ipv6->sin6_family = AF_INET6; 66 | memcpy(&ipv6->sin6_addr, &in6addr_any, sizeof(in6addr_any)); 67 | ipv6->sin6_port = htons((uint16_t)port); 68 | } 69 | errno = 0; 70 | return addr; 71 | } 72 | 73 | /* Convert literal IPv4 address to a binary one. */ 74 | static ipaddr tide_ipv4_literal(const char *addr, int port) { 75 | ipaddr raddr; 76 | struct sockaddr_in *ipv4 = (struct sockaddr_in*)&raddr; 77 | int rc = inet_pton(AF_INET, addr, &ipv4->sin_addr); 78 | tide_assert(rc >= 0); 79 | if(rc == 1) { 80 | ipv4->sin_family = AF_INET; 81 | ipv4->sin_port = htons((uint16_t)port); 82 | errno = 0; 83 | return raddr; 84 | } 85 | ipv4->sin_family = AF_UNSPEC; 86 | errno = EINVAL; 87 | return raddr; 88 | } 89 | 90 | /* Convert literal IPv6 address to a binary one. */ 91 | static ipaddr tide_ipv6_literal(const char *addr, int port) { 92 | ipaddr raddr; 93 | struct sockaddr_in6 *ipv6 = (struct sockaddr_in6*)&raddr; 94 | int rc = inet_pton(AF_INET6, addr, &ipv6->sin6_addr); 95 | tide_assert(rc >= 0); 96 | if(rc == 1) { 97 | ipv6->sin6_family = AF_INET6; 98 | ipv6->sin6_port = htons((uint16_t)port); 99 | errno = 0; 100 | return raddr; 101 | } 102 | ipv6->sin6_family = AF_UNSPEC; 103 | errno = EINVAL; 104 | return raddr; 105 | } 106 | 107 | /* Convert literal IPv4 or IPv6 address to a binary one. */ 108 | static ipaddr tide_ipliteral(const char *addr, int port, int mode) { 109 | ipaddr raddr; 110 | struct sockaddr *sa = (struct sockaddr*)&raddr; 111 | if(tide_slow(!addr || port < 0 || port > 0xffff)) { 112 | sa->sa_family = AF_UNSPEC; 113 | errno = EINVAL; 114 | return raddr; 115 | } 116 | switch(mode) { 117 | case IPADDR_IPV4: 118 | return tide_ipv4_literal(addr, port); 119 | case IPADDR_IPV6: 120 | return tide_ipv6_literal(addr, port); 121 | case 0: 122 | case IPADDR_PREF_IPV4: 123 | raddr = tide_ipv4_literal(addr, port); 124 | if(errno == 0) 125 | return raddr; 126 | return tide_ipv6_literal(addr, port); 127 | case IPADDR_PREF_IPV6: 128 | raddr = tide_ipv6_literal(addr, port); 129 | if(errno == 0) 130 | return raddr; 131 | return tide_ipv4_literal(addr, port); 132 | default: 133 | tide_assert(0); 134 | } 135 | } 136 | 137 | int tide_ipfamily(ipaddr addr) { 138 | return ((struct sockaddr*)&addr)->sa_family; 139 | } 140 | 141 | int tide_iplen(ipaddr addr) { 142 | return tide_ipfamily(addr) == AF_INET ? 143 | sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); 144 | } 145 | 146 | int tide_ipport(ipaddr addr) { 147 | return ntohs(tide_ipfamily(addr) == AF_INET ? 148 | ((struct sockaddr_in*)&addr)->sin_port : 149 | ((struct sockaddr_in6*)&addr)->sin6_port); 150 | } 151 | 152 | ipaddr iplocal(const char *name, int port, int mode) { 153 | if(!name) 154 | return tide_ipany(port, mode); 155 | ipaddr addr = tide_ipliteral(name, port, mode); 156 | #if defined __sun 157 | return addr; 158 | #else 159 | if(errno == 0) 160 | return addr; 161 | /* Address is not a literal. It must be an interface name then. */ 162 | struct ifaddrs *ifaces = NULL; 163 | int rc = getifaddrs(&ifaces); 164 | tide_assert (rc == 0); 165 | tide_assert (ifaces); 166 | /* Find first IPv4 and first IPv6 address. */ 167 | struct ifaddrs *ipv4 = NULL; 168 | struct ifaddrs *ipv6 = NULL; 169 | struct ifaddrs *it; 170 | for(it = ifaces; it != NULL; it = it->ifa_next) { 171 | if(!it->ifa_addr) 172 | continue; 173 | if(strcmp(it->ifa_name, name) != 0) 174 | continue; 175 | switch(it->ifa_addr->sa_family) { 176 | case AF_INET: 177 | tide_assert(!ipv4); 178 | ipv4 = it; 179 | break; 180 | case AF_INET6: 181 | tide_assert(!ipv6); 182 | ipv6 = it; 183 | break; 184 | } 185 | if(ipv4 && ipv6) 186 | break; 187 | } 188 | /* Choose the correct address family based on mode. */ 189 | switch(mode) { 190 | case IPADDR_IPV4: 191 | ipv6 = NULL; 192 | break; 193 | case IPADDR_IPV6: 194 | ipv4 = NULL; 195 | break; 196 | case 0: 197 | case IPADDR_PREF_IPV4: 198 | if(ipv4) 199 | ipv6 = NULL; 200 | break; 201 | case IPADDR_PREF_IPV6: 202 | if(ipv6) 203 | ipv4 = NULL; 204 | break; 205 | default: 206 | tide_assert(0); 207 | } 208 | if(ipv4) { 209 | struct sockaddr_in *inaddr = (struct sockaddr_in*)&addr; 210 | memcpy(inaddr, ipv4->ifa_addr, sizeof (struct sockaddr_in)); 211 | inaddr->sin_port = htons(port); 212 | freeifaddrs(ifaces); 213 | errno = 0; 214 | return addr; 215 | } 216 | if(ipv6) { 217 | struct sockaddr_in6 *inaddr = (struct sockaddr_in6*)&addr; 218 | memcpy(inaddr, ipv6->ifa_addr, sizeof (struct sockaddr_in6)); 219 | inaddr->sin6_port = htons(port); 220 | freeifaddrs(ifaces); 221 | errno = 0; 222 | return addr; 223 | } 224 | freeifaddrs(ifaces); 225 | ((struct sockaddr*)&addr)->sa_family = AF_UNSPEC; 226 | errno = ENODEV; 227 | return addr; 228 | #endif 229 | } 230 | 231 | ipaddr ipremote(const char *name, int port, int mode) { 232 | ipaddr addr = tide_ipliteral(name, port, mode); 233 | if(errno == 0) 234 | return addr; 235 | struct addrinfo request; 236 | struct addrinfo *result; 237 | memset(&request, 0, sizeof(request)); 238 | request.ai_family = AF_UNSPEC; 239 | request.ai_socktype = SOCK_STREAM; 240 | int rc = getaddrinfo(name, NULL, &request, &result); 241 | if(tide_slow(rc != 0)) { 242 | if(rc == EAI_AGAIN || rc == EAI_MEMORY) { 243 | errno = ENOMEM; 244 | return addr; 245 | } 246 | tide_assert(0); 247 | } 248 | struct addrinfo *ipv4 = NULL; 249 | struct addrinfo *ipv6 = NULL; 250 | struct addrinfo *it = result; 251 | while(it) { 252 | if(!ipv4 && it->ai_family == AF_INET) 253 | ipv4 = it; 254 | if(!ipv6 && it->ai_family == AF_INET6) 255 | ipv6 = it; 256 | if(ipv4 && ipv6) 257 | break; 258 | it = it->ai_next; 259 | } 260 | switch(mode) { 261 | case IPADDR_IPV4: 262 | ipv6 = NULL; 263 | break; 264 | case IPADDR_IPV6: 265 | ipv4 = NULL; 266 | break; 267 | case 0: 268 | case IPADDR_PREF_IPV4: 269 | if(ipv4) 270 | ipv6 = NULL; 271 | break; 272 | case IPADDR_PREF_IPV6: 273 | if(ipv6) 274 | ipv4 = NULL; 275 | break; 276 | default: 277 | tide_assert(0); 278 | } 279 | if(ipv4) { 280 | struct sockaddr_in *inaddr = (struct sockaddr_in*)&addr; 281 | memcpy(inaddr, ipv4->ai_addr, sizeof (struct sockaddr_in)); 282 | inaddr->sin_port = htons(port); 283 | } 284 | if(ipv6) { 285 | struct sockaddr_in6 *inaddr = (struct sockaddr_in6*)&addr; 286 | memcpy(inaddr, ipv6->ai_addr, sizeof (struct sockaddr_in6)); 287 | inaddr->sin6_port = htons(port); 288 | } 289 | freeaddrinfo(result); 290 | errno = 0; 291 | return addr; 292 | } 293 | 294 | -------------------------------------------------------------------------------- /Sources/CTide/ip.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2015 Martin Sustrik 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), 7 | to deal in the Software without restriction, including without limitation 8 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | and/or sell copies of the Software, and to permit persons to whom 10 | the Software is furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included 13 | in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | IN THE SOFTWARE. 22 | 23 | */ 24 | 25 | #ifndef tide_IP_INCLUDED 26 | #define tide_IP_INCLUDED 27 | 28 | #include "tcp.h" 29 | 30 | int tide_ipfamily(ipaddr addr); 31 | int tide_iplen(ipaddr addr); 32 | int tide_ipport(ipaddr addr); 33 | 34 | #endif 35 | 36 | -------------------------------------------------------------------------------- /Sources/CTide/tcp.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2015 Martin Sustrik 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), 7 | to deal in the Software without restriction, including without limitation 8 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | and/or sell copies of the Software, and to permit persons to whom 10 | the Software is furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included 13 | in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | IN THE SOFTWARE. 22 | 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include "ip.h" 37 | #include "tcp.h" 38 | #include "utils.h" 39 | 40 | /* The buffer size is based on typical Ethernet MTU (1500 bytes). Making it 41 | smaller would yield small suboptimal packets. Making it higher would bring 42 | no substantial benefit. The value is made smaller to account for IPv4/IPv6 43 | and TCP headers. Few more bytes are subtracted to account for any possible 44 | IP or TCP options */ 45 | #ifndef tide_TCP_BUFLEN 46 | #define tide_TCP_BUFLEN (1500 - 68) 47 | #endif 48 | 49 | enum tide_tcptype { 50 | tide_TCPLISTENER, 51 | tide_TCPCONN 52 | }; 53 | 54 | struct tide_tcpsock { 55 | enum tide_tcptype type; 56 | }; 57 | 58 | struct tide_tcplistener { 59 | struct tide_tcpsock sock; 60 | int fd; 61 | int port; 62 | }; 63 | 64 | struct tide_tcpconn { 65 | struct tide_tcpsock sock; 66 | int fd; 67 | size_t ifirst; 68 | size_t ilen; 69 | size_t olen; 70 | char ibuf[tide_TCP_BUFLEN]; 71 | char obuf[tide_TCP_BUFLEN]; 72 | }; 73 | 74 | static void tide_tcptune(int s) { 75 | /* Allow re-using the same local address rapidly. */ 76 | int opt = 1; 77 | int rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)); 78 | tide_assert(rc == 0); 79 | /* If possible, prevent SIGPIPE signal when writing to the connection 80 | already closed by the peer. */ 81 | #ifdef SO_NOSIGPIPE 82 | opt = 1; 83 | rc = setsockopt (s, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof (opt)); 84 | tide_assert (rc == 0 || errno == EINVAL); 85 | #endif 86 | } 87 | 88 | static void tcpconn_init(struct tide_tcpconn *conn, int fd) { 89 | conn->sock.type = tide_TCPCONN; 90 | conn->fd = fd; 91 | conn->ifirst = 0; 92 | conn->ilen = 0; 93 | conn->olen = 0; 94 | } 95 | 96 | tcpsock tcplisten(ipaddr addr, int backlog) { 97 | /* Open the listening socket. */ 98 | int s = socket(tide_ipfamily(addr), SOCK_STREAM, 0); 99 | if(s == -1) 100 | return NULL; 101 | tide_tcptune(s); 102 | 103 | /* Start listening. */ 104 | int rc = bind(s, (struct sockaddr*)&addr, tide_iplen(addr)); 105 | if(rc != 0) 106 | return NULL; 107 | rc = listen(s, backlog); 108 | if(rc != 0) 109 | return NULL; 110 | 111 | /* If the user requested an ephemeral port, 112 | retrieve the port number assigned by the OS now. */ 113 | int port = tide_ipport(addr); 114 | if(!port == 0) { 115 | ipaddr baddr; 116 | socklen_t len = sizeof(ipaddr); 117 | rc = getsockname(s, (struct sockaddr*)&baddr, &len); 118 | if(rc == -1) { 119 | int err = errno; 120 | close(s); 121 | errno = err; 122 | return NULL; 123 | } 124 | port = tide_ipport(baddr); 125 | } 126 | 127 | /* Create the object. */ 128 | struct tide_tcplistener *l = malloc(sizeof(struct tide_tcplistener)); 129 | if(!l) { 130 | close(s); 131 | errno = ENOMEM; 132 | return NULL; 133 | } 134 | l->sock.type = tide_TCPLISTENER; 135 | l->fd = s; 136 | l->port = port; 137 | errno = 0; 138 | return &l->sock; 139 | } 140 | 141 | int tcpport(tcpsock s) { 142 | if(s->type != tide_TCPLISTENER) 143 | tide_panic("trying to get port from a socket that isn't listening"); 144 | struct tide_tcplistener *l = (struct tide_tcplistener*)s; 145 | return l->port; 146 | } 147 | 148 | tcpsock tcpaccept(tcpsock s) { 149 | if(s->type != tide_TCPLISTENER) 150 | tide_panic("trying to accept on a socket that isn't listening"); 151 | struct tide_tcplistener *l = (struct tide_tcplistener*)s; 152 | int as = accept(l->fd, NULL, NULL); 153 | if (as >= 0) { 154 | tide_tcptune(as); 155 | struct tide_tcpconn *conn = malloc(sizeof(struct tide_tcpconn)); 156 | if(!conn) { 157 | close(as); 158 | errno = ENOMEM; 159 | return NULL; 160 | } 161 | tcpconn_init(conn, as); 162 | errno = 0; 163 | return (tcpsock)conn; 164 | } 165 | return NULL; 166 | } 167 | 168 | tcpsock tcpconnect(ipaddr addr) { 169 | /* Open a socket. */ 170 | int s = socket(tide_ipfamily(addr), SOCK_STREAM, 0); 171 | if(s == -1) 172 | return NULL; 173 | tide_tcptune(s); 174 | 175 | /* Connect to the remote endpoint. */ 176 | int rc = connect(s, (struct sockaddr*)&addr, tide_iplen(addr)); 177 | if(rc != 0) 178 | return NULL; 179 | /* Create the object. */ 180 | struct tide_tcpconn *conn = malloc(sizeof(struct tide_tcpconn)); 181 | if(!conn) { 182 | close(s); 183 | errno = ENOMEM; 184 | return NULL; 185 | } 186 | tcpconn_init(conn, s); 187 | errno = 0; 188 | return (tcpsock)conn; 189 | } 190 | 191 | size_t tcpsend(tcpsock s, const void *buf, size_t len) { 192 | if(s->type != tide_TCPCONN) 193 | tide_panic("trying to send to an unconnected socket"); 194 | struct tide_tcpconn *conn = (struct tide_tcpconn*)s; 195 | 196 | /* If it fits into the output buffer copy it there and be done. */ 197 | if(conn->olen + len <= tide_TCP_BUFLEN) { 198 | memcpy(&conn->obuf[conn->olen], buf, len); 199 | conn->olen += len; 200 | errno = 0; 201 | return len; 202 | } 203 | 204 | /* If it doesn't fit, flush the output buffer first. */ 205 | tcpflush(s); 206 | if(errno != 0) 207 | return 0; 208 | 209 | /* Try to fit it into the buffer once again. */ 210 | if(conn->olen + len <= tide_TCP_BUFLEN) { 211 | memcpy(&conn->obuf[conn->olen], buf, len); 212 | conn->olen += len; 213 | errno = 0; 214 | return len; 215 | } 216 | 217 | /* The data chunk to send is longer than the output buffer. 218 | Let's do the sending in-place. */ 219 | char *pos = (char*)buf; 220 | size_t remaining = len; 221 | while(remaining) { 222 | ssize_t sz = send(conn->fd, pos, remaining, 0); 223 | if(sz == -1) 224 | return len - remaining; 225 | pos += sz; 226 | remaining -= sz; 227 | } 228 | return len; 229 | } 230 | 231 | void tcpflush(tcpsock s) { 232 | if(s->type != tide_TCPCONN) 233 | tide_panic("trying to send to an unconnected socket"); 234 | struct tide_tcpconn *conn = (struct tide_tcpconn*)s; 235 | if(!conn->olen) { 236 | errno = 0; 237 | return; 238 | } 239 | char *pos = conn->obuf; 240 | size_t remaining = conn->olen; 241 | while(remaining) { 242 | ssize_t sz = send(conn->fd, pos, remaining, 0); 243 | if(sz == -1) 244 | return; 245 | pos += sz; 246 | remaining -= sz; 247 | } 248 | conn->olen = 0; 249 | errno = 0; 250 | } 251 | 252 | size_t tcprecv(tcpsock s, void *buf, size_t len) { 253 | if(s->type != tide_TCPCONN) 254 | tide_panic("trying to receive from an unconnected socket"); 255 | struct tide_tcpconn *conn = (struct tide_tcpconn*)s; 256 | /* If there's enough data in the buffer it's easy. */ 257 | if(conn->ilen >= len) { 258 | memcpy(buf, &conn->ibuf[conn->ifirst], len); 259 | conn->ifirst += len; 260 | conn->ilen -= len; 261 | errno = 0; 262 | return len; 263 | } 264 | 265 | /* Let's move all the data from the buffer first. */ 266 | char *pos = (char*)buf; 267 | size_t remaining = len; 268 | memcpy(pos, &conn->ibuf[conn->ifirst], conn->ilen); 269 | pos += conn->ilen; 270 | remaining -= conn->ilen; 271 | conn->ifirst = 0; 272 | conn->ilen = 0; 273 | 274 | tide_assert(remaining); 275 | while(1) { 276 | if(remaining > tide_TCP_BUFLEN) { 277 | /* If we still have a lot to read try to read it in one go directly 278 | into the destination buffer. */ 279 | ssize_t sz = recv(conn->fd, pos, remaining, 0); 280 | if(!sz) { 281 | errno = ECONNRESET; 282 | return len - remaining; 283 | } 284 | if(sz == -1) { 285 | return len - remaining; 286 | } 287 | if((size_t)sz == remaining) { 288 | errno = 0; 289 | return len; 290 | } 291 | pos += sz; 292 | remaining -= sz; 293 | } 294 | else { 295 | /* If we have just a little to read try to read the full connection 296 | buffer to minimise the number of system calls. */ 297 | ssize_t sz = recv(conn->fd, conn->ibuf, tide_TCP_BUFLEN, 0); 298 | if(!sz) { 299 | errno = ECONNRESET; 300 | return len - remaining; 301 | } 302 | if(sz == -1) { 303 | return len - remaining; 304 | } 305 | if((size_t)sz < remaining) { 306 | memcpy(pos, conn->ibuf, sz); 307 | pos += sz; 308 | remaining -= sz; 309 | conn->ifirst = 0; 310 | conn->ilen = 0; 311 | } 312 | else { 313 | memcpy(pos, conn->ibuf, remaining); 314 | conn->ifirst = remaining; 315 | conn->ilen = sz - remaining; 316 | errno = 0; 317 | return len; 318 | } 319 | } 320 | } 321 | } 322 | 323 | size_t tcprecvuntil(tcpsock s, void *buf, size_t len, const char *delims, size_t delimcount) { 324 | if(s->type != tide_TCPCONN) 325 | tide_panic("trying to receive from an unconnected socket"); 326 | char *pos = (char*)buf; 327 | size_t i; 328 | for(i = 0; i != len; ++i, ++pos) { 329 | size_t res = tcprecv(s, pos, 1); 330 | if(res == 1) { 331 | size_t j; 332 | for(j = 0; j != delimcount; ++j) 333 | if(*pos == delims[j]) 334 | return i + 1; 335 | } 336 | if (errno != 0) 337 | return i + res; 338 | } 339 | errno = ENOBUFS; 340 | return len; 341 | } 342 | 343 | void tcpclose(tcpsock s) { 344 | if(s->type == tide_TCPLISTENER) { 345 | struct tide_tcplistener *l = (struct tide_tcplistener*)s; 346 | int rc = close(l->fd); 347 | tide_assert(rc == 0); 348 | free(l); 349 | return; 350 | } 351 | if(s->type == tide_TCPCONN) { 352 | struct tide_tcpconn *c = (struct tide_tcpconn*)s; 353 | int rc = close(c->fd); 354 | tide_assert(rc == 0); 355 | free(c); 356 | return; 357 | } 358 | tide_assert(0); 359 | } 360 | 361 | tcpsock tcpattach(int fd, int listening) { 362 | if(listening == 0) { 363 | struct tide_tcpconn *conn = malloc(sizeof(struct tide_tcpconn)); 364 | if(!conn) { 365 | errno = ENOMEM; 366 | return NULL; 367 | } 368 | tcpconn_init(conn, fd); 369 | errno = 0; 370 | return (tcpsock)conn; 371 | } 372 | /* It's a listening socket. Find out the port it is listening on. */ 373 | ipaddr addr; 374 | socklen_t sz = sizeof(ipaddr); 375 | int rc = getsockname(fd, (struct sockaddr*)&addr, &sz); 376 | if(rc == -1) 377 | return NULL; 378 | struct tide_tcplistener *l = malloc(sizeof(struct tide_tcplistener)); 379 | if(!l) { 380 | errno = ENOMEM; 381 | return NULL; 382 | } 383 | l->sock.type = tide_TCPLISTENER; 384 | l->fd = fd; 385 | l->port = tide_ipport(addr); 386 | errno = 0; 387 | return &l->sock; 388 | } 389 | 390 | int tcpdetach(tcpsock s) { 391 | if(s->type == tide_TCPLISTENER) { 392 | struct tide_tcplistener *l = (struct tide_tcplistener*)s; 393 | int fd = l->fd; 394 | free(l); 395 | return fd; 396 | } 397 | if(s->type == tide_TCPCONN) { 398 | int fd = ((struct tide_tcpconn*)s)->fd; 399 | free(s); 400 | return fd; 401 | } 402 | tide_assert(0); 403 | } 404 | 405 | int tcpfd(tcpsock s) { 406 | if(s->type == tide_TCPLISTENER) { 407 | return ((struct tide_tcplistener*)s)->fd; 408 | } 409 | if(s->type == tide_TCPCONN) { 410 | return ((struct tide_tcpconn*)s)->fd; 411 | } 412 | tide_assert(0); 413 | } 414 | 415 | -------------------------------------------------------------------------------- /Sources/CTide/tcp.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2015 Martin Sustrik 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), 7 | to deal in the Software without restriction, including without limitation 8 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | and/or sell copies of the Software, and to permit persons to whom 10 | the Software is furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included 13 | in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | IN THE SOFTWARE. 22 | 23 | */ 24 | 25 | #ifndef TIDE_H_INCLUDED 26 | #define TIDE_H_INCLUDED 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | /******************************************************************************/ 33 | /* Symbol visibility */ 34 | /******************************************************************************/ 35 | 36 | #if defined TIDE_NO_EXPORTS 37 | # define TIDE_EXPORT 38 | #else 39 | # if defined _WIN32 40 | # if defined TIDE_EXPORTS 41 | # define TIDE_EXPORT __declspec(dllexport) 42 | # else 43 | # define TIDE_EXPORT __declspec(dllimport) 44 | # endif 45 | # else 46 | # if defined __SUNPRO_C 47 | # define TIDE_EXPORT __global 48 | # elif (defined __GNUC__ && __GNUC__ >= 4) || \ 49 | defined __INTEL_COMPILER || defined __clang__ 50 | # define TIDE_EXPORT __attribute__ ((visibility("default"))) 51 | # else 52 | # define TIDE_EXPORT 53 | # endif 54 | # endif 55 | #endif 56 | 57 | /******************************************************************************/ 58 | /* IP address library */ 59 | /******************************************************************************/ 60 | 61 | #define IPADDR_IPV4 1 62 | #define IPADDR_IPV6 2 63 | #define IPADDR_PREF_IPV4 3 64 | #define IPADDR_PREF_IPV6 4 65 | 66 | typedef struct {char data[32];} ipaddr; 67 | 68 | TIDE_EXPORT ipaddr iplocal(const char *name, int port, int mode); 69 | TIDE_EXPORT ipaddr ipremote(const char *name, int port, int mode); 70 | 71 | /******************************************************************************/ 72 | /* TCP library */ 73 | /******************************************************************************/ 74 | 75 | typedef struct tide_tcpsock *tcpsock; 76 | 77 | TIDE_EXPORT tcpsock tcplisten(ipaddr addr, int backlog); 78 | TIDE_EXPORT int tcpport(tcpsock s); 79 | TIDE_EXPORT tcpsock tcpaccept(tcpsock s); 80 | TIDE_EXPORT tcpsock tcpconnect(ipaddr addr); 81 | TIDE_EXPORT size_t tcpsend(tcpsock s, const void *buf, size_t len); 82 | TIDE_EXPORT void tcpflush(tcpsock s); 83 | TIDE_EXPORT size_t tcprecv(tcpsock s, void *buf, size_t len); 84 | TIDE_EXPORT size_t tcprecvuntil(tcpsock s, void *buf, size_t len, const char *delims, size_t delimcount); 85 | TIDE_EXPORT void tcpclose(tcpsock s); 86 | TIDE_EXPORT tcpsock tcpattach(int fd, int listening); 87 | TIDE_EXPORT int tcpdetach(tcpsock s); 88 | TIDE_EXPORT int tcpfd(tcpsock s); 89 | 90 | #endif 91 | 92 | -------------------------------------------------------------------------------- /Sources/CTide/tide_swift.h: -------------------------------------------------------------------------------- 1 | // tide_swift.h 2 | // 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2015 Zewo 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | #ifndef tide_swift_h 25 | #define tide_swift_h 26 | 27 | #import "tcp.h" 28 | 29 | #endif /* tide_swift_h */ 30 | -------------------------------------------------------------------------------- /Sources/CTide/utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2015 Martin Sustrik 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), 7 | to deal in the Software without restriction, including without limitation 8 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | and/or sell copies of the Software, and to permit persons to whom 10 | the Software is furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included 13 | in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | IN THE SOFTWARE. 22 | 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #include "utils.h" 30 | 31 | void tide_panic(const char *text) { 32 | fprintf(stderr, "panic: %s\n", text); 33 | abort(); 34 | } 35 | -------------------------------------------------------------------------------- /Sources/CTide/utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2015 Martin Sustrik 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), 7 | to deal in the Software without restriction, including without limitation 8 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | and/or sell copies of the Software, and to permit persons to whom 10 | the Software is furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included 13 | in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | IN THE SOFTWARE. 22 | 23 | */ 24 | 25 | #ifndef UTILS_H_INCLUDED 26 | #define UTILS_H_INCLUDED 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | /* Cause panic. */ 34 | void tide_panic(const char *text); 35 | 36 | /* Compile-time assert. */ 37 | #define CT_ASSERT_HELPER2(prefix, line) \ 38 | prefix##line 39 | #define CT_ASSERT_HELPER1(prefix, line) \ 40 | CT_ASSERT_HELPER2(prefix, line) 41 | #define CT_ASSERT(x) \ 42 | typedef int CT_ASSERT_HELPER1(ct_assert_,__COUNTER__) [(x) ? 1 : -1] 43 | 44 | #if defined __GNUC__ || defined __llvm__ 45 | #define tide_fast(x) __builtin_expect(!!(x), 1) 46 | #define tide_slow(x) __builtin_expect(!!(x), 0) 47 | #else 48 | #define tide_fast(x) (x) 49 | #define tide_slow(x) (x) 50 | #endif 51 | 52 | /* Define our own assert. This way we are sure that it stays in place even 53 | if the standard C assert would be thrown away by the compiler. */ 54 | #define tide_assert(x) \ 55 | do {\ 56 | if (tide_slow(!(x))) {\ 57 | fprintf(stderr, "Assert failed: " #x " (%s:%d)\n",\ 58 | __FILE__, __LINE__);\ 59 | fflush(stderr);\ 60 | abort();\ 61 | }\ 62 | } while (0) 63 | 64 | #endif 65 | 66 | -------------------------------------------------------------------------------- /Sources/TCPIP/IP.swift: -------------------------------------------------------------------------------- 1 | // IP.swift 2 | // 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2015 Zewo 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | import CTide 26 | 27 | public enum IPMode { 28 | case IPV4 29 | case IPV6 30 | case IPV4Prefered 31 | case IPV6Prefered 32 | } 33 | 34 | extension IPMode { 35 | var code: Int32 { 36 | switch self { 37 | case .IPV4: return 1 38 | case .IPV6: return 2 39 | case .IPV4Prefered: return 3 40 | case .IPV6Prefered: return 4 41 | } 42 | } 43 | } 44 | 45 | public struct IP { 46 | let address: ipaddr 47 | 48 | public init(port: Int, mode: IPMode = .IPV4) throws { 49 | self.address = iplocal(nil, Int32(port), mode.code) 50 | 51 | if errno != 0 { 52 | let description = IPError.lastSystemErrorDescription 53 | throw IPError(description: description) 54 | } 55 | } 56 | 57 | public init(networkInterface: String, port: Int, mode: IPMode = .IPV4) throws { 58 | self.address = iplocal(networkInterface, Int32(port), mode.code) 59 | 60 | if errno != 0 { 61 | let description = IPError.lastSystemErrorDescription 62 | throw IPError(description: description) 63 | } 64 | } 65 | 66 | public init(address: String, port: Int, mode: IPMode = .IPV4) throws { 67 | self.address = ipremote(address, Int32(port), mode.code) 68 | 69 | if errno != 0 { 70 | let description = IPError.lastSystemErrorDescription 71 | throw IPError(description: description) 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Sources/TCPIP/IPError.swift: -------------------------------------------------------------------------------- 1 | // IPError.swift 2 | // 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2015 Zewo 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | import CTide 26 | 27 | public struct IPError : ErrorProtocol, CustomStringConvertible { 28 | public let description: String 29 | 30 | init(description: String, bytesProcessed: Int? = nil) { 31 | self.description = description 32 | } 33 | 34 | static var lastSystemErrorDescription: String { 35 | return String(validatingUTF8: strerror(errno))! 36 | } 37 | } -------------------------------------------------------------------------------- /Sources/TCPIP/TCPClientSocket.swift: -------------------------------------------------------------------------------- 1 | // TCPClientSocket.swift 2 | // 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2015 Zewo 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | import CTide 26 | 27 | public final class TCPClientSocket { 28 | private var socket: tcpsock 29 | public private(set) var closed = false 30 | 31 | public var fileDescriptor: Int32 { 32 | return tcpfd(socket) 33 | } 34 | 35 | init(socket: tcpsock) { 36 | self.socket = socket 37 | } 38 | 39 | public init(ip: IP) throws { 40 | self.socket = tcpconnect(ip.address) 41 | 42 | if errno != 0 { 43 | closed = true 44 | let description = TCPError.lastSystemErrorDescription 45 | throw TCPError(description: description) 46 | } 47 | } 48 | 49 | public init(fileDescriptor: Int32) throws { 50 | self.socket = tcpattach(fileDescriptor, 0) 51 | 52 | if errno != 0 { 53 | closed = true 54 | let description = TCPError.lastSystemErrorDescription 55 | throw TCPError(description: description) 56 | } 57 | } 58 | 59 | deinit { 60 | close() 61 | } 62 | 63 | public func send(data: UnsafeMutablePointer, length: Int) throws { 64 | if closed { 65 | throw TCPError(description: "Closed socket") 66 | } 67 | 68 | let bytesProcessed = tcpsend(socket, data, length) 69 | 70 | if errno != 0 { 71 | let description = TCPError.lastSystemErrorDescription 72 | throw TCPError(description: description, bytesProcessed: bytesProcessed) 73 | } 74 | } 75 | 76 | public func flush() throws { 77 | if closed { 78 | throw TCPError(description: "Closed socket") 79 | } 80 | 81 | tcpflush(socket) 82 | 83 | if errno != 0 { 84 | let description = TCPError.lastSystemErrorDescription 85 | throw TCPError(description: description) 86 | } 87 | } 88 | 89 | public func receive(bufferSize bufferSize: Int = 256) throws -> [Int8] { 90 | if closed { 91 | throw TCPError(description: "Closed socket") 92 | } 93 | 94 | var buffer: [Int8] = [Int8](repeating: 0, count: bufferSize) 95 | let bytesProcessed = tcprecv(socket, &buffer, bufferSize) 96 | 97 | if errno != 0 { 98 | let description = TCPError.lastSystemErrorDescription 99 | throw TCPError(description: description, bytesProcessed: bytesProcessed) 100 | } 101 | 102 | return Array(buffer[0 ..< bytesProcessed]) 103 | } 104 | 105 | public func receive(bufferSize bufferSize: Int = 256, untilDelimiter delimiter: String) throws -> [Int8] { 106 | if closed { 107 | throw TCPError(description: "Closed socket") 108 | } 109 | 110 | var buffer: [Int8] = [Int8](repeating: 0, count: bufferSize) 111 | let bytesProcessed = tcprecvuntil(socket, &buffer, bufferSize, delimiter, delimiter.utf8.count) 112 | 113 | if errno != 0 && errno != ENOBUFS { 114 | let description = TCPError.lastSystemErrorDescription 115 | throw TCPError(description: description, bytesProcessed: bytesProcessed) 116 | } 117 | 118 | return Array(buffer[0 ..< bytesProcessed]) 119 | } 120 | 121 | public func attach(fileDescriptor: Int32) throws { 122 | if !closed { 123 | tcpclose(socket) 124 | } 125 | 126 | socket = tcpattach(fileDescriptor, 0) 127 | 128 | if errno != 0 { 129 | let description = TCPError.lastSystemErrorDescription 130 | throw TCPError(description: description) 131 | } 132 | 133 | closed = false 134 | } 135 | 136 | public func detach() throws -> Int32 { 137 | if closed { 138 | throw TCPError(description: "Closed socket") 139 | } 140 | 141 | closed = true 142 | return tcpdetach(socket) 143 | } 144 | 145 | public func close() { 146 | if !closed { 147 | closed = true 148 | tcpclose(socket) 149 | } 150 | } 151 | } 152 | 153 | extension TCPClientSocket { 154 | public func sendString(string: String) throws { 155 | var data = string.utf8.map { Int8($0) } 156 | try send(&data, length: data.count) 157 | } 158 | 159 | public func send(data: [Int8]) throws { 160 | var data = data 161 | try send(&data, length: data.count) 162 | } 163 | 164 | public func receiveString(bufferSize bufferSize: Int = 256, untilDelimiter delimiter: String) throws -> String? { 165 | var response = try receive(bufferSize: bufferSize, untilDelimiter: delimiter) 166 | response.append(0) 167 | return String(validatingUTF8: response) 168 | } 169 | 170 | public func receiveString(bufferSize bufferSize: Int = 256) throws -> String? { 171 | var response = try receive(bufferSize: bufferSize) 172 | response.append(0) 173 | return String(validatingUTF8: response) 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /Sources/TCPIP/TCPError.swift: -------------------------------------------------------------------------------- 1 | // TCPError.swift 2 | // 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2015 Zewo 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | import CTide 26 | 27 | public struct TCPError : ErrorProtocol, CustomStringConvertible { 28 | public let description: String 29 | public let bytesProcessed: Int? 30 | 31 | init(description: String, bytesProcessed: Int? = nil) { 32 | self.description = description 33 | self.bytesProcessed = bytesProcessed 34 | } 35 | 36 | static var lastSystemErrorDescription: String { 37 | return String(validatingUTF8: strerror(errno))! 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sources/TCPIP/TCPServerSocket.swift: -------------------------------------------------------------------------------- 1 | // TCPServerSocket.swift 2 | // 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2015 Zewo 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | import CTide 26 | 27 | public final class TCPServerSocket { 28 | private var socket: tcpsock 29 | public private(set) var closed = false 30 | 31 | public var port: Int { 32 | return Int(tcpport(self.socket)) 33 | } 34 | 35 | public var fileDescriptor: Int32 { 36 | return tcpfd(socket) 37 | } 38 | 39 | public init(ip: IP, backlog: Int = 10) throws { 40 | self.socket = tcplisten(ip.address, Int32(backlog)) 41 | 42 | if errno != 0 { 43 | closed = true 44 | let description = TCPError.lastSystemErrorDescription 45 | throw TCPError(description: description) 46 | } 47 | } 48 | 49 | public init(fileDescriptor: Int32) throws { 50 | self.socket = tcpattach(fileDescriptor, 1) 51 | 52 | if errno != 0 { 53 | closed = true 54 | let description = TCPError.lastSystemErrorDescription 55 | throw TCPError(description: description) 56 | } 57 | } 58 | 59 | deinit { 60 | close() 61 | } 62 | 63 | public func accept() throws -> TCPClientSocket { 64 | if closed { 65 | throw TCPError(description: "Closed socket") 66 | } 67 | 68 | let clientSocket = tcpaccept(socket) 69 | 70 | if errno != 0 { 71 | let description = TCPError.lastSystemErrorDescription 72 | throw TCPError(description: description) 73 | } 74 | 75 | return TCPClientSocket(socket: clientSocket) 76 | } 77 | 78 | public func attach(fileDescriptor: Int32) throws { 79 | if !closed { 80 | tcpclose(socket) 81 | } 82 | 83 | socket = tcpattach(fileDescriptor, 1) 84 | 85 | if errno != 0 { 86 | closed = true 87 | let description = TCPError.lastSystemErrorDescription 88 | throw TCPError(description: description) 89 | } 90 | 91 | closed = false 92 | } 93 | 94 | public func detach() throws -> Int32 { 95 | if closed { 96 | throw TCPError(description: "Closed socket") 97 | } 98 | 99 | closed = true 100 | return tcpdetach(socket) 101 | } 102 | 103 | public func close() { 104 | if !closed { 105 | closed = true 106 | tcpclose(socket) 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Tests/IPTests.swift: -------------------------------------------------------------------------------- 1 | // IPTests.swift 2 | // 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2015 Zewo 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | import XCTest 26 | import TCPIP 27 | 28 | class IPTests: XCTestCase { 29 | 30 | func testLocalIPV4() { 31 | do { 32 | let _ = try IP(port: 5555, mode: .IPV4) 33 | } catch { 34 | XCTAssert(false) 35 | } 36 | } 37 | 38 | func testLocalIPV6() { 39 | do { 40 | let _ = try IP(port: 5555, mode: .IPV6) 41 | } catch { 42 | XCTAssert(false) 43 | } 44 | } 45 | 46 | func testLocalIPV4Preferd() { 47 | do { 48 | let _ = try IP(port: 5555, mode: .IPV4Prefered) 49 | } catch { 50 | XCTAssert(false) 51 | } 52 | } 53 | 54 | func testLocalIPV6Prefered() { 55 | do { 56 | let _ = try IP(port: 5555, mode: .IPV6Prefered) 57 | } catch { 58 | XCTAssert(false) 59 | } 60 | } 61 | 62 | func testNetworkInterfaceIPV4() { 63 | do { 64 | let _ = try IP(networkInterface: "lo0", port: 5555, mode: .IPV4) 65 | } catch { 66 | XCTAssert(true) 67 | } 68 | } 69 | 70 | func testNetworkInterfaceIPV6() { 71 | do { 72 | let _ = try IP(networkInterface: "lo0", port: 5555, mode: .IPV6) 73 | } catch { 74 | XCTAssert(true) 75 | } 76 | } 77 | 78 | func testNetworkInterfaceIPV4Prefered() { 79 | do { 80 | let _ = try IP(networkInterface: "lo0", port: 5555, mode: .IPV4Prefered) 81 | } catch { 82 | XCTAssert(true) 83 | } 84 | } 85 | 86 | func testNetworkInterfaceIPV6Prefered() { 87 | do { 88 | let _ = try IP(networkInterface: "lo0", port: 5555, mode: .IPV6Prefered) 89 | } catch { 90 | XCTAssert(true) 91 | } 92 | } 93 | 94 | func testRemoteIPV4() { 95 | do { 96 | let _ = try IP(address: "127.0.0.1", port: 5555, mode: .IPV4) 97 | } catch { 98 | XCTAssert(true) 99 | } 100 | } 101 | 102 | func testRemoteIPV6() { 103 | do { 104 | let _ = try IP(address: "::1", port: 5555, mode: .IPV6) 105 | } catch { 106 | XCTAssert(true) 107 | } 108 | } 109 | 110 | func testRemoteIPV4Prefered() { 111 | do { 112 | let _ = try IP(address: "127.0.0.1", port: 5555, mode: .IPV4Prefered) 113 | } catch { 114 | XCTAssert(true) 115 | } 116 | } 117 | 118 | func testRemoteIPV6Prefered() { 119 | do { 120 | let _ = try IP(address: "::1", port: 5555, mode: .IPV6Prefered) 121 | } catch { 122 | XCTAssert(true) 123 | } 124 | } 125 | 126 | func testInvalidPortIPV4() { 127 | do { 128 | let _ = try IP(port: 70000, mode: .IPV4) 129 | } catch { 130 | XCTAssert(true) 131 | } 132 | } 133 | 134 | func testInvalidPortIPV6() { 135 | do { 136 | let _ = try IP(port: 70000, mode: .IPV6) 137 | } catch { 138 | XCTAssert(true) 139 | } 140 | } 141 | 142 | func testInvalidPortIPV4Prefered() { 143 | do { 144 | let _ = try IP(port: 70000, mode: .IPV4Prefered) 145 | } catch { 146 | XCTAssert(true) 147 | } 148 | } 149 | 150 | func testInvalidPortIPV6Prefered() { 151 | do { 152 | let _ = try IP(port: 70000, mode: .IPV6Prefered) 153 | } catch { 154 | XCTAssert(true) 155 | } 156 | } 157 | 158 | func testInvalidNetworkInterfaceIPV4() { 159 | do { 160 | let _ = try IP(networkInterface: "yo-yo ma", port: 5555, mode: .IPV4) 161 | } catch { 162 | XCTAssert(true) 163 | } 164 | } 165 | 166 | func testInvalidNetworkInterfaceIPV6() { 167 | do { 168 | let _ = try IP(networkInterface: "yo-yo ma", port: 5555, mode: .IPV6) 169 | } catch { 170 | XCTAssert(true) 171 | } 172 | } 173 | 174 | func testInvalidNetworkInterfaceIPV4Prefered() { 175 | do { 176 | let _ = try IP(networkInterface: "yo-yo ma", port: 5555, mode: .IPV4Prefered) 177 | } catch { 178 | XCTAssert(true) 179 | } 180 | } 181 | 182 | func testInvalidNetworkInterfaceIPV6Prefered() { 183 | do { 184 | let _ = try IP(networkInterface: "yo-yo ma", port: 5555, mode: .IPV6Prefered) 185 | } catch { 186 | XCTAssert(true) 187 | } 188 | } 189 | 190 | func testRemoteInvalidPortIPV4() { 191 | do { 192 | let _ = try IP(address: "127.0.0.1", port: 70000, mode: .IPV4) 193 | } catch { 194 | XCTAssert(true) 195 | } 196 | } 197 | 198 | func testRemoteInvalidPortIPV6() { 199 | do { 200 | let _ = try IP(address: "::1", port: 70000, mode: .IPV6) 201 | } catch { 202 | XCTAssert(true) 203 | } 204 | } 205 | 206 | func testRemoteInvalidPortIPV4Prefered() { 207 | do { 208 | let _ = try IP(address: "127.0.0.1", port: 70000, mode: .IPV4Prefered) 209 | } catch { 210 | XCTAssert(true) 211 | } 212 | } 213 | 214 | func testRemoteInvalidPortIPV6Prefered() { 215 | do { 216 | let _ = try IP(address: "::1", port: 70000, mode: .IPV6Prefered) 217 | } catch { 218 | XCTAssert(true) 219 | } 220 | } 221 | 222 | } -------------------------------------------------------------------------------- /Tests/TCPClientSocketTests.swift: -------------------------------------------------------------------------------- 1 | // TCPClientSocketTests.swift 2 | // 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2015 Zewo 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | import XCTest 26 | import TCPIP 27 | import Dispatch 28 | 29 | class TCPClientSocketTests: XCTestCase { 30 | 31 | func testConnectionRefused() { 32 | var called = false 33 | 34 | do { 35 | let ip = try IP(address: "127.0.0.1", port: 5555) 36 | let _ = try TCPClientSocket(ip: ip) 37 | XCTAssert(false) 38 | } catch { 39 | called = true 40 | } 41 | 42 | XCTAssert(called) 43 | } 44 | 45 | func testInitWithFileDescriptor() { 46 | var called = false 47 | 48 | do { 49 | let _ = try TCPClientSocket(fileDescriptor: 0) 50 | called = true 51 | } catch { 52 | XCTAssert(false) 53 | } 54 | 55 | XCTAssert(called) 56 | } 57 | 58 | func testSendClosedSocket() { 59 | var called = false 60 | 61 | func client(port: Int) { 62 | do { 63 | let ip = try IP(address: "127.0.0.1", port: port) 64 | let clientSocket = try TCPClientSocket(ip: ip) 65 | clientSocket.close() 66 | try clientSocket.send(nil, length: 0) 67 | XCTAssert(false) 68 | } catch { 69 | called = true 70 | } 71 | } 72 | 73 | do { 74 | let port = 5555 75 | let ip = try IP(port: port) 76 | let serverSocket = try TCPServerSocket(ip: ip) 77 | async { 78 | client(port) 79 | } 80 | try serverSocket.accept() 81 | NSThread.sleepForTimeInterval(0.1) 82 | } catch { 83 | XCTAssert(false) 84 | } 85 | 86 | XCTAssert(called) 87 | } 88 | 89 | func testFlushClosedSocket() { 90 | var called = false 91 | 92 | func client(port: Int) { 93 | do { 94 | let ip = try IP(address: "127.0.0.1", port: port) 95 | let clientSocket = try TCPClientSocket(ip: ip) 96 | clientSocket.close() 97 | try clientSocket.flush() 98 | XCTAssert(false) 99 | } catch { 100 | called = true 101 | } 102 | } 103 | 104 | do { 105 | let port = 5555 106 | let ip = try IP(port: port) 107 | let serverSocket = try TCPServerSocket(ip: ip) 108 | async { 109 | client(port) 110 | } 111 | try serverSocket.accept() 112 | NSThread.sleepForTimeInterval(0.1) 113 | } catch { 114 | XCTAssert(false) 115 | } 116 | 117 | XCTAssert(called) 118 | } 119 | 120 | func testReceiveClosedSocket() { 121 | var called = false 122 | 123 | func client(port: Int) { 124 | do { 125 | let ip = try IP(address: "127.0.0.1", port: port) 126 | let clientSocket = try TCPClientSocket(ip: ip) 127 | clientSocket.close() 128 | try clientSocket.receive() 129 | XCTAssert(false) 130 | } catch { 131 | called = true 132 | } 133 | } 134 | 135 | do { 136 | let port = 5555 137 | let ip = try IP(port: port) 138 | let serverSocket = try TCPServerSocket(ip: ip) 139 | async { 140 | client(port) 141 | } 142 | try serverSocket.accept() 143 | NSThread.sleepForTimeInterval(0.1) 144 | } catch { 145 | XCTAssert(false) 146 | } 147 | 148 | XCTAssert(called) 149 | } 150 | 151 | func testReceiveUntilClosedSocket() { 152 | var called = false 153 | 154 | func client(port: Int) { 155 | do { 156 | let ip = try IP(address: "127.0.0.1", port: port) 157 | let clientSocket = try TCPClientSocket(ip: ip) 158 | clientSocket.close() 159 | try clientSocket.receive(untilDelimiter: "") 160 | XCTAssert(false) 161 | } catch { 162 | called = true 163 | } 164 | } 165 | 166 | do { 167 | let port = 5555 168 | let ip = try IP(port: port) 169 | let serverSocket = try TCPServerSocket(ip: ip) 170 | async { 171 | client(port) 172 | } 173 | try serverSocket.accept() 174 | NSThread.sleepForTimeInterval(0.1) 175 | } catch { 176 | XCTAssert(false) 177 | } 178 | 179 | XCTAssert(called) 180 | } 181 | 182 | func testDetachClosedSocket() { 183 | var called = false 184 | 185 | func client(port: Int) { 186 | do { 187 | let ip = try IP(address: "127.0.0.1", port: port) 188 | let clientSocket = try TCPClientSocket(ip: ip) 189 | clientSocket.close() 190 | try clientSocket.detach() 191 | XCTAssert(false) 192 | } catch { 193 | called = true 194 | } 195 | } 196 | 197 | do { 198 | let port = 5555 199 | let ip = try IP(port: port) 200 | let serverSocket = try TCPServerSocket(ip: ip) 201 | async { 202 | client(port) 203 | } 204 | try serverSocket.accept() 205 | NSThread.sleepForTimeInterval(0.1) 206 | } catch { 207 | XCTAssert(false) 208 | } 209 | 210 | XCTAssert(called) 211 | } 212 | 213 | func testSendBufferPointerInvalidLength() { 214 | var called = false 215 | 216 | func client(port: Int) { 217 | do { 218 | let ip = try IP(address: "127.0.0.1", port: port) 219 | let clientSocket = try TCPClientSocket(ip: ip) 220 | try clientSocket.send(nil, length: -1) 221 | XCTAssert(false) 222 | } catch { 223 | called = true 224 | } 225 | } 226 | 227 | do { 228 | let port = 5555 229 | let ip = try IP(port: port) 230 | let serverSocket = try TCPServerSocket(ip: ip) 231 | async { 232 | client(port) 233 | } 234 | try serverSocket.accept() 235 | NSThread.sleepForTimeInterval(0.1) 236 | } catch { 237 | XCTAssert(false) 238 | } 239 | 240 | XCTAssert(called) 241 | } 242 | 243 | func testSendReceive() { 244 | var called = false 245 | 246 | func client(port: Int) { 247 | do { 248 | let ip = try IP(address: "127.0.0.1", port: port) 249 | let clientSocket = try TCPClientSocket(ip: ip) 250 | try clientSocket.send([123]) 251 | try clientSocket.flush() 252 | } catch { 253 | XCTAssert(false) 254 | } 255 | } 256 | 257 | do { 258 | let port = 5555 259 | let ip = try IP(port: port) 260 | let serverSocket = try TCPServerSocket(ip: ip) 261 | async { 262 | client(port) 263 | } 264 | let clientSocket = try serverSocket.accept() 265 | let data = try clientSocket.receive(bufferSize: 1) 266 | called = true 267 | XCTAssert(data == [123]) 268 | clientSocket.close() 269 | } catch { 270 | XCTAssert(false) 271 | } 272 | 273 | XCTAssert(called) 274 | } 275 | 276 | } 277 | -------------------------------------------------------------------------------- /Tests/TCPTests.swift: -------------------------------------------------------------------------------- 1 | // TCPTests.swift 2 | // 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2015 Zewo 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | import XCTest 26 | import TCPIP 27 | import Dispatch 28 | 29 | func async(f: Void -> Void) { 30 | let queue = dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0) 31 | dispatch_async(queue, f) 32 | } 33 | 34 | class TCPTests: XCTestCase { 35 | 36 | func testTCPClientServer() { 37 | func client(port: Int) { 38 | do { 39 | let ip = try IP(address: "127.0.0.1", port: port) 40 | let clientSocket = try TCPClientSocket(ip: ip) 41 | 42 | let fileDescriptor = try clientSocket.detach() 43 | XCTAssert(fileDescriptor != -1) 44 | try clientSocket.attach(fileDescriptor) 45 | 46 | 47 | let data = try clientSocket.receiveString(bufferSize: 3) 48 | XCTAssert(data == "ABC") 49 | 50 | try clientSocket.sendString("123\n45\n6789") 51 | try clientSocket.flush() 52 | 53 | clientSocket.close() 54 | } catch { 55 | XCTAssert(false) 56 | } 57 | } 58 | 59 | do { 60 | let ip = try IP(port: 5555) 61 | let listeningSocket = try TCPServerSocket(ip: ip) 62 | 63 | let fileDescriptor = try listeningSocket.detach() 64 | XCTAssert(fileDescriptor != -1) 65 | try listeningSocket.attach(fileDescriptor) 66 | XCTAssert(listeningSocket.port == 5555) 67 | 68 | async { 69 | client(5555) 70 | } 71 | 72 | let clientSocket = try listeningSocket.accept() 73 | 74 | try clientSocket.sendString("ABC") 75 | try clientSocket.flush() 76 | 77 | let first = try clientSocket.receiveString(untilDelimiter: "\n") 78 | XCTAssert(first == "123\n") 79 | 80 | let second = try clientSocket.receiveString(untilDelimiter: "\n") 81 | XCTAssert(second == "45\n") 82 | 83 | let third = try clientSocket.receiveString(bufferSize: 3, untilDelimiter: "\n") 84 | XCTAssert(third == "678") 85 | 86 | listeningSocket.close() 87 | clientSocket.close() 88 | } catch { 89 | XCTAssert(false) 90 | } 91 | } 92 | 93 | } 94 | 95 | --------------------------------------------------------------------------------