├── .github
└── workflows
│ └── ci.yml
├── .gitignore
├── LICENSE
├── README.md
├── pom.xml
└── src
├── main
└── java
│ └── jnr
│ └── unixsocket
│ ├── BindHandler.java
│ ├── Common.java
│ ├── Credentials.java
│ ├── Native.java
│ ├── SockAddrUnix.java
│ ├── Ucred.java
│ ├── UnixDatagramChannel.java
│ ├── UnixDatagramSocket.java
│ ├── UnixServerSocket.java
│ ├── UnixServerSocketChannel.java
│ ├── UnixSocket.java
│ ├── UnixSocketAddress.java
│ ├── UnixSocketChannel.java
│ ├── UnixSocketOptions.java
│ └── impl
│ ├── AbstractNativeDatagramChannel.java
│ ├── AbstractNativeServerSocketChannel.java
│ ├── AbstractNativeSocketChannel.java
│ └── Common.java
└── test
├── java
└── jnr
│ └── unixsocket
│ ├── BasicDatagramFunctionalityTest.java
│ ├── BasicFunctionalityTest.java
│ ├── ChannelOptionsTest.java
│ ├── CredentialsFunctionalTest.java
│ ├── ForFDTest.java
│ ├── SocketInteropTest.java
│ ├── TcpChannelsApiSocketPair.java
│ ├── TcpSocketsApiSocketPair.java
│ ├── TestSocketPair.java
│ ├── UnixDatagramChannelTest.java
│ ├── UnixSocketChannelTest.java
│ ├── UnixSocketPair.java
│ └── example
│ ├── LocalSyslogClient.java
│ ├── UnixClient.java
│ └── UnixServer.java
└── resources
└── background.sh
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a Java project with Maven
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
3 |
4 | name: Java CI with Maven
5 |
6 | on:
7 | push:
8 | branches: [ master ]
9 | pull_request:
10 | branches: [ master ]
11 |
12 | jobs:
13 | jdk8:
14 |
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - uses: actions/checkout@v2
19 | - name: Set up JDK 8
20 | uses: actions/setup-java@v1.4.3
21 | with:
22 | java-version: 8
23 | - name: Build with Maven
24 | run: mvn -B package --file pom.xml
25 |
26 | jdk11:
27 |
28 | runs-on: ubuntu-latest
29 |
30 | steps:
31 | - uses: actions/checkout@v2
32 | - name: Set up JDK 11
33 | uses: actions/setup-java@v1.4.3
34 | with:
35 | java-version: 11
36 | - name: Build with Maven
37 | run: mvn -B package --file pom.xml
38 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # maven
2 | target/
3 | *.versionsBackup
4 | *.releaseBackup
5 |
6 | # eclipse
7 | .classpath
8 | .project
9 | .settings
10 |
11 | # intellij / android studio
12 | *.iml
13 | *.ipr
14 | *.iws
15 | .idea/
16 |
17 | # common junk
18 | *.log
19 | *.diff
20 | *.patch
21 | *.sw[a-p]
22 | *.bak
23 | *.backup
24 | *.debug
25 | *.dump
26 |
27 | # vim
28 | .*.sw[a-p]
29 | *~
30 | ~*
31 |
32 | # Mac filesystem dust
33 | .DS_Store
34 |
35 | # pmd
36 | .pmdruleset
37 | .pmd
38 |
39 | # merge tooling
40 | *.orig
41 | *.rej
42 | \.orig$
43 | \.orig\..*$
44 | \.chg\..*$
45 | \.rej$
46 | \.conflict\~$
47 |
48 | .idea
49 | /.checkstyle
50 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [![][Build Status img]][Build Status]
2 | [![][license img]][license]
3 | [![][Maven Central img]][Maven Central]
4 | [![][Javadocs img]][Javadocs]
5 |
6 | jnr-unixsocket
7 | ==============
8 |
9 | Native I/O access for java.
10 |
11 | Check out the [examples](https://github.com/jnr/jnr-unixsocket/tree/master/src/test/java/jnr/unixsocket/example) for more information.
12 |
13 | [Build Status]:https://travis-ci.org/jnr/jnr-unixsocket
14 | [Build Status img]:https://travis-ci.org/jnr/jnr-unixsocket.svg?branch=master
15 |
16 | [license]:LICENSE
17 | [license img]:https://img.shields.io/badge/license-Apache%202-blue.svg
18 |
19 | [Maven Central]:https://maven-badges.herokuapp.com/maven-central/com.github.jnr/jnr-unixsocket
20 | [Maven Central img]:https://maven-badges.herokuapp.com/maven-central/com.github.jnr/jnr-unixsocket/badge.svg
21 |
22 | [Javadocs]:http://javadoc.io/doc/com.github.jnr/jnr-unixsocket
23 | [Javadocs img]:http://javadoc.io/badge/com.github.jnr/jnr-unixsocket.svg
24 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | 4.0.0
3 |
4 |
5 | org.sonatype.oss
6 | oss-parent
7 | 7
8 |
9 |
10 | com.github.jnr
11 | jnr-unixsocket
12 | jar
13 | 0.38.24-SNAPSHOT
14 | jnr-unixsocket
15 | UNIX socket channels for java
16 | http://github.com/jnr/jnr-unixsocket
17 |
18 |
19 |
20 | The Apache Software License, Version 2.0
21 | http://www.apache.org/licenses/LICENSE-2.0.txt
22 | repo
23 |
24 |
25 |
26 |
27 | scm:git:git@github.com:jnr/jnr-unixsocket.git
28 | scm:git:git@github.com:jnr/jnr-unixsocket.git
29 | git@github.com:jnr/jnr-unixsocket.git
30 |
31 |
32 |
33 |
34 | wmeissner
35 | Wayne Meissner
36 | wmeissner@gmail.com
37 |
38 |
39 | felfert
40 | Fritz Elfert
41 | fritz-github@fritz-elfert.de
42 | Europe/Berlin
43 |
44 |
45 |
46 |
47 | UTF-8
48 | 8
49 | 8
50 |
51 |
52 |
53 |
54 | junit
55 | junit
56 | 4.13.1
57 | test
58 |
59 |
60 | com.github.jnr
61 | jnr-ffi
62 | 2.2.17
63 | compile
64 |
65 |
66 | com.github.jnr
67 | jnr-constants
68 | 0.10.4
69 | compile
70 |
71 |
72 | com.github.jnr
73 | jnr-enxio
74 | 0.32.18
75 | compile
76 |
77 |
78 | com.github.jnr
79 | jnr-posix
80 | 3.1.20
81 | compile
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | maven-checkstyle-plugin
90 | 2.17
91 |
92 |
93 | process-test-classes
94 |
95 | check
96 |
97 |
98 | true
99 | true
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 | com.github.spotbugs
121 | spotbugs-maven-plugin
122 | 3.1.12.2
123 |
124 |
125 |
126 | com.github.spotbugs
127 | spotbugs
128 | 4.0.0-beta4
129 |
130 |
131 |
132 |
133 | process-test-classes
134 |
135 | check
136 |
137 |
138 |
139 |
140 | Max
141 | false
142 | true
143 | true
144 | false
145 |
146 |
147 |
148 |
149 |
150 | maven-pmd-plugin
151 | 3.13.0
152 |
153 | true
154 | false
155 | false
156 |
157 |
158 | /rulesets/java/basic.xml
159 | /rulesets/java/empty.xml
160 | /rulesets/java/imports.xml
161 | /rulesets/java/unnecessary.xml
162 | /rulesets/java/unusedcode.xml
163 |
164 |
165 | /rulesets/java/braces.xml
166 | /rulesets/java/finalizers.xml
167 |
168 |
169 |
170 |
171 | process-test-classes
172 |
173 | check
174 | cpd-check
175 |
176 |
177 |
178 |
179 |
180 |
181 | org.apache.felix
182 | maven-bundle-plugin
183 | 2.3.7
184 |
185 |
186 | <_nouses>true
187 | *,jnr.ffi.mapper,jnr.ffi.provider.converters,jnr.ffi.provider.jffi,com.kenai.jffi
188 | jnr.unixsocket
189 |
190 |
191 |
192 |
193 | bundle-manifest
194 | process-classes
195 |
196 | manifest
197 |
198 |
199 |
200 |
201 |
202 | org.apache.maven.plugins
203 | maven-jar-plugin
204 | 2.3.1
205 |
206 |
207 | ${project.build.outputDirectory}/META-INF/MANIFEST.MF
208 |
209 | org.jnrproject.unixsocket
210 |
211 |
212 |
213 |
214 |
215 |
216 | jar
217 | test-jar
218 |
219 |
220 |
221 |
222 |
223 | org.apache.maven.plugins
224 | maven-source-plugin
225 | 2.2.1
226 |
227 |
228 | attach-sources
229 |
230 | jar-no-fork
231 |
232 |
233 |
234 |
235 |
236 | org.apache.maven.plugins
237 | maven-assembly-plugin
238 | 2.5.5
239 |
240 |
241 | assemble-all
242 | package
243 |
244 | single
245 |
246 |
247 |
248 | jar-with-dependencies
249 |
250 |
251 |
252 |
253 |
254 |
297 |
298 | org.apache.maven.plugins
299 | maven-compiler-plugin
300 | 3.8.1
301 |
302 |
303 |
304 |
305 | org.apache.maven.wagon
306 | wagon-webdav
307 |
308 |
309 |
310 |
311 |
312 |
313 | java9
314 |
315 | [9,)
316 |
317 |
318 | 8
319 |
320 |
321 |
322 |
323 |
324 |
--------------------------------------------------------------------------------
/src/main/java/jnr/unixsocket/BindHandler.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Fritz Elfert
3 | *
4 | * This file is part of the JNR project.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package jnr.unixsocket;
19 |
20 | import java.io.IOException;
21 | import java.net.SocketAddress;
22 |
23 | import java.nio.channels.UnsupportedAddressTypeException;
24 | import java.nio.channels.AlreadyBoundException;
25 |
26 | import java.util.concurrent.atomic.AtomicBoolean;
27 |
28 | /**
29 | * Helper class, providing common handling of bind() handling.
30 | */
31 | final class BindHandler {
32 | private final AtomicBoolean bound;
33 |
34 | BindHandler(boolean initialState) {
35 | bound = new AtomicBoolean(initialState);
36 | }
37 |
38 | boolean isBound() {
39 | return bound.get();
40 | }
41 |
42 | synchronized UnixSocketAddress bind(int fd, SocketAddress local) throws IOException {
43 | if (null != local && !(local instanceof UnixSocketAddress)) {
44 | throw new UnsupportedAddressTypeException();
45 | }
46 | if (bound.get()) {
47 | throw new AlreadyBoundException();
48 | } else {
49 | UnixSocketAddress ret = Common.bind(fd, (UnixSocketAddress)local);
50 | bound.set(true);
51 | return ret;
52 | }
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/jnr/unixsocket/Common.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Fritz Elfert
3 | *
4 | * This file is part of the JNR project.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 | package jnr.unixsocket;
20 |
21 | import java.io.File;
22 | import java.io.IOException;
23 | import java.net.SocketOption;
24 | import java.nio.file.Files;
25 | import java.util.Map;
26 | import java.util.HashMap;
27 |
28 | import jnr.constants.platform.ProtocolFamily;
29 | import jnr.constants.platform.SocketLevel;
30 |
31 | import jnr.ffi.Platform;
32 | import jnr.ffi.Platform.OS;
33 | import jnr.ffi.byref.IntByReference;
34 |
35 | /**
36 | * Helper class, providing common methods.
37 | */
38 | final class Common {
39 |
40 | private static OS currentOS = Platform.getNativePlatform().getOS();
41 |
42 | private Common() {
43 | }
44 |
45 | static UnixSocketAddress bind(int fd, UnixSocketAddress local) throws IOException {
46 | SockAddrUnix sa;
47 | if (null == local) {
48 | // Support autobind
49 | sa = SockAddrUnix.create();
50 | sa.setFamily(ProtocolFamily.PF_UNIX);
51 | if (currentOS == OS.LINUX) {
52 | // On Linux, we simply set an empty path
53 | sa.setPath("");
54 | } else {
55 | // Emulate something similar (bind to some random unique address),
56 | // but use regular namespace
57 | File f = Files.createTempFile("jnr-unixsocket-tmp", ".sock").toFile();
58 | f.deleteOnExit();
59 | f.delete();
60 | sa.setPath(f.getPath());
61 | }
62 | } else {
63 | sa = local.getStruct();
64 | }
65 | if (Native.bind(fd, sa, sa.length()) < 0) {
66 | throw new IOException(Native.getLastErrorString());
67 | }
68 | return getsockname(fd);
69 | }
70 |
71 | static UnixSocketAddress getsockname(int sockfd) {
72 | UnixSocketAddress local = new UnixSocketAddress();
73 | SockAddrUnix addr = local.getStruct();
74 | IntByReference len = new IntByReference(addr.getMaximumLength());
75 |
76 | if (Native.libc().getsockname(sockfd, addr, len) < 0) {
77 | throw new Error(Native.getLastErrorString());
78 | }
79 | addr.updatePath(len.getValue());
80 | return local;
81 | }
82 |
83 | static UnixSocketAddress getpeername(int sockfd) {
84 | UnixSocketAddress remote = new UnixSocketAddress();
85 | SockAddrUnix addr = remote.getStruct();
86 | IntByReference len = new IntByReference(addr.getMaximumLength());
87 |
88 | if (Native.libc().getpeername(sockfd, addr, len) < 0) {
89 | throw new Error(Native.getLastErrorString());
90 | }
91 | addr.updatePath(len.getValue());
92 | return remote;
93 | }
94 |
95 | static T getSocketOption(int fd, SocketOption> name) throws IOException {
96 | jnr.constants.platform.SocketOption optname = rMap.get(name);
97 | if (null == optname) {
98 | throw new AssertionError("Option not found");
99 | }
100 | Class> type = name.type();
101 | if (type == Credentials.class) {
102 | return (T) Credentials.getCredentials(fd);
103 | }
104 | if (type == Integer.class) {
105 | return (T) Integer.valueOf(Native.getsockopt(fd, SocketLevel.SOL_SOCKET, optname.intValue()));
106 | }
107 | return (T) Boolean.valueOf(Native.getboolsockopt(fd, SocketLevel.SOL_SOCKET, optname.intValue()));
108 | }
109 |
110 | static void setSocketOption(int fd, SocketOption> name,
111 | Object value) throws IOException {
112 | if (null == value) {
113 | throw new IllegalArgumentException("Invalid option value");
114 | }
115 |
116 | jnr.constants.platform.SocketOption optname = wMap.get(name);
117 | if (null == optname) {
118 | throw new AssertionError("Option not found or not writable");
119 | }
120 |
121 | Class> type = name.type();
122 | if (type != Integer.class && type != Boolean.class) {
123 | throw new AssertionError("Unsupported option type");
124 | }
125 |
126 | int optvalue;
127 | if (type == Integer.class) {
128 | optvalue = ((Integer)value).intValue();
129 | } else {
130 | optvalue = ((Boolean)value).booleanValue() ? 1 : 0;
131 | }
132 |
133 | if (name == UnixSocketOptions.SO_RCVBUF || name == UnixSocketOptions.SO_SNDBUF) {
134 | int i = ((Integer)value).intValue();
135 | if (i < 0) {
136 | throw new IllegalArgumentException("Invalid send/receive buffer size");
137 | }
138 | }
139 |
140 | if (name == UnixSocketOptions.SO_RCVTIMEO || name == UnixSocketOptions.SO_SNDTIMEO) {
141 | int i = ((Integer)value).intValue();
142 | if (i < 0) {
143 | throw new IllegalArgumentException("Invalid send/receive timeout");
144 | }
145 | }
146 |
147 | if (0 != Native.setsockopt(fd, SocketLevel.SOL_SOCKET, optname, optvalue)) {
148 | throw new IOException(Native.getLastErrorString());
149 | }
150 | }
151 |
152 | private static final Map,jnr.constants.platform.SocketOption> wMap = new HashMap<>();
153 | private static final Map,jnr.constants.platform.SocketOption> rMap = new HashMap<>();
154 | static {
155 | wMap.put(UnixSocketOptions.SO_RCVBUF, jnr.constants.platform.SocketOption.SO_RCVBUF);
156 | wMap.put(UnixSocketOptions.SO_SNDBUF, jnr.constants.platform.SocketOption.SO_SNDBUF);
157 | wMap.put(UnixSocketOptions.SO_RCVTIMEO, jnr.constants.platform.SocketOption.SO_RCVTIMEO);
158 | wMap.put(UnixSocketOptions.SO_SNDTIMEO, jnr.constants.platform.SocketOption.SO_SNDTIMEO);
159 | wMap.put(UnixSocketOptions.SO_KEEPALIVE, jnr.constants.platform.SocketOption.SO_KEEPALIVE);
160 | wMap.put(UnixSocketOptions.SO_PASSCRED, jnr.constants.platform.SocketOption.SO_PASSCRED);
161 |
162 | rMap.putAll(wMap);
163 | rMap.put(UnixSocketOptions.SO_PEERCRED, jnr.constants.platform.SocketOption.SO_PEERCRED);
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/src/main/java/jnr/unixsocket/Credentials.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 Greg Vanore
3 | *
4 | * This file is part of the JNR project.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package jnr.unixsocket;
20 |
21 | import jnr.constants.platform.SocketLevel;
22 | import jnr.constants.platform.SocketOption;
23 |
24 | /**
25 | * This class represents the peer credentials, retrievable from an AF_UNIX socket.
26 | *
27 | * An instance of this class can be retrieved, using either the socket-level methods
28 | * {@link UnixSocket#getCredentials} and {@link UnixDatagramSocket#getCredentials} or by specifying
29 | * {@link UnixSocketOptions#SO_PEERCRED} as argument to one of the
30 | * channel-level methods {@link UnixSocketChannel#getOption} and {@link UnixDatagramChannel#getOption}.
31 | *
32 | * See also:socket (7)
33 | */
34 | public final class Credentials {
35 | private final Ucred ucred;
36 |
37 | Credentials(Ucred ucred) {
38 | this.ucred = ucred;
39 | }
40 |
41 | /**
42 | * Retrieves the peer's process ID.
43 | * @return The PID.
44 | */
45 | public int getPid() {
46 | return ucred.getPidField().intValue();
47 | }
48 |
49 | /**
50 | * Retrieves the peer's numeric effective user ID.
51 | * @return The EUID.
52 | */
53 | public int getUid() {
54 | return ucred.getUidField().intValue();
55 | }
56 |
57 | /**
58 | * Retrieves the peer's numeric effective group ID.
59 | * @return The EGID.
60 | */
61 | public int getGid() {
62 | return ucred.getGidField().intValue();
63 | }
64 |
65 | /**
66 | * Returns a human readable description of this instance.
67 | */
68 | @Override
69 | public java.lang.String toString() {
70 | return java.lang.String.format("[uid=%d gid=%d pid=%d]", getUid(), getGid(), getPid());
71 | }
72 |
73 | static Credentials getCredentials(int fd) {
74 | Ucred c = new Ucred();
75 | int error = Native.getsockopt(fd, SocketLevel.SOL_SOCKET, SocketOption.SO_PEERCRED, c);
76 | if (error != 0) {
77 | throw new UnsupportedOperationException(Native.getLastErrorString());
78 | }
79 |
80 | return new Credentials(c);
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/main/java/jnr/unixsocket/Native.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2009 Wayne Meissner
3 | *
4 | * This file is part of the JNR project.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package jnr.unixsocket;
20 |
21 | import java.io.IOException;
22 | import java.nio.ByteBuffer;
23 | import java.nio.ByteOrder;
24 |
25 | import jnr.constants.platform.Errno;
26 | import jnr.constants.platform.ProtocolFamily;
27 | import jnr.constants.platform.Sock;
28 | import jnr.constants.platform.SocketLevel;
29 | import jnr.constants.platform.SocketOption;
30 | import jnr.ffi.LastError;
31 | import jnr.ffi.LibraryLoader;
32 | import jnr.ffi.Platform;
33 | import jnr.ffi.Pointer;
34 | import jnr.ffi.Runtime;
35 | import jnr.ffi.Struct;
36 | import jnr.ffi.annotations.In;
37 | import jnr.ffi.annotations.Out;
38 | import jnr.ffi.annotations.Transient;
39 | import jnr.ffi.byref.IntByReference;
40 | import jnr.ffi.types.size_t;
41 | import jnr.ffi.types.ssize_t;
42 | import jnr.posix.DefaultNativeTimeval;
43 | import jnr.posix.Timeval;
44 |
45 | class Native {
46 | static final String[] libnames = Platform.getNativePlatform().getOS() == Platform.OS.SOLARIS
47 | ? new String[] { "socket", "nsl", Platform.getNativePlatform().getStandardCLibraryName() }
48 | : new String[] { Platform.getNativePlatform().getStandardCLibraryName() };
49 | public interface LibC {
50 |
51 | int F_GETFL = jnr.constants.platform.Fcntl.F_GETFL.intValue();
52 | int F_SETFL = jnr.constants.platform.Fcntl.F_SETFL.intValue();
53 | int O_NONBLOCK = jnr.constants.platform.OpenFlags.O_NONBLOCK.intValue();
54 |
55 | int socket(int domain, int type, int protocol);
56 | int listen(int fd, int backlog);
57 | int bind(int fd, @In @Out @Transient SockAddrUnix addr, int len);
58 | int accept(int fd, @Out SockAddrUnix addr, @In @Out IntByReference len);
59 | int connect(int s, @In @Transient SockAddrUnix name, int namelen);
60 | int getsockname(int fd, @Out SockAddrUnix addr, @In @Out IntByReference len);
61 | int getpeername(int fd, @Out SockAddrUnix addr, @In @Out IntByReference len);
62 | int socketpair(int domain, int type, int protocol, @Out int[] sv);
63 | int fcntl(int fd, int cmd, int data);
64 | int getsockopt(int s, int level, int optname, @Out ByteBuffer optval, @In @Out IntByReference optlen);
65 | int getsockopt(int s, int level, int optname, @Out Timeval optval, @In @Out IntByReference optlen);
66 | int setsockopt(int s, int level, int optname, @In ByteBuffer optval, int optlen);
67 | int setsockopt(int s, int level, int optname, @In Timeval optval, int optlen);
68 | String strerror(int error);
69 | @ssize_t int sendto(int s, @In ByteBuffer data, @size_t long size, int flags, @In @Transient SockAddrUnix name, int namelen);
70 | @ssize_t int recvfrom(int s, @Out ByteBuffer data, @size_t long size, int flags, @Out SockAddrUnix addr, @In @Out IntByReference len);
71 | }
72 |
73 | static final LibC INSTANCE;
74 |
75 | static {
76 | LibraryLoader loader = LibraryLoader.create(LibC.class);
77 | for (String libraryName : libnames) {
78 | loader.library(libraryName);
79 | }
80 | INSTANCE = loader.load();
81 | }
82 |
83 | static final LibC libsocket() {
84 | return INSTANCE;
85 | }
86 |
87 | static final LibC libc() {
88 | return INSTANCE;
89 | }
90 |
91 | static int socket(ProtocolFamily domain, Sock type, int protocol) throws IOException {
92 | int fd = libsocket().socket(domain.intValue(), type.intValue(), protocol);
93 | if (fd < 0) {
94 | throw new IOException(getLastErrorString());
95 | }
96 | return fd;
97 | }
98 |
99 | static int socketpair(ProtocolFamily domain, Sock type, int protocol, int[] sv) throws IOException {
100 | if (libsocket().socketpair(domain.intValue(), type.intValue(), protocol, sv) < 0) {
101 | throw new IOException("socketpair(2) failed " + Native.getLastErrorString());
102 | }
103 | return 0;
104 | }
105 |
106 | static int listen(int fd, int backlog) {
107 | return libsocket().listen(fd, backlog);
108 | }
109 |
110 | static int bind(int fd, SockAddrUnix addr, int len) {
111 | return libsocket().bind(fd, addr, len);
112 | }
113 |
114 | static int accept(int fd, SockAddrUnix addr, IntByReference len) {
115 | return libsocket().accept(fd, addr, len);
116 | }
117 |
118 | static int connect(int fd, SockAddrUnix addr, int len) {
119 | return libsocket().connect(fd, addr, len);
120 | }
121 |
122 | static String getLastErrorString() {
123 | return strerror(LastError.getLastError(Runtime.getSystemRuntime()));
124 | }
125 |
126 | static Errno getLastError() {
127 | return Errno.valueOf(LastError.getLastError(Runtime.getSystemRuntime()));
128 | }
129 |
130 | static String strerror(int error) {
131 | return libc().strerror(error);
132 | }
133 |
134 | public static void setBlocking(int fd, boolean block) {
135 | int flags = libc().fcntl(fd, LibC.F_GETFL, 0);
136 | if (block) {
137 | flags &= ~LibC.O_NONBLOCK;
138 | } else {
139 | flags |= LibC.O_NONBLOCK;
140 | }
141 | libc().fcntl(fd, LibC.F_SETFL, flags);
142 | }
143 |
144 | public static int setsockopt(int s, SocketLevel level, SocketOption optname, boolean optval) {
145 | return setsockopt(s, level, optname, optval ? 1 : 0);
146 | }
147 |
148 | public static int setsockopt(int s, SocketLevel level, SocketOption optname, int optval) {
149 | if (optname == SocketOption.SO_RCVTIMEO || optname == SocketOption.SO_SNDTIMEO) {
150 | DefaultNativeTimeval t = new DefaultNativeTimeval(Runtime.getSystemRuntime());
151 | t.setTime(new long [] {optval / 1000, ((long)optval % 1000) * 1000});
152 | return libsocket().setsockopt(s, level.intValue(), optname.intValue(), t, DefaultNativeTimeval.size(t));
153 | } else {
154 | ByteBuffer buf = ByteBuffer.allocate(4);
155 | buf.order(ByteOrder.nativeOrder());
156 | buf.putInt(optval).flip();
157 | return libsocket().setsockopt(s, level.intValue(), optname.intValue(), buf, buf.remaining());
158 | }
159 | }
160 |
161 | public static int getsockopt (int s, SocketLevel level, int optname) {
162 | IntByReference ref;
163 | if (optname == SocketOption.SO_RCVTIMEO.intValue() || optname == SocketOption.SO_SNDTIMEO.intValue()) {
164 | DefaultNativeTimeval t = new DefaultNativeTimeval(Runtime.getSystemRuntime());
165 | ref = new IntByReference(DefaultNativeTimeval.size(t));
166 | Native.libsocket().getsockopt(s, level.intValue(), optname, t, ref);
167 | return (t.tv_sec.intValue() * 1000 + t.tv_usec.intValue() / 1000);
168 | } else {
169 | ByteBuffer buf = ByteBuffer.allocate(4);
170 | buf.order(ByteOrder.nativeOrder());
171 | ref = new IntByReference(4);
172 | Native.libsocket().getsockopt(s, level.intValue(), optname, buf, ref);
173 | return buf.getInt();
174 | }
175 | }
176 |
177 | public static int getsockopt(int s, SocketLevel level, SocketOption optname, Struct data) {
178 | Pointer struct_ptr = Struct.getMemory(data);
179 | IntByReference ref = new IntByReference(Struct.size(data));
180 | ByteBuffer buf = ByteBuffer.wrap((byte[])struct_ptr.array());
181 |
182 | return Native.libsocket().getsockopt(s, level.intValue(), optname.intValue(), buf, ref);
183 | }
184 |
185 | public static boolean getboolsockopt (int s, SocketLevel level, int optname) {
186 | return getsockopt(s, level, optname) != 0;
187 | }
188 |
189 | public static int sendto(int fd, ByteBuffer src, SockAddrUnix addr, int len) throws IOException {
190 | if (src == null) {
191 | throw new IllegalArgumentException("Source buffer cannot be null");
192 | }
193 |
194 | int n;
195 | do {
196 | n = libsocket().sendto(fd, src, src.remaining(), 0, addr, len);
197 | } while (n < 0 && Errno.EINTR.equals(getLastError()));
198 |
199 | if (n > 0) {
200 | src.position(src.position() + n);
201 | }
202 |
203 | return n;
204 | }
205 |
206 | public static int recvfrom(int fd, ByteBuffer dst, SockAddrUnix addr) throws IOException {
207 | if (dst == null) {
208 | throw new IllegalArgumentException("Destination buffer cannot be null");
209 | }
210 | if (dst.isReadOnly()) {
211 | throw new IllegalArgumentException("Read-only buffer");
212 | }
213 |
214 | IntByReference addrlen = (null == addr) ? null : new IntByReference(addr.getMaximumLength());
215 | int n;
216 | do {
217 | n = libsocket().recvfrom(fd, dst, dst.remaining(), 0, addr, addrlen);
218 | } while (n < 0 && Errno.EINTR.equals(getLastError()));
219 |
220 | if (n > 0) {
221 | dst.position(dst.position() + n);
222 | }
223 |
224 | return n;
225 | }
226 | }
227 |
--------------------------------------------------------------------------------
/src/main/java/jnr/unixsocket/SockAddrUnix.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2009 Wayne Meissner
3 | *
4 | * This file is part of the JNR project.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package jnr.unixsocket;
20 |
21 | import static java.nio.charset.StandardCharsets.UTF_8;
22 |
23 | import jnr.constants.platform.ProtocolFamily;
24 | import jnr.ffi.Platform;
25 | import jnr.ffi.Platform.OS;
26 | import jnr.ffi.Runtime;
27 | import jnr.ffi.Struct;
28 |
29 | /**
30 | * Native unix domain socket address structure.
31 | */
32 | abstract class SockAddrUnix extends Struct {
33 |
34 | private static transient OS currentOS = Platform.getNativePlatform().getOS();
35 | public final static int ADDR_LENGTH = 108;
36 | public final static int HEADER_LENGTH = 2;
37 |
38 | protected abstract UTF8String getPathField();
39 | protected abstract NumberField getFamilyField();
40 |
41 | // This is important to be cached here for supporting abstract namespace on Linux
42 | // (which starts with a NUL byte. path is NOT NUL terminated in this case!)
43 | private java.lang.String cachedPath;
44 |
45 | SockAddrUnix() {
46 | super(Runtime.getSystemRuntime());
47 | }
48 |
49 | /**
50 | * Sets the protocol family of this unix socket address.
51 | *
52 | * @param family The protocol family, usually {@link ProtocolFamily#PF_UNIX}
53 | */
54 | final void setFamily(ProtocolFamily family) {
55 | getFamilyField().set(family.intValue());
56 | }
57 |
58 |
59 | /**
60 | * Gets the protocol family of this unix socket address.
61 | *
62 | * @return The protocol family
63 | */
64 | final ProtocolFamily getFamily() {
65 | return ProtocolFamily.valueOf(getFamilyField().intValue());
66 | }
67 |
68 | /**
69 | * Sets the file system path of this socket address
70 | *
71 | * @param path The unix socket address
72 | */
73 | void setPath(java.lang.String path) {
74 | cachedPath = path;
75 | getPathField().set(cachedPath);
76 | }
77 |
78 | /**
79 | * Updates the file system path of this socket address.
80 | * In order to support abstract namespaces, this MUST be
81 | * called after any native syscall that sets this
82 | * path struct like getsockname(), getpeername(), accept().
83 | *
84 | * @param len the value of the addrlen var, set by the above syscalls.
85 | */
86 | void updatePath(final int len) {
87 | if (currentOS == OS.LINUX) {
88 | // Linux always returns an accurate length in
89 | // order to support abstract namespace, where
90 | // path STARTS with a NUL byte.
91 | cachedPath = len == HEADER_LENGTH ? "" : getPath(len - HEADER_LENGTH);
92 | } else {
93 | // All others might return a len > 0 (typically 14) AND the path is terminated
94 | // by a NUL byte if it is shorter than sizeof(sun_path)
95 | cachedPath = getPathField().get();
96 | int slen = len - HEADER_LENGTH;
97 | if (slen <= 0) {
98 | cachedPath = "";
99 | } else {
100 | if (slen < getPathField().length() && slen < cachedPath.length()) {
101 | cachedPath = cachedPath.substring(0, slen);
102 | }
103 | }
104 | }
105 | }
106 |
107 | /**
108 | * Gets the file system path of this socket address
109 | *
110 | * @return A String
111 | */
112 | final java.lang.String getPath() {
113 | if (null == cachedPath) {
114 | cachedPath = getPathField().get();
115 | }
116 | return cachedPath;
117 | }
118 |
119 | /**
120 | * Gets the path of this socket address, supporting abstract namespace on Linux.
121 | *
122 | * @param len The desired length of the string.
123 | * If the first character of the path is NUL, then this value ist considered
124 | * exact, otherwise it includes a trailing NUL charater and therefore the actual
125 | * string length is len - 1.
126 | */
127 | final java.lang.String getPath(int len) {
128 | UTF8String str = getPathField();
129 | byte [] ba = new byte[str.length()];
130 | str.getMemory().get(str.offset(), ba, 0, len);
131 | if (0 != ba[0]) {
132 | len -= 1;
133 | }
134 | return new java.lang.String(java.util.Arrays.copyOf(ba, len), UTF_8);
135 | }
136 |
137 | /**
138 | * Gets the maximum length of this address (including len/family header)
139 | *
140 | * @return The maximum size of the address in bytes
141 | */
142 | int getMaximumLength() {
143 | return HEADER_LENGTH + getPathField().length();
144 | }
145 |
146 | /**
147 | * Gets the actual length of this address (including len/family header)
148 | *
149 | * @return The actual size of this address, in bytes
150 | */
151 | int length() {
152 | if (currentOS == OS.LINUX && null != cachedPath) {
153 | return HEADER_LENGTH + cachedPath.length();
154 | }
155 | return HEADER_LENGTH + strlen(getPathField());
156 | }
157 |
158 | /**
159 | * Gets len/family header length
160 | *
161 | * @return The size of header, in bytes
162 | */
163 | int getHeaderLength() {
164 | return HEADER_LENGTH;
165 | }
166 |
167 |
168 | /**
169 | * Creates a new instance of SockAddrUnix
170 | *
171 | * @return An instance of SockAddrUnix
172 | */
173 | static SockAddrUnix create() {
174 | return Platform.getNativePlatform().isBSD() ? new BSDSockAddrUnix() : new DefaultSockAddrUnix();
175 | }
176 |
177 | private static final int strlen(UTF8String str) {
178 | int end = str.getMemory().indexOf(str.offset(), (byte) 0);
179 | return end >= 0 ? end : str.length();
180 | }
181 |
182 | /**
183 | * An implementation of {@link SockAddrUnix} for BSD systems
184 | */
185 | static final class BSDSockAddrUnix extends SockAddrUnix {
186 |
187 | public final Unsigned8 sun_len = new Unsigned8();
188 | public final Unsigned8 sun_family = new Unsigned8();
189 | public final UTF8String sun_addr = new UTF8String(ADDR_LENGTH);
190 |
191 | @Override
192 | public void setPath(java.lang.String path) {
193 | super.setPath(path);
194 | sun_len.set(path.length());
195 | }
196 | protected UTF8String getPathField() {
197 | return sun_addr;
198 | }
199 | protected NumberField getFamilyField() {
200 | return sun_family;
201 | }
202 | }
203 |
204 |
205 | /**
206 | * An implementation of {@link SockAddrUnix} for Linux, Solaris, et, al
207 | */
208 | static final class DefaultSockAddrUnix extends SockAddrUnix {
209 | public final Unsigned16 sun_family = new Unsigned16();
210 | public final UTF8String sun_addr = new UTF8String(ADDR_LENGTH);
211 |
212 | protected UTF8String getPathField() {
213 | return sun_addr;
214 | }
215 |
216 | protected NumberField getFamilyField() {
217 | return sun_family;
218 | }
219 | }
220 | }
221 |
--------------------------------------------------------------------------------
/src/main/java/jnr/unixsocket/Ucred.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the JNR project.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package jnr.unixsocket;
18 |
19 | import jnr.ffi.Struct;
20 |
21 | /**
22 | * Native structure for SCM_CREDENTIALS. See 'man 7 unix'.
23 | */
24 | final class Ucred extends Struct {
25 | final pid_t pid = new pid_t();
26 | final uid_t uid = new uid_t();
27 | final gid_t gid = new gid_t();
28 |
29 | public Ucred() {
30 | super(jnr.ffi.Runtime.getSystemRuntime());
31 | }
32 |
33 | pid_t getPidField() {
34 | return pid;
35 | }
36 |
37 | uid_t getUidField() {
38 | return uid;
39 | }
40 |
41 | gid_t getGidField() {
42 | return gid;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/jnr/unixsocket/UnixDatagramChannel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Fritz Elfert
3 | *
4 | * This file is part of the JNR project.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package jnr.unixsocket;
20 |
21 | import java.io.IOException;
22 |
23 | import java.nio.ByteBuffer;
24 | import java.nio.channels.ClosedChannelException;
25 | import java.nio.channels.DatagramChannel;
26 | import java.nio.channels.MembershipKey;
27 | import java.nio.channels.UnsupportedAddressTypeException;
28 |
29 | import java.net.InetAddress;
30 | import java.net.NetworkInterface;
31 | import java.net.SocketAddress;
32 | import java.net.SocketException;
33 | import java.net.SocketOption;
34 |
35 | import java.util.concurrent.locks.ReadWriteLock;
36 | import java.util.concurrent.locks.ReentrantReadWriteLock;
37 | import java.util.Collections;
38 | import java.util.HashSet;
39 | import java.util.Set;
40 |
41 | import jnr.constants.platform.ProtocolFamily;
42 | import jnr.constants.platform.Sock;
43 | import jnr.unixsocket.impl.AbstractNativeDatagramChannel;
44 |
45 | public class UnixDatagramChannel extends AbstractNativeDatagramChannel {
46 | static enum State {
47 | UNINITIALIZED,
48 | CONNECTED,
49 | IDLE,
50 | }
51 | private State state;
52 | private UnixSocketAddress remoteAddress = null;
53 | private UnixSocketAddress localAddress = null;
54 | private final ReadWriteLock stateLock = new ReentrantReadWriteLock();
55 | private final BindHandler bindHandler;
56 |
57 | public static final UnixDatagramChannel open() throws IOException {
58 | return new UnixDatagramChannel();
59 | }
60 |
61 | public static final UnixDatagramChannel open(ProtocolFamily domain, int protocol) throws IOException {
62 | return new UnixDatagramChannel(domain, protocol);
63 | }
64 |
65 | public static final UnixDatagramChannel[] pair() throws IOException {
66 | int[] sockets = { -1, -1 };
67 | Native.socketpair(ProtocolFamily.PF_UNIX, Sock.SOCK_DGRAM, 0, sockets);
68 | return new UnixDatagramChannel[] {
69 | new UnixDatagramChannel(sockets[0], State.CONNECTED, true),
70 | new UnixDatagramChannel(sockets[1], State.CONNECTED, true)
71 | };
72 | }
73 |
74 | private UnixDatagramChannel() throws IOException {
75 | this(Native.socket(ProtocolFamily.PF_UNIX, Sock.SOCK_DGRAM, 0));
76 | }
77 |
78 | UnixDatagramChannel(ProtocolFamily domain, int protocol) throws IOException
79 | {
80 | this(Native.socket(domain, Sock.SOCK_DGRAM, protocol));
81 | }
82 |
83 | UnixDatagramChannel(int fd) {
84 | this(fd, State.IDLE, false);
85 | }
86 |
87 | UnixDatagramChannel(int fd, State initialState, boolean initialBoundState) {
88 | super(fd);
89 | stateLock.writeLock().lock();
90 | try {
91 | state = initialState;
92 | bindHandler = new BindHandler(initialBoundState);
93 | } finally {
94 | stateLock.writeLock().unlock();
95 | }
96 | }
97 |
98 | UnixDatagramChannel(int fd, UnixSocketAddress remote) throws IOException {
99 | this(fd);
100 | connect(remote);
101 | }
102 |
103 | @Override
104 | public UnixDatagramChannel bind(SocketAddress local) throws IOException {
105 | localAddress = bindHandler.bind(getFD(), local);
106 | return this;
107 | }
108 |
109 | public UnixDatagramChannel connect(UnixSocketAddress remote) {
110 | stateLock.writeLock().lock();
111 | remoteAddress = remote;
112 | state = State.CONNECTED;
113 | stateLock.writeLock().unlock();
114 | return this;
115 | }
116 |
117 | public UnixDatagramChannel disconnect() throws IOException {
118 | stateLock.writeLock().lock();
119 | remoteAddress = null;
120 | state = State.IDLE;
121 | stateLock.writeLock().unlock();
122 | return this;
123 | }
124 |
125 | boolean isBound() {
126 | return bindHandler.isBound();
127 | }
128 |
129 | public boolean isConnected() {
130 | stateLock.readLock().lock();
131 | boolean isConnected = state == State.CONNECTED;
132 | stateLock.readLock().unlock();
133 | return isConnected;
134 | }
135 |
136 | public final UnixSocketAddress getRemoteSocketAddress() {
137 | if (!isConnected()) {
138 | return null;
139 | }
140 | return remoteAddress != null ? remoteAddress : (remoteAddress = Common.getpeername(getFD()));
141 | }
142 |
143 | public final UnixSocketAddress getLocalSocketAddress() {
144 | return localAddress != null ? localAddress : (localAddress = Common.getsockname(getFD()));
145 | }
146 |
147 | @Override
148 | public UnixSocketAddress receive(ByteBuffer src) throws IOException {
149 | UnixSocketAddress remote = new UnixSocketAddress();
150 | int n = Native.recvfrom(getFD(), src, remote.getStruct());
151 | if (n < 0) {
152 | throw new IOException(Native.getLastErrorString());
153 | }
154 | return remote;
155 | }
156 |
157 | @Override
158 | public int send(ByteBuffer src, SocketAddress target) throws IOException {
159 | UnixSocketAddress remote = null;
160 | if (null == target) {
161 | if (isConnected()) {
162 | remote = remoteAddress;
163 | } else {
164 | throw new IllegalArgumentException("Destination address cannot be null on unconnected datagram sockets");
165 | }
166 | } else {
167 | if (!(target instanceof UnixSocketAddress)) {
168 | throw new UnsupportedAddressTypeException();
169 | }
170 | remote = (UnixSocketAddress)target;
171 | }
172 | SockAddrUnix sa = (null == remote) ? null : remote.getStruct();
173 | int addrlen = (null == sa) ? 0 : sa.length();
174 | int n = Native.sendto(getFD(), src, sa, addrlen);
175 | if (n < 0) {
176 | throw new IOException(Native.getLastErrorString());
177 | }
178 |
179 | return n;
180 | }
181 |
182 | @Override
183 | public DatagramChannel connect(SocketAddress remote) throws IOException {
184 | if (remote instanceof UnixSocketAddress) {
185 | return connect(((UnixSocketAddress) remote));
186 | } else {
187 | throw new UnsupportedAddressTypeException();
188 | }
189 | }
190 |
191 | @Override
192 | public UnixDatagramSocket socket() {
193 | try {
194 | return new UnixDatagramSocket(this);
195 | } catch (SocketException e) {
196 | throw new NullPointerException("Could not create UnixDatagramSocket");
197 | }
198 | }
199 |
200 | @Override
201 | public long write(ByteBuffer[] srcs, int offset, int length)
202 | throws IOException {
203 |
204 | if (state == State.CONNECTED) {
205 | return super.write(srcs, offset, length);
206 | } else if (state == State.IDLE) {
207 | return 0;
208 | } else {
209 | throw new ClosedChannelException();
210 | }
211 | }
212 |
213 | @Override
214 | public int read(ByteBuffer dst) throws IOException {
215 | if (state == State.CONNECTED) {
216 | return super.read(dst);
217 | } else if (state == State.IDLE) {
218 | return 0;
219 | } else {
220 | throw new ClosedChannelException();
221 | }
222 | }
223 |
224 | @Override
225 | public int write(ByteBuffer src) throws IOException {
226 | if (state == State.CONNECTED) {
227 | return super.write(src);
228 | } else if (state == State.IDLE) {
229 | return 0;
230 | } else {
231 | throw new ClosedChannelException();
232 | }
233 | }
234 |
235 |
236 | @Override
237 | public SocketAddress getRemoteAddress() throws IOException {
238 | return remoteAddress;
239 | }
240 |
241 | @Override
242 | public SocketAddress getLocalAddress() throws IOException {
243 | return localAddress;
244 | }
245 |
246 | private static class DefaultOptionsHolder {
247 | static final Set> defaultOptions = defaultOptions();
248 |
249 | private static Set> defaultOptions() {
250 | HashSet> set = new HashSet>(5);
251 | set.add(UnixSocketOptions.SO_SNDBUF);
252 | set.add(UnixSocketOptions.SO_SNDTIMEO);
253 | set.add(UnixSocketOptions.SO_RCVBUF);
254 | set.add(UnixSocketOptions.SO_RCVTIMEO);
255 | set.add(UnixSocketOptions.SO_PEERCRED);
256 | return Collections.unmodifiableSet(set);
257 | }
258 | }
259 |
260 | @Override
261 | public final Set> supportedOptions() {
262 | return DefaultOptionsHolder.defaultOptions;
263 | }
264 |
265 | @Override
266 | public T getOption(SocketOption name) throws IOException {
267 | if (!supportedOptions().contains(name)) {
268 | throw new UnsupportedOperationException("'" + name + "' not supported");
269 | }
270 | return Common.getSocketOption(getFD(), name);
271 | }
272 |
273 | @Override
274 | public DatagramChannel setOption(SocketOption name, T value)
275 | throws IOException {
276 | if (name == null) {
277 | throw new IllegalArgumentException("name may not be null");
278 | }
279 | if (!supportedOptions().contains(name)) {
280 | throw new UnsupportedOperationException("'" + name + "' not supported");
281 | }
282 | Common.setSocketOption(getFD(), name, value);
283 | return this;
284 | }
285 |
286 | @Override
287 | public MembershipKey join(InetAddress group, NetworkInterface interf) {
288 | throw new UnsupportedOperationException("join is not supported");
289 | }
290 |
291 | @Override
292 | public MembershipKey join(InetAddress group, NetworkInterface interf, InetAddress source) {
293 | throw new UnsupportedOperationException("join is not supported");
294 | }
295 |
296 | }
297 |
--------------------------------------------------------------------------------
/src/main/java/jnr/unixsocket/UnixDatagramSocket.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Fritz Elfert
3 | *
4 | * This file is part of the JNR project.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 | package jnr.unixsocket;
20 |
21 | import java.io.IOException;
22 | import java.net.InetAddress;
23 | import java.net.SocketAddress;
24 | import java.net.SocketException;
25 | import java.net.DatagramPacket;
26 | import java.net.DatagramSocket;
27 | import java.nio.channels.DatagramChannel;
28 | import java.nio.channels.UnsupportedAddressTypeException;
29 | import java.util.concurrent.atomic.AtomicBoolean;
30 |
31 | /**
32 | * A SOCK_DGRAM variant of an AF_UNIX socket.
33 | * This specializaton of DatagramSocket delegates
34 | * most of it's funtionality to the corresponding
35 | * UnixDatagramChannel.
36 | */
37 | public class UnixDatagramSocket extends DatagramSocket {
38 |
39 | private final UnixDatagramChannel chan;
40 | private final AtomicBoolean closed = new AtomicBoolean(false);
41 |
42 | /**
43 | * Constructs a new instance.
44 | * @param channel The channel to use.
45 | * @throws SocketException if the socket could not be created.
46 | */
47 | UnixDatagramSocket(final UnixDatagramChannel channel) throws SocketException {
48 | chan = channel;
49 | }
50 |
51 |
52 | /**
53 | * Constructs a new unbound instance.
54 | * @throws SocketException if the socket could not be created.
55 | */
56 | public UnixDatagramSocket() throws SocketException {
57 | chan = null;
58 | }
59 |
60 | /**
61 | * Binds this UnixDatagramSocket to a specific AF_UNIX address.
62 | *
63 | * If the address is {@code null}, then on Linux, an autobind will be performed,
64 | * which will bind this socket in Linux' abstract namespace on a unique path, chosen by
65 | * the system. On all other platforms, A temporary path in the regular filesystem will be chosen.
66 | *
67 | * @param local The {@link UnixSocketAddress} to bind to.
68 | * @throws SocketException if any error happens during the bind, or if the
69 | * socket is already bound.
70 | * @throws UnsupportedAddressTypeException if addr is a SocketAddress subclass
71 | * not supported by this socket.
72 | */
73 | @Override
74 | public void bind(final SocketAddress local) throws SocketException {
75 | if (null != chan) {
76 | if (isClosed()) {
77 | throw new SocketException("Socket is closed");
78 | }
79 | if (isBound()) {
80 | throw new SocketException("already bound");
81 | }
82 | try {
83 | chan.bind(local);
84 | } catch (IOException e) {
85 | throw (SocketException)new SocketException().initCause(e);
86 | }
87 | }
88 | }
89 |
90 | @Override
91 | public synchronized void disconnect() {
92 | if (isClosed()) {
93 | return;
94 | }
95 | if (null != chan) {
96 | try {
97 | chan.disconnect();
98 | } catch (IOException e) {
99 | ignore();
100 | }
101 | }
102 | }
103 |
104 | @Override
105 | public synchronized void close() {
106 | if (null != chan && closed.compareAndSet(false, true)) {
107 | try {
108 | chan.close();
109 | } catch (IOException e) {
110 | ignore();
111 | }
112 | }
113 | }
114 |
115 | @Override
116 | public void connect(SocketAddress addr) throws SocketException {
117 | try {
118 | chan.connect(addr);
119 | } catch (IOException e) {
120 | throw (SocketException)new SocketException().initCause(e);
121 | }
122 | }
123 |
124 | @Override
125 | public void connect(InetAddress addr, int port) {
126 | throw new UnsupportedOperationException("connect(InetAddress, int) is not supported");
127 | }
128 |
129 | @Override
130 | public DatagramChannel getChannel() {
131 | return chan;
132 | }
133 |
134 | /**
135 | * Returns the address to which this socket is connected (NOT implemented).
136 | * Since AF_UNIX sockets can not have an InetAddress, this returns always {@code null}.
137 | * Use {@link #getRemoteSocketAddress} instead, which always returns a {@link UnixSocketAddress}.
138 | * @return {@code null} always.
139 | */
140 | @Override
141 | public InetAddress getInetAddress() {
142 | return null;
143 | }
144 |
145 | /**
146 | * Returns the address of the endpoint this socket is bound to.
147 | *
148 | * @return a {@code SocketAddress} representing the local endpoint of this
149 | * socket, or {@code null} if it is closed or not bound.
150 | * A non-null return value is always of type {@link UnixSocketAddress}
151 | * @see #bind(SocketAddress)
152 | */
153 | @Override
154 | public SocketAddress getLocalSocketAddress() {
155 | if (isClosed()) {
156 | return null;
157 | }
158 | if (null == chan) {
159 | return null;
160 | }
161 | return chan.getLocalSocketAddress();
162 | }
163 |
164 | /**
165 | * Returns the address of the endpoint this socket is connected to, or
166 | * {@code null} if it is unconnected.
167 | *
168 | * @return a {@code SocketAddress} representing the remote
169 | * endpoint of this socket, or {@code null} if it is
170 | * not connected.
171 | * A non-null return value is always of type {@link UnixSocketAddress}
172 | */
173 | @Override
174 | public SocketAddress getRemoteSocketAddress() {
175 | if (!isConnected()) {
176 | return null;
177 | }
178 | return chan.getRemoteSocketAddress();
179 | }
180 |
181 | @Override
182 | public boolean isBound() {
183 | if (null == chan) {
184 | return false;
185 | }
186 | return chan.isBound();
187 | }
188 |
189 | @Override
190 | public boolean isClosed() {
191 | if (null == chan) {
192 | return false;
193 | }
194 | return closed.get();
195 | }
196 |
197 | @Override
198 | public boolean isConnected() {
199 | if (null == chan) {
200 | return false;
201 | }
202 | return chan.isConnected();
203 | }
204 |
205 | /**
206 | * Retrieves the credentials for this UNIX socket. Clients calling this
207 | * method will receive the server's credentials, and servers will receive
208 | * the client's credentials. User ID, group ID, and PID are supplied.
209 | *
210 | * See man unix 7; SCM_CREDENTIALS
211 | *
212 | * @throws UnsupportedOperationException if the underlying socket library
213 | * doesn't support the SO_PEERCRED option
214 | * @throws SocketException if fetching the socket option failed.
215 | *
216 | * @return the credentials of the remote; null if not connected
217 | */
218 | public final Credentials getCredentials() throws SocketException {
219 | if (!chan.isConnected()) {
220 | return null;
221 | }
222 | try {
223 | return chan.getOption(UnixSocketOptions.SO_PEERCRED);
224 | } catch (IOException e) {
225 | throw (SocketException)new SocketException().initCause(e);
226 | }
227 | }
228 |
229 | @Override
230 | public int getReceiveBufferSize() throws SocketException {
231 | try {
232 | return chan.getOption(UnixSocketOptions.SO_RCVBUF).intValue();
233 | } catch (IOException e) {
234 | throw (SocketException)new SocketException().initCause(e);
235 | }
236 | }
237 |
238 | @Override
239 | public int getSendBufferSize() throws SocketException {
240 | try {
241 | return chan.getOption(UnixSocketOptions.SO_SNDBUF).intValue();
242 | } catch (IOException e) {
243 | throw (SocketException)new SocketException().initCause(e);
244 | }
245 | }
246 |
247 | @Override
248 | public int getSoTimeout() throws SocketException {
249 | try {
250 | return chan.getOption(UnixSocketOptions.SO_RCVTIMEO).intValue();
251 | } catch (IOException e) {
252 | throw (SocketException)new SocketException().initCause(e);
253 | }
254 | }
255 |
256 | @Override
257 | public void setReceiveBufferSize(int size) throws SocketException {
258 | try {
259 | chan.setOption(UnixSocketOptions.SO_RCVBUF, Integer.valueOf(size));
260 | } catch (IOException e) {
261 | throw (SocketException)new SocketException().initCause(e);
262 | }
263 | }
264 |
265 | @Override
266 | public void setSendBufferSize(int size) throws SocketException {
267 | try {
268 | chan.setOption(UnixSocketOptions.SO_SNDBUF, Integer.valueOf(size));
269 | } catch (IOException e) {
270 | throw (SocketException)new SocketException().initCause(e);
271 | }
272 | }
273 |
274 | @Override
275 | public void setSoTimeout(int timeout) throws SocketException {
276 | try {
277 | chan.setOption(UnixSocketOptions.SO_RCVTIMEO, Integer.valueOf(timeout));
278 | } catch (IOException e) {
279 | throw (SocketException)new SocketException().initCause(e);
280 | }
281 | }
282 |
283 | /**
284 | * Sends a datagram packet from this socket (NOT implemented).
285 | * Unfortunately, {@link java.net.DatagramPacket} is final and can not deal
286 | * with AF_UNIX addresses. Therefore, this functionality was omitted.
287 | * @see java.net.DatagramPacket
288 | * @see java.net.DatagramSocket#send
289 | * @throws UnsupportedOperationException always.
290 | */
291 | @Override
292 | public void send(DatagramPacket p) throws IOException {
293 | throw new UnsupportedOperationException("sending DatagramPackets is not supported");
294 | }
295 |
296 | /**
297 | * Receives a datagram packet from this socket (NOT implemented).
298 | * Unfortunately, {@link java.net.DatagramPacket} is final and can not deal
299 | * with AF_UNIX addresses. Therefore, this functionality was omitted.
300 | * @see java.net.DatagramPacket
301 | * @see java.net.DatagramSocket#receive
302 | * @throws UnsupportedOperationException always.
303 | */
304 | @Override
305 | public synchronized void receive(DatagramPacket p) throws IOException {
306 | throw new UnsupportedOperationException("receiving DatagramPackets is not supported");
307 | }
308 |
309 | private void ignore() {
310 | }
311 | }
312 |
--------------------------------------------------------------------------------
/src/main/java/jnr/unixsocket/UnixServerSocket.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2009 Wayne Meissner
3 | *
4 | * This file is part of the JNR project.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package jnr.unixsocket;
20 |
21 | import java.io.IOException;
22 | import java.net.SocketAddress;
23 |
24 | import java.nio.channels.UnsupportedAddressTypeException;
25 |
26 | public class UnixServerSocket {
27 | final UnixServerSocketChannel channel;
28 | final int fd;
29 | volatile UnixSocketAddress localAddress;
30 |
31 | public UnixServerSocket() throws IOException {
32 | this.channel = new UnixServerSocketChannel(this);
33 | this.fd = channel.getFD();
34 | }
35 |
36 | UnixServerSocket(UnixServerSocketChannel channel) {
37 | this.channel = channel;
38 | this.fd = channel.getFD();
39 | }
40 |
41 | public UnixSocket accept() throws IOException {
42 | return new UnixSocket(channel.accept());
43 | }
44 |
45 | public void bind(SocketAddress endpoint) throws IOException {
46 | bind(endpoint, 128);
47 | }
48 |
49 | public void bind(SocketAddress endpoint, int backlog) throws IOException {
50 | if (null != endpoint && !(endpoint instanceof UnixSocketAddress)) {
51 | throw new UnsupportedAddressTypeException();
52 | }
53 | localAddress = Common.bind(fd, (UnixSocketAddress)endpoint);
54 | if (Native.listen(fd, backlog) < 0) {
55 | throw new IOException(Native.getLastErrorString());
56 | }
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/jnr/unixsocket/UnixServerSocketChannel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2009 Wayne Meissner
3 | *
4 | * This file is part of the JNR project.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package jnr.unixsocket;
20 |
21 | import jnr.constants.platform.ProtocolFamily;
22 | import jnr.constants.platform.Sock;
23 | import jnr.unixsocket.impl.AbstractNativeServerSocketChannel;
24 | import jnr.ffi.byref.IntByReference;
25 |
26 | import java.io.IOException;
27 | import java.nio.channels.ClosedChannelException;
28 | import java.nio.channels.NotYetBoundException;
29 | import java.nio.channels.SelectionKey;
30 | import java.nio.channels.spi.SelectorProvider;
31 |
32 | import static jnr.unixsocket.Native.getLastError;
33 | import static jnr.unixsocket.Native.getLastErrorString;
34 |
35 | /**
36 | *
37 | */
38 | public class UnixServerSocketChannel extends AbstractNativeServerSocketChannel {
39 |
40 | private final UnixServerSocket socket;
41 |
42 | UnixServerSocketChannel(UnixServerSocket socket) throws IOException {
43 | super(Native.socket(ProtocolFamily.PF_UNIX, Sock.SOCK_STREAM, 0));
44 | this.socket = new UnixServerSocket(this);
45 | }
46 |
47 | UnixServerSocketChannel(SelectorProvider provider, int fd) {
48 | super(provider, fd, SelectionKey.OP_ACCEPT | SelectionKey.OP_READ);
49 | this.socket = new UnixServerSocket(this);
50 | }
51 |
52 | public static UnixServerSocketChannel open() throws IOException {
53 | return new UnixServerSocket().channel;
54 | }
55 |
56 | public UnixSocketChannel accept() throws IOException {
57 | UnixSocketAddress remote = new UnixSocketAddress();
58 | SockAddrUnix addr = remote.getStruct();
59 | int maxLength = addr.getMaximumLength();
60 | IntByReference len = new IntByReference(maxLength);
61 |
62 | int clientfd = -1;
63 | begin();
64 | try {
65 | clientfd = Native.accept(getFD(), addr, len);
66 | } finally {
67 | end(clientfd >= 0);
68 | }
69 |
70 | if (clientfd < 0) {
71 | if (isBlocking()) {
72 | switch (getLastError()) {
73 | case EBADF:
74 | throw new ClosedChannelException();
75 | case EINVAL:
76 | throw new NotYetBoundException();
77 | default:
78 | throw new IOException("accept failed: " + getLastErrorString());
79 | }
80 | }
81 |
82 | return null;
83 | }
84 |
85 | // Handle unnamed sockets and sockets in Linux' abstract namespace
86 | addr.updatePath(len.getValue());
87 |
88 | // Always force the socket back to blocking mode
89 | Native.setBlocking(clientfd, true);
90 |
91 | return new UnixSocketChannel(clientfd);
92 | }
93 |
94 | public final UnixServerSocket socket() {
95 | return socket;
96 | }
97 |
98 | public final UnixSocketAddress getRemoteSocketAddress() {
99 | return null;
100 | }
101 |
102 | public final UnixSocketAddress getLocalSocketAddress() {
103 | return socket.localAddress;
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/main/java/jnr/unixsocket/UnixSocket.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2009 Wayne Meissner
3 | * Copyright (C) 2016 Marcus Linke
4 | *
5 | * (ported from https://github.com/softprops/unisockets/blob/master/unisockets-core/src/main/scala/Socket.scala)
6 | *
7 | * This file is part of the JNR project.
8 | *
9 | * Licensed under the Apache License, Version 2.0 (the "License");
10 | * you may not use this file except in compliance with the License.
11 | * You may obtain a copy of the License at
12 | *
13 | * http://www.apache.org/licenses/LICENSE-2.0
14 | *
15 | * Unless required by applicable law or agreed to in writing, software
16 | * distributed under the License is distributed on an "AS IS" BASIS,
17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | * See the License for the specific language governing permissions and
19 | * limitations under the License.
20 | *
21 | */
22 | package jnr.unixsocket;
23 |
24 | import java.io.IOException;
25 | import java.io.InputStream;
26 | import java.io.OutputStream;
27 | import java.net.InetAddress;
28 | import java.net.SocketAddress;
29 | import java.net.SocketException;
30 | import java.nio.ByteBuffer;
31 | import java.nio.channels.Channels;
32 | import java.nio.channels.ReadableByteChannel;
33 | import java.nio.channels.SelectableChannel;
34 | import java.nio.channels.SocketChannel;
35 | import java.nio.channels.WritableByteChannel;
36 | import java.util.concurrent.atomic.AtomicBoolean;
37 |
38 | public class UnixSocket extends java.net.Socket {
39 |
40 | private UnixSocketChannel chan;
41 |
42 | private AtomicBoolean closed = new AtomicBoolean(false);
43 | private AtomicBoolean indown = new AtomicBoolean(false);
44 | private AtomicBoolean outdown = new AtomicBoolean(false);
45 |
46 | private InputStream in;
47 | private OutputStream out;
48 |
49 | public UnixSocket(UnixSocketChannel chan) {
50 | this.chan = chan;
51 | in = Channels.newInputStream(new UnselectableByteChannel(chan));
52 | out = Channels.newOutputStream(new UnselectableByteChannel(chan));
53 | }
54 |
55 | @Override
56 | public void bind(SocketAddress local) throws IOException {
57 | if (null != chan) {
58 | if (isClosed()) {
59 | throw new SocketException("Socket is closed");
60 | }
61 | if (isBound()) {
62 | throw new SocketException("already bound");
63 | }
64 | try {
65 | chan.bind(local);
66 | } catch (IOException e) {
67 | throw (SocketException)new SocketException().initCause(e);
68 | }
69 | }
70 | }
71 |
72 | @Override
73 | public void close() throws IOException {
74 | if (null != chan && closed.compareAndSet(false, true)) {
75 | try {
76 | chan.close();
77 | } catch (IOException e) {
78 | ignore();
79 | }
80 | }
81 | }
82 |
83 | @Override
84 | public void connect(SocketAddress addr) throws IOException {
85 | connect(addr, 0);
86 | }
87 |
88 | @Override
89 | public void connect(SocketAddress addr, int timeout) throws IOException {
90 | if (addr instanceof UnixSocketAddress) {
91 | chan.connect((UnixSocketAddress) addr);
92 | } else {
93 | throw new IllegalArgumentException("address of type "
94 | + addr.getClass() + " are not supported. Use "
95 | + UnixSocketAddress.class + " instead");
96 | }
97 | }
98 |
99 | @Override
100 | public SocketChannel getChannel() {
101 | return chan;
102 | }
103 |
104 | @Override
105 | public InetAddress getInetAddress() {
106 | return null;
107 | }
108 |
109 | @Override
110 | public InputStream getInputStream() throws IOException {
111 | if (chan.isConnected()) {
112 | return in;
113 | } else {
114 | throw new IOException("not connected");
115 | }
116 | }
117 |
118 | @Override
119 | public SocketAddress getLocalSocketAddress() {
120 | return chan.getLocalSocketAddress();
121 | }
122 |
123 | @Override
124 | public OutputStream getOutputStream() throws IOException {
125 | if (chan.isConnected()) {
126 | return out;
127 | } else {
128 | throw new IOException("not connected");
129 | }
130 | }
131 |
132 | @Override
133 | public SocketAddress getRemoteSocketAddress() {
134 | SocketAddress address = chan.getRemoteSocketAddress();
135 |
136 | if (address != null) {
137 | return address;
138 | } else {
139 | return null;
140 | }
141 | }
142 |
143 | @Override
144 | public boolean isBound() {
145 | if (null == chan) {
146 | return false;
147 | }
148 | return chan.isBound();
149 | }
150 |
151 | @Override
152 | public boolean isClosed() {
153 | return closed.get();
154 | }
155 |
156 | @Override
157 | public boolean isConnected() {
158 | return chan.isConnected();
159 | }
160 |
161 | @Override
162 | public boolean isInputShutdown() {
163 | return indown.get();
164 | }
165 |
166 | @Override
167 | public boolean isOutputShutdown() {
168 | return outdown.get();
169 | }
170 |
171 | @Override
172 | public void shutdownInput() throws IOException {
173 | if (indown.compareAndSet(false, true)) {
174 | chan.shutdownInput();
175 | }
176 | }
177 |
178 | @Override
179 | public void shutdownOutput() throws IOException {
180 | if (outdown.compareAndSet(false, true)) {
181 | chan.shutdownOutput();
182 | }
183 | }
184 |
185 | /**
186 | * Retrieves the credentials for this UNIX socket. Clients calling this
187 | * method will receive the server's credentials, and servers will receive
188 | * the client's credentials. User ID, group ID, and PID are supplied.
189 | *
190 | * See man unix 7; SCM_CREDENTIALS
191 | *
192 | * @throws UnsupportedOperationException if the underlying socket library
193 | * doesn't support the SO_PEERCRED option
194 | * @throws SocketException if fetching the socket option failed.
195 | *
196 | * @return the credentials of the remote; null if not connected
197 | */
198 | public final Credentials getCredentials() throws SocketException {
199 | if (!chan.isConnected()) {
200 | return null;
201 | }
202 | try {
203 | return chan.getOption(UnixSocketOptions.SO_PEERCRED);
204 | } catch (IOException e) {
205 | throw (SocketException)new SocketException().initCause(e);
206 | }
207 | }
208 |
209 | @Override
210 | public boolean getKeepAlive() throws SocketException {
211 | try {
212 | return chan.getOption(UnixSocketOptions.SO_KEEPALIVE).booleanValue();
213 | } catch (IOException e) {
214 | throw (SocketException)new SocketException().initCause(e);
215 | }
216 | }
217 |
218 | @Override
219 | public int getReceiveBufferSize() throws SocketException {
220 | try {
221 | return chan.getOption(UnixSocketOptions.SO_RCVBUF).intValue();
222 | } catch (IOException e) {
223 | throw (SocketException)new SocketException().initCause(e);
224 | }
225 | }
226 |
227 | @Override
228 | public int getSendBufferSize() throws SocketException {
229 | try {
230 | return chan.getOption(UnixSocketOptions.SO_SNDBUF).intValue();
231 | } catch (IOException e) {
232 | throw (SocketException)new SocketException().initCause(e);
233 | }
234 | }
235 |
236 | @Override
237 | public int getSoTimeout() throws SocketException {
238 | try {
239 | return chan.getOption(UnixSocketOptions.SO_RCVTIMEO).intValue();
240 | } catch (IOException e) {
241 | throw (SocketException)new SocketException().initCause(e);
242 | }
243 | }
244 |
245 | @Override
246 | public void setKeepAlive(boolean on) throws SocketException {
247 | try {
248 | chan.setOption(UnixSocketOptions.SO_KEEPALIVE, Boolean.valueOf(on));
249 | } catch (IOException e) {
250 | throw (SocketException)new SocketException().initCause(e);
251 | }
252 | }
253 |
254 | @Override
255 | public void setReceiveBufferSize(int size) throws SocketException {
256 | try {
257 | chan.setOption(UnixSocketOptions.SO_RCVBUF, Integer.valueOf(size));
258 | } catch (IOException e) {
259 | throw (SocketException)new SocketException().initCause(e);
260 | }
261 | }
262 |
263 | @Override
264 | public void setSendBufferSize(int size) throws SocketException {
265 | try {
266 | chan.setOption(UnixSocketOptions.SO_SNDBUF, Integer.valueOf(size));
267 | } catch (IOException e) {
268 | throw (SocketException)new SocketException().initCause(e);
269 | }
270 | }
271 |
272 | @Override
273 | public void setSoTimeout(int timeout) throws SocketException {
274 | try {
275 | chan.setOption(UnixSocketOptions.SO_RCVTIMEO, Integer.valueOf(timeout));
276 | } catch (IOException e) {
277 | throw (SocketException)new SocketException().initCause(e);
278 | }
279 | }
280 |
281 | private void ignore() {
282 | }
283 |
284 | /**
285 | * A byte channel that doesn't implement {@link SelectableChannel}. Though
286 | * that type isn't in the public API, if the channel passed in implements
287 | * that interface then unwanted synchronization is performed which can harm
288 | * concurrency and can cause deadlocks.
289 | *
290 | * https://bugs.openjdk.java.net/browse/JDK-4774871
291 | */
292 | static final class UnselectableByteChannel implements ReadableByteChannel, WritableByteChannel {
293 | private final UnixSocketChannel channel;
294 |
295 | UnselectableByteChannel(UnixSocketChannel channel) {
296 | this.channel = channel;
297 | }
298 |
299 | @Override
300 | public int write(ByteBuffer src) throws IOException {
301 | return channel.write(src);
302 | }
303 |
304 | @Override
305 | public int read(ByteBuffer dst) throws IOException {
306 | return channel.read(dst);
307 | }
308 |
309 | @Override
310 | public boolean isOpen() {
311 | return channel.isOpen();
312 | }
313 |
314 | @Override
315 | public void close() throws IOException {
316 | channel.close();
317 | }
318 | }
319 | }
320 |
--------------------------------------------------------------------------------
/src/main/java/jnr/unixsocket/UnixSocketAddress.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2009 Wayne Meissner
3 | *
4 | * This file is part of the JNR project.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package jnr.unixsocket;
20 |
21 | import java.io.IOException;
22 | import java.io.ObjectInputStream;
23 | import java.io.ObjectOutputStream;
24 |
25 | import jnr.constants.platform.ProtocolFamily;
26 |
27 | /**
28 | * This class represents an AF_UNIX-style socket address.
29 | * On Linux, it supports the platform-specific abstract name space.
30 | *
31 | * Using an abstract name space is denoted by the socket path starting with
32 | * a NUL byte. Sockets in abstract name space have no entry in the file system.
33 | * When linux performs autobind, it constructs the resulting path with a
34 | * leading NUL, followed by a unique 5-digit hexadecimal number.
35 | */
36 | public class UnixSocketAddress extends java.net.SocketAddress {
37 |
38 | private static final long serialVersionUID = 4821337010221569096L;
39 | private transient SockAddrUnix address;
40 |
41 | UnixSocketAddress() {
42 | address = SockAddrUnix.create();
43 | address.setFamily(ProtocolFamily.PF_UNIX);
44 | }
45 |
46 | public UnixSocketAddress(java.io.File path) {
47 | address = SockAddrUnix.create();
48 | address.setFamily(ProtocolFamily.PF_UNIX);
49 | address.setPath(path.getPath());
50 | }
51 |
52 | public UnixSocketAddress(final String path) {
53 | address = SockAddrUnix.create();
54 | address.setFamily(ProtocolFamily.PF_UNIX);
55 | address.setPath(path);
56 | }
57 |
58 | SockAddrUnix getStruct() {
59 | return address;
60 | }
61 |
62 | int length() {
63 | return address.length();
64 | }
65 |
66 | /**
67 | * Retrieves the path.
68 | * @return The path of this AF_UNIX address.
69 | * Note: On Linux, can contain a leading NUL byte, if this address
70 | * resides in abstract namespace.
71 | */
72 | public String path() {
73 | return address.getPath();
74 | }
75 |
76 | /**
77 | * Returns a human readable path.
78 | * On Linux, AF_UNIX sockets can be bound/connected in abstract namespace.
79 | * This is denoted by a leading NUL byte in the path.
80 | * In order to be properly displayed, this method returns a path prefixed
81 | * by '@' like netstat, lsof an similar tools.
82 | * @return The human readable path of this address.
83 | */
84 | public String humanReadablePath() {
85 | String ret = path();
86 | // Handle abstract namespace like netstat: replace NUL by '@'
87 | if (ret.indexOf('\000') == 0) {
88 | return ret.replace('\000', '@');
89 | }
90 | return ret;
91 | }
92 |
93 | /**
94 | * Retrieves a human readable description of this address.
95 | * @return The human readable description of this address.
96 | */
97 | @Override
98 | public String toString() {
99 | return "[family=" + address.getFamily() + " path=" + humanReadablePath() + "]";
100 | }
101 |
102 | @Override
103 | public boolean equals(Object _other) {
104 | if (!(_other instanceof UnixSocketAddress)) {
105 | return false;
106 | }
107 |
108 | UnixSocketAddress other = (UnixSocketAddress)_other;
109 |
110 | return address.getFamily() == other.address.getFamily() &&
111 | path().equals(other.path());
112 | }
113 |
114 | @Override
115 | public int hashCode() {
116 | return address.hashCode();
117 | }
118 |
119 | // Serializable
120 | private void writeObject(ObjectOutputStream o) throws IOException {
121 | o.defaultWriteObject();
122 | o.writeObject(path());
123 | }
124 |
125 | private void readObject(ObjectInputStream o)
126 | throws IOException, ClassNotFoundException {
127 | o.defaultReadObject();
128 | String path = (String)o.readObject();
129 | if (null == address) {
130 | address = SockAddrUnix.create();
131 | }
132 | address.setPath(path);
133 | address.setFamily(ProtocolFamily.PF_UNIX);
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/src/main/java/jnr/unixsocket/UnixSocketChannel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2009 Wayne Meissner
3 | * Copyright (C) 2016 Marcus Linke
4 | *
5 | * This file is part of the JNR project.
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | */
19 |
20 | package jnr.unixsocket;
21 |
22 | import java.io.IOException;
23 | import java.net.SocketAddress;
24 | import java.net.SocketOption;
25 | import java.nio.ByteBuffer;
26 | import java.nio.channels.ClosedChannelException;
27 | import java.nio.channels.SocketChannel;
28 | import java.nio.channels.UnsupportedAddressTypeException;
29 | import java.util.Collections;
30 | import java.util.HashSet;
31 | import java.util.Set;
32 | import java.util.concurrent.locks.ReadWriteLock;
33 | import java.util.concurrent.locks.ReentrantReadWriteLock;
34 |
35 | import jnr.constants.platform.Errno;
36 | import jnr.constants.platform.ProtocolFamily;
37 | import jnr.constants.platform.Sock;
38 | import jnr.unixsocket.impl.AbstractNativeSocketChannel;
39 | import jnr.ffi.LastError;
40 |
41 | /**
42 | * A {@link java.nio.channels.Channel} implementation that uses a native unix
43 | * socket
44 | */
45 | public class UnixSocketChannel extends AbstractNativeSocketChannel {
46 | enum State {
47 | UNINITIALIZED,
48 | CONNECTED,
49 | IDLE,
50 | CONNECTING,
51 | }
52 |
53 | private State state;
54 | private UnixSocketAddress remoteAddress = null;
55 | private UnixSocketAddress localAddress = null;
56 | private final ReadWriteLock stateLock = new ReentrantReadWriteLock();
57 | private final BindHandler bindHandler;
58 |
59 | public static final UnixSocketChannel open() throws IOException {
60 | return new UnixSocketChannel();
61 | }
62 |
63 | public static final UnixSocketChannel open(UnixSocketAddress remote)
64 | throws IOException {
65 | UnixSocketChannel channel = new UnixSocketChannel();
66 |
67 | try {
68 | channel.connect(remote);
69 | } catch (IOException e) {
70 | channel.close();
71 | throw e;
72 | }
73 | return channel;
74 | }
75 |
76 | public static final UnixSocketChannel create() throws IOException {
77 | return new UnixSocketChannel();
78 | }
79 |
80 | public static final UnixSocketChannel[] pair() throws IOException {
81 | int[] sockets = { -1, -1 };
82 | Native.socketpair(ProtocolFamily.PF_UNIX, Sock.SOCK_STREAM, 0, sockets);
83 | return new UnixSocketChannel[] {
84 | new UnixSocketChannel(sockets[0], State.CONNECTED, true),
85 | new UnixSocketChannel(sockets[1], State.CONNECTED, true) };
86 | }
87 |
88 | /**
89 | * Create a UnixSocketChannel to wrap an existing file descriptor
90 | * (presumably itself a UNIX socket).
91 | *
92 | * @param fd
93 | * the file descriptor to wrap
94 | * @return the new UnixSocketChannel instance
95 | */
96 | public static final UnixSocketChannel fromFD(int fd) {
97 | return new UnixSocketChannel(fd);
98 | }
99 |
100 | UnixSocketChannel() throws IOException {
101 | this(Native.socket(ProtocolFamily.PF_UNIX, Sock.SOCK_STREAM, 0));
102 | }
103 |
104 | UnixSocketChannel(int fd) {
105 | this(fd, State.CONNECTED, false);
106 | }
107 |
108 | UnixSocketChannel(int fd, State initialState, boolean initialBoundState) {
109 | super(fd);
110 | stateLock.writeLock().lock();
111 | try {
112 | state = initialState;
113 | bindHandler = new BindHandler(initialBoundState);
114 | } finally {
115 | stateLock.writeLock().unlock();
116 | }
117 | }
118 |
119 | private boolean doConnect(SockAddrUnix remote) throws IOException {
120 | if (Native.connect(getFD(), remote, remote.length()) != 0) {
121 | Errno error = Errno.valueOf(LastError.getLastError(jnr.ffi.Runtime
122 | .getSystemRuntime()));
123 |
124 | switch (error) {
125 | case EAGAIN:
126 | case EWOULDBLOCK:
127 | return false;
128 |
129 | default:
130 | throw new IOException(error.toString());
131 | }
132 | }
133 |
134 | return true;
135 | }
136 |
137 | public boolean connect(UnixSocketAddress remote) throws IOException {
138 | remoteAddress = remote;
139 | if (!doConnect(remoteAddress.getStruct())) {
140 | stateLock.writeLock().lock();
141 | state = State.CONNECTING;
142 | stateLock.writeLock().unlock();
143 | return false;
144 |
145 | } else {
146 | stateLock.writeLock().lock();
147 | state = State.CONNECTED;
148 | stateLock.writeLock().unlock();
149 | return true;
150 | }
151 | }
152 |
153 | boolean isBound() {
154 | return bindHandler.isBound();
155 | }
156 |
157 | public boolean isConnected() {
158 | stateLock.readLock().lock();
159 | boolean result = state == State.CONNECTED;
160 | stateLock.readLock().unlock();
161 | return result;
162 | }
163 |
164 | private boolean isIdle() {
165 | stateLock.readLock().lock();
166 | boolean result = state == State.IDLE;
167 | stateLock.readLock().unlock();
168 | return result;
169 | }
170 |
171 | public boolean isConnectionPending() {
172 | stateLock.readLock().lock();
173 | boolean isConnectionPending = state == State.CONNECTING;
174 | stateLock.readLock().unlock();
175 | return isConnectionPending;
176 | }
177 |
178 | public boolean finishConnect() throws IOException {
179 | stateLock.writeLock().lock();
180 | try {
181 | switch (state) {
182 | case CONNECTED:
183 | return true;
184 |
185 | case CONNECTING:
186 | if (!doConnect(remoteAddress.getStruct())) {
187 | return false;
188 | }
189 | state = State.CONNECTED;
190 | return true;
191 |
192 | default:
193 | throw new IllegalStateException(
194 | "socket is not waiting for connect to complete");
195 | }
196 | } finally {
197 | stateLock.writeLock().unlock();
198 | }
199 | }
200 |
201 | public final UnixSocketAddress getRemoteSocketAddress() {
202 | if (!isConnected()) {
203 | return null;
204 | }
205 |
206 | if (remoteAddress != null) {
207 | return remoteAddress;
208 | } else {
209 | remoteAddress = Common.getpeername(getFD());
210 | return remoteAddress;
211 | }
212 | }
213 |
214 | public final UnixSocketAddress getLocalSocketAddress() {
215 | if (localAddress != null) {
216 | return localAddress;
217 | } else {
218 | localAddress = Common.getsockname(getFD());
219 | return localAddress;
220 | }
221 | }
222 |
223 | @Override
224 | public boolean connect(SocketAddress remote) throws IOException {
225 | if (remote instanceof UnixSocketAddress) {
226 | return connect(((UnixSocketAddress) remote));
227 | } else {
228 | throw new UnsupportedAddressTypeException();
229 | }
230 | }
231 |
232 | @Override
233 | public UnixSocket socket() {
234 | return new UnixSocket(this);
235 | }
236 |
237 | @Override
238 | public long write(ByteBuffer[] srcs, int offset, int length)
239 | throws IOException {
240 |
241 | if (isConnected()) {
242 | return super.write(srcs, offset, length);
243 | } else if (isIdle()) {
244 | return 0;
245 | } else {
246 | throw new ClosedChannelException();
247 | }
248 | }
249 |
250 | @Override
251 | public int read(ByteBuffer dst) throws IOException {
252 | if (isConnected()) {
253 | return super.read(dst);
254 | } else if (isIdle()) {
255 | return 0;
256 | } else {
257 | throw new ClosedChannelException();
258 | }
259 | }
260 |
261 | @Override
262 | public int write(ByteBuffer src) throws IOException {
263 | if (isConnected()) {
264 | return super.write(src);
265 | } else if (isIdle()) {
266 | return 0;
267 | } else {
268 | throw new ClosedChannelException();
269 | }
270 | }
271 |
272 | @Override
273 | public SocketAddress getRemoteAddress() throws IOException {
274 | return remoteAddress;
275 | }
276 |
277 | @Override
278 | public SocketAddress getLocalAddress() throws IOException {
279 | return localAddress;
280 | }
281 |
282 | private static class DefaultOptionsHolder {
283 | static final Set> defaultOptions = defaultOptions();
284 |
285 | private static Set> defaultOptions() {
286 | HashSet> set = new HashSet>(5);
287 | set.add(UnixSocketOptions.SO_SNDBUF);
288 | set.add(UnixSocketOptions.SO_SNDTIMEO);
289 | set.add(UnixSocketOptions.SO_RCVBUF);
290 | set.add(UnixSocketOptions.SO_RCVTIMEO);
291 | set.add(UnixSocketOptions.SO_PEERCRED);
292 | set.add(UnixSocketOptions.SO_KEEPALIVE);
293 | set.add(UnixSocketOptions.SO_PASSCRED);
294 | return Collections.unmodifiableSet(set);
295 | }
296 | }
297 |
298 | @Override
299 | public final Set> supportedOptions() {
300 | return DefaultOptionsHolder.defaultOptions;
301 | }
302 |
303 | @Override
304 | public T getOption(SocketOption name) throws IOException {
305 | if (!supportedOptions().contains(name)) {
306 | throw new UnsupportedOperationException("'" + name
307 | + "' not supported");
308 | }
309 | return Common.getSocketOption(getFD(), name);
310 | }
311 |
312 | @Override
313 | public SocketChannel setOption(SocketOption name, T value)
314 | throws IOException {
315 | if (name == null) {
316 | throw new IllegalArgumentException("name may not be null");
317 | }
318 | if (!supportedOptions().contains(name)) {
319 | throw new UnsupportedOperationException("'" + name
320 | + "' not supported");
321 | }
322 | Common.setSocketOption(getFD(), name, value);
323 | return this;
324 | }
325 |
326 | @Override
327 | public synchronized UnixSocketChannel bind(SocketAddress local) throws IOException {
328 | localAddress = bindHandler.bind(getFD(), local);
329 | return this;
330 | }
331 |
332 | }
333 |
--------------------------------------------------------------------------------
/src/main/java/jnr/unixsocket/UnixSocketOptions.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Fritz Elfert
3 | *
4 | * This file is part of the JNR project.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package jnr.unixsocket;
20 |
21 | import java.net.SocketOption;
22 |
23 | /**
24 | * Defines common socket options for AF_UNIX sockets.
25 | */
26 | public final class UnixSocketOptions {
27 |
28 | private static class GenericOption implements SocketOption {
29 | private final String name;
30 | private final Class type;
31 | GenericOption(String name, Class type) {
32 | this.name = name;
33 | this.type = type;
34 | }
35 | @Override public String name() { return name; }
36 | @Override public Class type() { return type; }
37 | @Override public String toString() { return name; }
38 | }
39 |
40 | /**
41 | * Get/Set size of the socket send buffer.
42 | */
43 | public static final SocketOption SO_SNDBUF =
44 | new GenericOption("SO_SNDBUF", Integer.class);
45 |
46 | /**
47 | * Get/Set send timeout.
48 | */
49 | public static final SocketOption SO_SNDTIMEO =
50 | new GenericOption("SO_SNDTIMEO", Integer.class);
51 |
52 | /**
53 | * Get/Set size of the socket receive buffer.
54 | */
55 | public static final SocketOption SO_RCVBUF =
56 | new GenericOption("SO_RCVBUF", Integer.class);
57 |
58 | /**
59 | * Get/Set receive timeout.
60 | */
61 | public static final SocketOption SO_RCVTIMEO =
62 | new GenericOption("SO_RCVTIMEO", Integer.class);
63 |
64 | /**
65 | * Keep connection alive.
66 | */
67 | public static final SocketOption SO_KEEPALIVE =
68 | new GenericOption("SO_KEEPALIVE", Boolean.class);
69 |
70 | /**
71 | * Fetch peer credentials.
72 | */
73 | public static final SocketOption SO_PEERCRED =
74 | new GenericOption("SO_PEERCRED", Credentials.class);
75 |
76 | /**
77 | * Enable credential transmission.
78 | */
79 | public static final SocketOption SO_PASSCRED =
80 | new GenericOption("SO_PASSCRED", Boolean.class);
81 |
82 | }
83 |
84 |
--------------------------------------------------------------------------------
/src/main/java/jnr/unixsocket/impl/AbstractNativeDatagramChannel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Fritz Elfert
3 | *
4 | * This file is part of the JNR project.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package jnr.unixsocket.impl;
20 |
21 | import jnr.enxio.channels.Native;
22 | import jnr.enxio.channels.NativeSelectableChannel;
23 | import jnr.enxio.channels.NativeSelectorProvider;
24 |
25 | import java.io.IOException;
26 | import java.nio.ByteBuffer;
27 | import java.nio.channels.ByteChannel;
28 | import java.nio.channels.DatagramChannel;
29 | import java.nio.channels.spi.SelectorProvider;
30 |
31 | public abstract class AbstractNativeDatagramChannel extends DatagramChannel
32 | implements ByteChannel, NativeSelectableChannel {
33 |
34 | private final Common common;
35 |
36 | public AbstractNativeDatagramChannel(int fd) {
37 | this(NativeSelectorProvider.getInstance(), fd);
38 | }
39 |
40 | AbstractNativeDatagramChannel(SelectorProvider provider, int fd) {
41 | super(provider);
42 | common = new Common(fd);
43 | }
44 |
45 | public void setFD(int fd) {
46 | common.setFD(fd);
47 | }
48 |
49 | public final int getFD() {
50 | return common.getFD();
51 | }
52 |
53 | @Override
54 | protected void implCloseSelectableChannel() throws IOException {
55 | Native.close(common.getFD());
56 | }
57 |
58 | @Override
59 | protected void implConfigureBlocking(boolean block) throws IOException {
60 | Native.setBlocking(common.getFD(), block);
61 | }
62 |
63 | public int read(ByteBuffer dst) throws IOException {
64 | return common.read(dst);
65 | }
66 |
67 | @Override
68 | public long read(ByteBuffer[] dsts, int offset,
69 | int length) throws IOException {
70 | return common.read(dsts, offset, length);
71 | }
72 |
73 | public int write(ByteBuffer src) throws IOException {
74 | return common.write(src);
75 | }
76 |
77 | @Override
78 | public long write(ByteBuffer[] srcs, int offset,
79 | int length) throws IOException {
80 | return common.write(srcs, offset, length);
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/src/main/java/jnr/unixsocket/impl/AbstractNativeServerSocketChannel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 Jesse Wilson
3 | *
4 | * This file is part of the JNR project.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package jnr.unixsocket.impl;
20 |
21 | import jnr.constants.platform.Shutdown;
22 | import jnr.enxio.channels.Native;
23 | import jnr.enxio.channels.NativeServerSocketChannel;
24 |
25 | import java.io.IOException;
26 | import java.nio.channels.spi.SelectorProvider;
27 |
28 | public abstract class AbstractNativeServerSocketChannel extends NativeServerSocketChannel {
29 | public AbstractNativeServerSocketChannel(int fd) {
30 | super(fd);
31 | }
32 |
33 | public AbstractNativeServerSocketChannel(SelectorProvider provider, int fd, int ops) {
34 | super(provider, fd, ops);
35 | }
36 |
37 | @Override
38 | protected void implCloseSelectableChannel() throws IOException {
39 | // Shutdown to interrupt any potentially blocked threads. This is necessary on Linux.
40 | Native.shutdown(getFD(), SHUT_RD);
41 | Native.close(getFD());
42 | }
43 |
44 | private static final int SHUT_RD = Shutdown.SHUT_RD.intValue();
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/jnr/unixsocket/impl/AbstractNativeSocketChannel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Marcus Linke
3 | *
4 | * This file is part of the JNR project.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package jnr.unixsocket.impl;
20 |
21 | import java.io.IOException;
22 | import java.nio.ByteBuffer;
23 | import java.nio.channels.ByteChannel;
24 | import java.nio.channels.SocketChannel;
25 | import java.nio.channels.spi.SelectorProvider;
26 |
27 | import jnr.constants.platform.Errno;
28 | import jnr.constants.platform.Shutdown;
29 | import jnr.enxio.channels.Native;
30 | import jnr.enxio.channels.NativeException;
31 | import jnr.enxio.channels.NativeSelectableChannel;
32 | import jnr.enxio.channels.NativeSelectorProvider;
33 |
34 | public abstract class AbstractNativeSocketChannel extends SocketChannel
35 | implements ByteChannel, NativeSelectableChannel {
36 |
37 | private final Common common;
38 |
39 | public AbstractNativeSocketChannel(int fd) {
40 | this(NativeSelectorProvider.getInstance(), fd);
41 | }
42 |
43 | AbstractNativeSocketChannel(SelectorProvider provider, int fd) {
44 | super(provider);
45 | common = new Common(fd);
46 | }
47 |
48 | public void setFD(int fd) {
49 | common.setFD(fd);
50 | }
51 |
52 | public final int getFD() {
53 | return common.getFD();
54 | }
55 |
56 | @Override
57 | protected void implCloseSelectableChannel() throws IOException {
58 | if (this.isConnected()) {
59 | this.shutdownInput();
60 | this.shutdownOutput();
61 | }
62 |
63 | Native.close(common.getFD());
64 | }
65 |
66 | @Override
67 | protected void implConfigureBlocking(boolean block) throws IOException {
68 | Native.setBlocking(common.getFD(), block);
69 | }
70 |
71 | public int read(ByteBuffer dst) throws IOException {
72 | return common.read(dst);
73 | }
74 |
75 | @Override
76 | public long read(ByteBuffer[] dsts, int offset,
77 | int length) throws IOException {
78 | return common.read(dsts, offset, length);
79 | }
80 |
81 | public int write(ByteBuffer src) throws IOException {
82 | return common.write(src);
83 | }
84 |
85 | @Override
86 | public long write(ByteBuffer[] srcs, int offset,
87 | int length) throws IOException {
88 | return common.write(srcs, offset, length);
89 | }
90 |
91 | @Override
92 | public SocketChannel shutdownInput() throws IOException {
93 | int n = Native.shutdown(common.getFD(), SHUT_RD);
94 | if (n < 0 && Native.getLastError() != Errno.ENOTCONN) {
95 | throw new NativeException(Native.getLastErrorString(), Native.getLastError());
96 | }
97 | return this;
98 | }
99 |
100 | @Override
101 | public SocketChannel shutdownOutput() throws IOException {
102 | int n = Native.shutdown(common.getFD(), SHUT_WR);
103 | if (n < 0 && Native.getLastError() != Errno.ENOTCONN) {
104 | throw new NativeException(Native.getLastErrorString(), Native.getLastError());
105 | }
106 | return this;
107 | }
108 |
109 | private static final int SHUT_RD = Shutdown.SHUT_RD.intValue();
110 | private static final int SHUT_WR = Shutdown.SHUT_WR.intValue();
111 | }
112 |
--------------------------------------------------------------------------------
/src/main/java/jnr/unixsocket/impl/Common.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Fritz Elfert
3 | *
4 | * This file is part of the JNR project.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package jnr.unixsocket.impl;
20 |
21 | import java.io.IOException;
22 | import java.nio.ByteBuffer;
23 |
24 | import jnr.constants.platform.Errno;
25 | import jnr.enxio.channels.Native;
26 | import jnr.enxio.channels.NativeException;
27 |
28 | /**
29 | * Helper class, providing common methods.
30 | */
31 | final class Common {
32 |
33 | private int _fd = -1;
34 |
35 | Common(int fd) {
36 | _fd = fd;
37 | }
38 |
39 | void setFD(int fd) {
40 | _fd = fd;
41 | }
42 |
43 | int getFD() {
44 | return _fd;
45 | }
46 |
47 | int read(ByteBuffer dst) throws IOException {
48 |
49 | ByteBuffer buffer = ByteBuffer.allocate(dst.remaining());
50 |
51 | int n = Native.read(_fd, buffer);
52 |
53 | buffer.flip();
54 |
55 | dst.put(buffer);
56 |
57 | switch (n) {
58 | case 0:
59 | return -1;
60 |
61 | case -1:
62 | Errno lastError = Native.getLastError();
63 | switch (lastError) {
64 | case EAGAIN:
65 | case EWOULDBLOCK:
66 | return 0;
67 |
68 | default:
69 | throw new NativeException(Native.getLastErrorString(), lastError);
70 | }
71 |
72 | default: {
73 |
74 | return n;
75 | }
76 | }
77 | }
78 |
79 | long read(ByteBuffer[] dsts, int offset, int length)
80 | throws IOException {
81 | long total = 0;
82 |
83 | for (int i = 0; i < length; i++) {
84 | ByteBuffer dst = dsts[offset + i];
85 | long read = read(dst);
86 | if (read == -1) {
87 | return read;
88 | }
89 | total += read;
90 | }
91 |
92 | return total;
93 | }
94 |
95 | int write(ByteBuffer src) throws IOException {
96 |
97 | int r = src.remaining();
98 |
99 | ByteBuffer buffer = ByteBuffer.allocate(r);
100 |
101 | buffer.put(src);
102 |
103 | buffer.position(0);
104 |
105 | int n = Native.write(_fd, buffer);
106 |
107 | if (n >=0 ) {
108 | if (n < r) {
109 | src.position(src.position()-(r-n));
110 | }
111 | } else {
112 | Errno lastError = Native.getLastError();
113 | switch (lastError) {
114 | case EAGAIN:
115 | case EWOULDBLOCK:
116 | src.position(src.position()-r);
117 | return 0;
118 | default:
119 | throw new NativeException(Native.getLastErrorString(), lastError);
120 | }
121 | }
122 |
123 | return n;
124 | }
125 |
126 | long write(ByteBuffer[] srcs, int offset, int length) throws IOException {
127 |
128 | long result = 0;
129 |
130 | for (int index = offset; index < length; ++index) {
131 | ByteBuffer buffer = srcs[index];
132 | int remaining = buffer.remaining();
133 | int written = 0;
134 | while (true) {
135 | int w = write(buffer);
136 | written += w;
137 | if (w == 0 || written == remaining) {
138 | break;
139 | }
140 | }
141 | result += written;
142 | if (written < remaining) {
143 | break;
144 | }
145 | }
146 |
147 | return result;
148 | }
149 |
150 | }
151 |
--------------------------------------------------------------------------------
/src/test/java/jnr/unixsocket/BasicDatagramFunctionalityTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Fritz Elfert
3 | *
4 | * This file is part of the JNR project.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 | package jnr.unixsocket;
20 |
21 | import static junit.framework.Assert.assertEquals;
22 | import static junit.framework.Assert.assertFalse;
23 | import static junit.framework.Assert.assertTrue;
24 | import static junit.framework.Assert.fail;
25 |
26 | import java.io.File;
27 | import java.io.IOException;
28 | import java.net.SocketException;
29 | import java.nio.ByteBuffer;
30 | import java.nio.channels.AlreadyBoundException;
31 | import java.nio.channels.DatagramChannel;
32 | import java.nio.charset.StandardCharsets;
33 | import java.nio.file.Files;
34 |
35 | import jnr.ffi.Platform;
36 | import jnr.ffi.Platform.OS;
37 |
38 | import org.junit.Assume;
39 | import org.junit.Test;
40 |
41 | public class BasicDatagramFunctionalityTest {
42 | private static final String DATA = "foo bar baz. The quick brown fox jumps over the lazy dog. ";
43 | volatile Throwable serverException;
44 | volatile long received = 0;
45 |
46 | private UnixSocketAddress makeAddress() throws IOException {
47 | File socketFile = Files.createTempFile("jnr-unixsocket-test", ".sock").toFile();
48 | socketFile.delete();
49 | socketFile.deleteOnExit();
50 | return new UnixSocketAddress(socketFile);
51 | }
52 |
53 | private void basicOperation(final long minBytesToSend) throws Throwable {
54 | serverException = null;
55 | final StringBuffer rxdata = new StringBuffer();
56 | final StringBuffer txdata = new StringBuffer();
57 | final ByteBuffer rxbuf = ByteBuffer.allocate(1024);
58 | final ByteBuffer txbuf = ByteBuffer.allocate(2024);
59 | final UnixSocketAddress serverAddress = makeAddress();
60 |
61 | Thread serverThread = new Thread("server side") {
62 | final UnixDatagramChannel serverChannel = UnixDatagramChannel.open().bind(serverAddress);
63 |
64 | public void run() {
65 | while (null == serverException) {
66 | try {
67 | rxbuf.clear();
68 | serverChannel.receive(rxbuf);
69 | rxbuf.flip();
70 | int count = rxbuf.limit();
71 | rxdata.append(StandardCharsets.UTF_8.decode(rxbuf).toString());
72 | received += count;;
73 | } catch (IOException ex) {
74 | serverException = ex;
75 | }
76 | }
77 | }
78 | };
79 | serverThread.start();
80 |
81 | // client logic
82 | DatagramChannel clientChannel = UnixDatagramChannel.open();
83 | received = 0;
84 | long written = 0;
85 | while (null == serverException && written < minBytesToSend) {
86 | txbuf.put(StandardCharsets.UTF_8.encode(DATA));
87 | txbuf.flip();
88 | written += clientChannel.send(txbuf, serverAddress);
89 | txbuf.compact();
90 | txdata.append(DATA);
91 | if (null != serverException) {
92 | throw new Exception().initCause(serverException);
93 | }
94 | }
95 | clientChannel.close();
96 | while (null == serverException && received < written) {
97 | Thread.sleep(100);
98 | }
99 |
100 | assertTrue("More than 0 bytes written", written > 0);
101 | assertEquals("received", written, received);
102 | assertEquals("received data", txdata.toString(), rxdata.toString());
103 | }
104 |
105 | @Test
106 | public void smallBasicOperationTest() throws Throwable {
107 | basicOperation(DATA.length());
108 | }
109 |
110 | @Test
111 | public void largeBasicOperationTest() throws Throwable {
112 | Assume.assumeTrue(OS.LINUX == Platform.getNativePlatform().getOS());
113 |
114 | basicOperation(1000L * DATA.length());
115 | }
116 |
117 | @Test
118 | public void doubleBindTest() throws Exception {
119 | UnixDatagramChannel ch = UnixDatagramChannel.open().bind(null);
120 | try {
121 | ch.bind(null);
122 | fail("Should have thrown AlreadyBoundException");
123 | } catch (AlreadyBoundException abx) {
124 | try {
125 | ch.socket().bind(null);
126 | fail("Should have thrown SocketException");
127 | } catch (SocketException sx) {
128 | assertEquals("exception message", sx.getMessage(), "already bound");
129 | }
130 | }
131 | }
132 |
133 | @Test
134 | public void pairTest() throws Exception {
135 | UnixDatagramChannel[] sp = UnixDatagramChannel.pair();
136 | for (final UnixDatagramChannel ch : sp) {
137 | assertTrue("Channel is connected", ch.isConnected());
138 | assertTrue("Channel is bound", ch.isBound());
139 | assertFalse("Channel's socket is not closed", ch.socket().isClosed());
140 | }
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/src/test/java/jnr/unixsocket/BasicFunctionalityTest.java:
--------------------------------------------------------------------------------
1 |
2 | package jnr.unixsocket;
3 |
4 | import jnr.enxio.channels.NativeSelectorProvider;
5 | import org.junit.After;
6 | import org.junit.Before;
7 | import org.junit.Test;
8 |
9 | import java.io.IOException;
10 | import java.io.InputStreamReader;
11 | import java.net.SocketException;
12 | import java.nio.ByteBuffer;
13 | import java.nio.CharBuffer;
14 | import java.nio.channels.AlreadyBoundException;
15 | import java.nio.channels.Channels;
16 | import java.nio.channels.SelectionKey;
17 | import java.nio.channels.Selector;
18 | import java.util.Set;
19 |
20 | import static java.nio.charset.StandardCharsets.UTF_8;
21 | import static org.junit.Assert.assertEquals;
22 | import static org.junit.Assert.assertFalse;
23 | import static org.junit.Assert.assertNotNull;
24 | import static org.junit.Assert.assertTrue;
25 | import static org.junit.Assert.fail;
26 |
27 | public class BasicFunctionalityTest {
28 | private static final String DATA = "blah blah";
29 |
30 | private UnixSocketPair socketPair;
31 | private Thread server;
32 | private volatile Exception serverException;
33 |
34 | @Before
35 | public void setUp() throws Exception {
36 | socketPair = new UnixSocketPair();
37 | }
38 |
39 | @After
40 | public void tearDown() throws Exception {
41 | socketPair.close();
42 | }
43 |
44 | @Test
45 | public void doubleBindTest() throws Exception {
46 | UnixSocketChannel ch = UnixSocketChannel.open().bind(null);
47 | try {
48 | ch.bind(null);
49 | fail("Should have thrown AlreadyBoundException");
50 | } catch (AlreadyBoundException abx) {
51 | try {
52 | ch.socket().bind(null);
53 | fail("Should have thrown SocketException");
54 | } catch (SocketException sx) {
55 | assertEquals("exception message", sx.getMessage(), "already bound");
56 | }
57 | }
58 | }
59 |
60 | @Test
61 | public void pairTest() throws Exception {
62 | UnixSocketChannel[] sp = UnixSocketChannel.pair();
63 | for (final UnixSocketChannel ch : sp) {
64 | assertTrue("Channel is connected", ch.isConnected());
65 | assertTrue("Channel is bound", ch.isBound());
66 | assertFalse("Channel's socket is not closed", ch.socket().isClosed());
67 | }
68 | }
69 |
70 | @Test
71 | public void basicOperation() throws Exception {
72 | // server logic
73 | final UnixServerSocketChannel channel = UnixServerSocketChannel.open();
74 | final Selector sel = NativeSelectorProvider.getInstance().openSelector();
75 | channel.configureBlocking(false);
76 | channel.socket().bind(socketPair.socketAddress());
77 | channel.register(sel, SelectionKey.OP_ACCEPT, new ServerActor(channel, sel));
78 |
79 | // TODO: This is ugly but simple enough. Many failures on server side will cause client to hang.
80 | server = new Thread("server side") {
81 | public void run() {
82 | try {
83 | while (sel.select() > 0) {
84 | Set keys = sel.selectedKeys();
85 |
86 | assertNotNull(keys);
87 | assertTrue(keys.size() > 0);
88 |
89 | for (SelectionKey k : keys) {
90 | assertTrue(k.attachment() instanceof Actor);
91 |
92 | Actor a = (Actor) k.attachment();
93 | if (!a.rxready()) {
94 | k.cancel();
95 | }
96 | }
97 | }
98 | } catch (Exception ex) {
99 | serverException = ex;
100 | }
101 | }
102 | };
103 |
104 | server.start();
105 |
106 | // client logic
107 | UnixSocketChannel channel2 = UnixSocketChannel.open(socketPair.socketAddress());
108 |
109 | assertEquals(socketPair.socketAddress(), channel2.getRemoteSocketAddress());
110 |
111 | Channels.newOutputStream(channel2).write(DATA.getBytes(UTF_8));
112 |
113 | InputStreamReader r = new InputStreamReader(Channels.newInputStream(channel2), UTF_8);
114 | CharBuffer result = CharBuffer.allocate(1024);
115 | r.read(result);
116 |
117 | assertEquals(DATA.length(), result.position());
118 |
119 | result.flip();
120 |
121 | assertEquals(DATA, result.toString());
122 |
123 | if (serverException != null) throw serverException;
124 | }
125 |
126 | static interface Actor {
127 | public boolean rxready();
128 | }
129 |
130 | final class ServerActor implements Actor {
131 | private final UnixServerSocketChannel channel;
132 | private final Selector selector;
133 |
134 | public ServerActor(UnixServerSocketChannel channel, Selector selector) {
135 | this.channel = channel;
136 | this.selector = selector;
137 | }
138 |
139 | public final boolean rxready() {
140 | try {
141 | UnixSocketChannel client = channel.accept();
142 |
143 | if (client == null) {
144 | // nonblocking result
145 | return false;
146 | }
147 | assertEquals(socketPair.socketAddress(), client.getLocalSocketAddress());
148 | assertEquals("", client.getRemoteSocketAddress().getStruct().getPath());
149 |
150 | client.configureBlocking(false);
151 | client.register(selector, SelectionKey.OP_READ, new ClientActor(client));
152 |
153 | return true;
154 | } catch (IOException ex) {
155 | return false;
156 | }
157 | }
158 | }
159 |
160 | final class ClientActor implements Actor {
161 | private final UnixSocketChannel channel;
162 |
163 | public ClientActor(UnixSocketChannel channel) {
164 | this.channel = channel;
165 | }
166 |
167 | public final boolean rxready() {
168 | try {
169 | ByteBuffer buf = ByteBuffer.allocate(1024);
170 | int n = channel.read(buf);
171 | assertEquals("", channel.getRemoteSocketAddress().getStruct().getPath());
172 |
173 | assertEquals(DATA.length(), n);
174 |
175 | if (n > 0) {
176 | buf.flip();
177 | channel.write(buf);
178 | return true;
179 | } else if (n < 0) {
180 | return false;
181 | }
182 |
183 | } catch (IOException ex) {
184 | ex.printStackTrace();
185 | return false;
186 | }
187 | return true;
188 | }
189 | }
190 | }
191 |
--------------------------------------------------------------------------------
/src/test/java/jnr/unixsocket/ChannelOptionsTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Fritz Elfert
3 | *
4 | * This file is part of the JNR project.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | */
19 | package jnr.unixsocket;
20 |
21 | import static junit.framework.Assert.*;
22 |
23 | import jnr.ffi.Platform;
24 | import jnr.ffi.Platform.OS;
25 |
26 | import org.junit.Assume;
27 | import org.junit.Test;
28 |
29 | public class ChannelOptionsTest {
30 |
31 | @Test
32 | public void readonlyDatagramChannelOptionTest() throws Exception {
33 | Assume.assumeTrue(OS.LINUX == Platform.getNativePlatform().getOS());
34 |
35 | UnixDatagramChannel[] sp = UnixDatagramChannel.pair();
36 | UnixDatagramChannel ch = sp[0];
37 | Credentials c = ch.socket().getCredentials();
38 | try {
39 | // SO_PEERCRED is readonly
40 | ch.setOption(UnixSocketOptions.SO_PEERCRED, c);
41 | fail("Should have thrown AssertionError");
42 | } catch (AssertionError ae) {
43 | assertEquals("exception message", ae.getMessage(), "Option not found or not writable");
44 | }
45 | }
46 |
47 | @Test
48 | public void readonlySocketChannelOptionTest() throws Exception {
49 | Assume.assumeTrue(OS.LINUX == Platform.getNativePlatform().getOS());
50 |
51 | UnixSocketChannel[] sp = UnixSocketChannel.pair();
52 | UnixSocketChannel ch = sp[0];
53 | Credentials c = ch.socket().getCredentials();
54 | try {
55 | // SO_PEERCRED is readonly
56 | ch.setOption(UnixSocketOptions.SO_PEERCRED, c);
57 | fail("Should have thrown AssertionError");
58 | } catch (AssertionError ae) {
59 | assertEquals("exception message", ae.getMessage(), "Option not found or not writable");
60 | }
61 | }
62 |
63 | @Test
64 | public void unsupportedChannelOptionTest() throws Exception {
65 | UnixDatagramChannel ch = UnixDatagramChannel.open();
66 | try {
67 | // SO_KEEPALIVE is suitable only for SOCK_STREAM sockets
68 | ch.getOption(UnixSocketOptions.SO_KEEPALIVE);
69 | fail("Should have thrown UnsupportedOperationException");
70 | } catch (UnsupportedOperationException uoe) {
71 | assertEquals("exception message", uoe.getMessage(), "'SO_KEEPALIVE' not supported");
72 | }
73 | }
74 |
75 | @Test
76 | public void keepaliveOptionTest() throws Exception {
77 | UnixSocketChannel ch = UnixSocketChannel.open();
78 | boolean origValue = ch.getOption(UnixSocketOptions.SO_KEEPALIVE).booleanValue();
79 | assertEquals("Initial value of SO_KEEPALIVE", origValue, false);
80 | ch.setOption(UnixSocketOptions.SO_KEEPALIVE, Boolean.TRUE);
81 | boolean changedValue = ch.getOption(UnixSocketOptions.SO_KEEPALIVE).booleanValue();
82 | assertEquals("Changed value of SO_KEEPALIVE", changedValue, true);
83 | ch.setOption(UnixSocketOptions.SO_KEEPALIVE, Boolean.FALSE);
84 | changedValue = ch.getOption(UnixSocketOptions.SO_KEEPALIVE).booleanValue();
85 | assertEquals("Changed value of SO_KEEPALIVE", changedValue, origValue);
86 | }
87 |
88 | @Test
89 | public void invalidOptionValueTest() throws Exception {
90 | UnixSocketChannel ch = UnixSocketChannel.open();
91 | try {
92 | ch.setOption(UnixSocketOptions.SO_RCVTIMEO, Integer.valueOf(-1));
93 | fail("Should have thrown IllegalArgumentException");
94 | } catch (IllegalArgumentException iae) {
95 | assertEquals("exception message", iae.getMessage(), "Invalid send/receive timeout");
96 | }
97 | try {
98 | ch.setOption(UnixSocketOptions.SO_SNDTIMEO, Integer.valueOf(-1));
99 | fail("Should have thrown IllegalArgumentException");
100 | } catch (IllegalArgumentException iae) {
101 | assertEquals("exception message", iae.getMessage(), "Invalid send/receive timeout");
102 | }
103 | try {
104 | ch.setOption(UnixSocketOptions.SO_RCVBUF, Integer.valueOf(-1));
105 | fail("Should have thrown IllegalArgumentException");
106 | } catch (IllegalArgumentException iae) {
107 | assertEquals("exception message", iae.getMessage(), "Invalid send/receive buffer size");
108 | }
109 | try {
110 | ch.setOption(UnixSocketOptions.SO_SNDBUF, Integer.valueOf(-1));
111 | fail("Should have thrown IllegalArgumentException");
112 | } catch (IllegalArgumentException iae) {
113 | assertEquals("exception message", iae.getMessage(), "Invalid send/receive buffer size");
114 | }
115 | }
116 |
117 | @Test
118 | // Linux doubles the values when setting.
119 | // OSX keeps settings consistent but restricts possible values to a multiple of 256
120 | // Check what other platforms do.
121 | public void socketBufferTest() throws Exception {
122 | UnixDatagramChannel ch = UnixDatagramChannel.open();
123 | int rxs = ch.getOption(UnixSocketOptions.SO_RCVBUF);
124 | int txs = ch.getOption(UnixSocketOptions.SO_SNDBUF);
125 | assertTrue("receive buffer size >= 256", rxs >= 256);
126 | assertTrue("send buffer size >= 256", txs >= 256);
127 | /*
128 | System.out.println(String.format("rxbuf=%d, txbuf=%d", rxs, txs));
129 | ch.setOption(UnixSocketOptions.SO_RCVBUF, rxs - 100);
130 | ch.setOption(UnixSocketOptions.SO_SNDBUF, txs - 100);
131 | rxs = ch.getOption(UnixSocketOptions.SO_RCVBUF);
132 | txs = ch.getOption(UnixSocketOptions.SO_SNDBUF);
133 | System.out.println(String.format("rxbuf=%d, txbuf=%d", rxs, txs));
134 | */
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/src/test/java/jnr/unixsocket/CredentialsFunctionalTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the JNR project.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package jnr.unixsocket;
18 |
19 | import static org.junit.Assert.assertEquals;
20 | import static org.junit.Assert.assertNotNull;
21 | import static org.junit.Assert.fail;
22 |
23 | import java.io.File;
24 | import java.io.FileReader;
25 | import java.io.IOException;
26 | import java.lang.management.ManagementFactory;
27 | import java.util.concurrent.Callable;
28 | import java.util.concurrent.ExecutionException;
29 | import java.util.concurrent.ExecutorService;
30 | import java.util.concurrent.Executors;
31 | import java.util.concurrent.Future;
32 |
33 | import jnr.ffi.Platform;
34 | import jnr.ffi.Platform.OS;
35 |
36 | import org.junit.Assume;
37 | import org.junit.Before;
38 | import org.junit.Rule;
39 | import org.junit.Test;
40 | import org.junit.rules.TemporaryFolder;
41 |
42 | public class CredentialsFunctionalTest {
43 | @Rule public TemporaryFolder tempFolder = new TemporaryFolder();
44 |
45 | private File serverSocket;
46 | private ExecutorService async = Executors.newSingleThreadExecutor();
47 |
48 | @Before
49 | public void createSockets() throws IOException {
50 | Assume.assumeTrue(OS.LINUX == Platform.getNativePlatform().getOS());
51 |
52 | serverSocket = tempFolder.newFile("serverSocket");
53 | serverSocket.delete(); //JUnit is "helpful" and creates it for us
54 | }
55 |
56 | @Test(timeout=30000)
57 | public void credentials() throws IOException, ExecutionException, InterruptedException {
58 | UnixSocketAddress address = new UnixSocketAddress(serverSocket);
59 | final UnixServerSocket socket = new UnixServerSocket();
60 | socket.bind(address);
61 |
62 | Future socketFuture = async.submit(new Callable() {
63 | public UnixSocket call() throws Exception {
64 | return socket.accept();
65 | }
66 | });
67 |
68 | UnixSocketChannel client = UnixSocketChannel.open(address);
69 | UnixSocket server = socketFuture.get();
70 |
71 | assertNotNull("Client socket must be non-null.", client);
72 | assertNotNull("Server socket must be non-null.", server);
73 |
74 | Credentials clientCreds = client.socket().getCredentials();
75 | Credentials serverCreds = server.getCredentials();
76 |
77 | int myPid = getCurrentPid();
78 |
79 | assertEquals("Current PID should match client credentials",
80 | myPid, clientCreds.getPid());
81 | assertEquals("Current PID should match server credentials",
82 | myPid, serverCreds.getPid());
83 |
84 | assertEquals("Client/server running in same process, UID should be the same",
85 | clientCreds.getUid(), serverCreds.getUid());
86 |
87 | //don't have an easy way of getting effective GID, but they should be the same
88 | assertEquals("Client/server running in same process, GID should be the same",
89 | clientCreds.getGid(), serverCreds.getGid());
90 |
91 | // Verify, that results from new interface are the same
92 | Credentials newCreds = client.getOption(UnixSocketOptions.SO_PEERCRED);
93 | assertNotNull(newCreds);
94 | assertEquals("Current PID should match new API PID",
95 | myPid, newCreds.getPid());
96 | assertEquals("old/new API results (UID) should be the same",
97 | clientCreds.getUid(), newCreds.getUid());
98 | assertEquals("old/new API results (GID) should be the same",
99 | clientCreds.getGid(), newCreds.getGid());
100 | }
101 |
102 | public int getCurrentPid() {
103 | String[] nameParts = ManagementFactory.getRuntimeMXBean().getName().split("@", 2);
104 | assertEquals("Cannot determine PID", 2, nameParts.length);
105 | return Integer.parseInt(nameParts[0]);
106 | }
107 |
108 | /*
109 | * A Linux-only utility method.
110 | */
111 | public int getLoginUid() throws IOException {
112 | FileReader fr = null;
113 | StringBuilder uidText = new StringBuilder();
114 | try {
115 | fr = new FileReader("/proc/self/loginuid");
116 | char[] buf = new char[16];
117 | int read = -1;
118 | while ((read = fr.read(buf)) > -1) {
119 | uidText.append(buf, 0, read);
120 | }
121 | } catch (IOException ioe) {
122 | fail("Unable to determine login uid: " + ioe.getMessage());
123 | } finally {
124 | fr.close();
125 | }
126 |
127 | return Integer.parseInt(uidText.toString());
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/src/test/java/jnr/unixsocket/ForFDTest.java:
--------------------------------------------------------------------------------
1 | package jnr.unixsocket;
2 |
3 | import jnr.constants.platform.ProtocolFamily;
4 | import jnr.constants.platform.Sock;
5 | import org.junit.Test;
6 |
7 | import java.io.File;
8 | import java.nio.ByteBuffer;
9 | import java.nio.charset.StandardCharsets;
10 |
11 | import static junit.framework.Assert.assertEquals;
12 | import static junit.framework.Assert.assertNotNull;
13 | import static junit.framework.Assert.assertTrue;
14 |
15 | /**
16 | * Created by headius on 11/24/15.
17 | */
18 | public class ForFDTest {
19 | private static final File SOCKADDR = new File("/tmp/jnr-unixsocket-forfd" + System.currentTimeMillis() + ".sock");
20 | static { SOCKADDR.deleteOnExit(); }
21 | private static final UnixSocketAddress ADDRESS = new UnixSocketAddress(SOCKADDR);
22 | private static final String FOOBAR = "foobar";
23 |
24 | private volatile Exception serverException;
25 |
26 | @Test
27 | public void testForFD() throws Exception {
28 | int fd = 0;
29 | UnixSocketChannel channel = null;
30 |
31 | try {
32 | final UnixServerSocketChannel server = UnixServerSocketChannel.open();
33 | server.socket().bind(ADDRESS);
34 |
35 | new Thread("accept thread") {
36 | public void run() {
37 | UnixSocketChannel channel = null;
38 |
39 | try {
40 | channel = server.accept();
41 | channel.write(ByteBuffer.wrap(FOOBAR.getBytes(StandardCharsets.UTF_8)));
42 | } catch (Exception e) {
43 | serverException = e;
44 | } finally {
45 | try {channel.close();} catch (Exception e) {}
46 | }
47 | }
48 | }.start();
49 |
50 | fd = Native.socket(ProtocolFamily.PF_UNIX, Sock.SOCK_STREAM, 0);
51 |
52 | assertTrue("socket failed", fd > 0);
53 |
54 | int ret = Native.connect(fd, ADDRESS.getStruct(), ADDRESS.getStruct().length());
55 |
56 | assertTrue("connect failed", ret >= 0);
57 |
58 | channel = UnixSocketChannel.fromFD(fd);
59 |
60 | assertNotNull(channel);
61 |
62 | ByteBuffer buf = ByteBuffer.allocate(1024);
63 |
64 | channel.read(buf);
65 |
66 | assertEquals(FOOBAR.length(), buf.position());
67 |
68 | buf.flip();
69 | String result = new String(buf.array(), buf.position(), buf.limit(), "UTF-8");
70 |
71 | assertEquals(FOOBAR, result);
72 |
73 | if (serverException != null) throw serverException;
74 | } finally {
75 | channel.close();
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/test/java/jnr/unixsocket/SocketInteropTest.java:
--------------------------------------------------------------------------------
1 | package jnr.unixsocket;
2 |
3 | import org.junit.After;
4 | import org.junit.Before;
5 | import org.junit.Rule;
6 | import org.junit.Test;
7 | import org.junit.rules.Timeout;
8 | import org.junit.runner.RunWith;
9 | import org.junit.runners.Parameterized;
10 | import org.junit.runners.Parameterized.Parameter;
11 | import org.junit.runners.Parameterized.Parameters;
12 |
13 | import java.io.BufferedReader;
14 | import java.io.Closeable;
15 | import java.io.IOException;
16 | import java.io.InputStream;
17 | import java.io.InputStreamReader;
18 | import java.io.OutputStream;
19 | import java.net.SocketException;
20 | import java.nio.channels.AsynchronousCloseException;
21 | import java.nio.channels.ClosedByInterruptException;
22 | import java.nio.channels.ClosedChannelException;
23 | import java.nio.channels.NotYetBoundException;
24 | import java.util.Arrays;
25 | import java.util.List;
26 | import java.util.concurrent.TimeUnit;
27 |
28 | import static java.nio.charset.StandardCharsets.UTF_8;
29 | import static org.junit.Assert.assertEquals;
30 | import static org.junit.Assert.assertTrue;
31 | import static org.junit.Assert.fail;
32 | import static org.junit.Assume.assumeTrue;
33 |
34 | /**
35 | * Confirm that UNIX sockets work similarly to TCP sockets.
36 | */
37 | @RunWith(Parameterized.class)
38 | public class SocketInteropTest {
39 | @Rule
40 | public Timeout timeout = new Timeout(5, TimeUnit.SECONDS);
41 |
42 | @Parameter
43 | public TestSocketPair.Factory socketPairFactory;
44 |
45 | private TestSocketPair socketPair;
46 |
47 | @Parameters(name = "{0}")
48 | public static List