1/* Copyright (c) uint
2
3 Permission to use, copy, modify, and distribute this software for any
4 purpose with or without fee is hereby granted, provided that the above
5 copyright notice and this permission notice appear in all copies.
6
7 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10 SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14
15#include <errno.h>
16#include <stdio.h>
17#include <sys/socket.h>
18#include <sys/stat.h>
19#include <sys/time.h>
20#include <sys/un.h>
21#include <string.h>
22#include <unistd.h>
23
24#include "ulepipe.h"
25
26int
27ulepipe_open(ulepipe* conn, const char* path)
28{
29 int sverrno = 0;
30 int fd = -1;
31
32 if (!conn || !path) {
33 errno = EINVAL;
34 return -1;
35 }
36
37 conn->fd = -1;
38 conn->peer_len = 0;
39
40 if (strlen(path) >= sizeof(conn->addr.sun_path)) {
41 errno = ENAMETOOLONG;
42 return -1;
43 }
44
45 fd = socket(AF_UNIX, SOCK_DGRAM, 0);
46 if (fd == -1)
47 return -1;
48
49 memset(&conn->addr, 0, sizeof(conn->addr));
50 conn->addr.sun_family = AF_UNIX;
51 memcpy(conn->addr.sun_path, path, strlen(path) + 1);
52
53 if (bind(fd, (struct sockaddr*)&conn->addr, sizeof(conn->addr)) == -1) {
54 sverrno = errno;
55 close(fd);
56 errno = sverrno;
57 return -1;
58 }
59
60 conn->fd = fd;
61 return 0;
62}
63
64void
65ulepipe_close(ulepipe* conn)
66{
67 if (!conn || conn->fd < 0)
68 return;
69
70 close(conn->fd);
71 unlink(conn->addr.sun_path);
72 conn->fd = -1;
73 conn->peer_len = 0;
74}
75
76ssize_t
77ulepipe_send(const char* path, void* data, size_t size)
78{
79 struct sockaddr_un addr = { 0 };
80 struct sockaddr_un client = { 0 };
81 struct msghdr msg = { 0 };
82 struct iovec iov = { 0 };
83 struct timeval timeout = { ULEPIPE_TIMEOUT, 0 };
84 char dir[64] = { 0 };
85 int sverrno = 0;
86 ssize_t n = -1;
87 int fd = -1;
88 unsigned int i;
89
90 if (!path || (!data && size)) {
91 errno = EINVAL;
92 return -1;
93 }
94
95 if (strlen(path) >= sizeof(addr.sun_path)) {
96 errno = ENAMETOOLONG;
97 return -1;
98 }
99
100 fd = socket(AF_UNIX, SOCK_DGRAM, 0);
101 if (fd == -1)
102 return -1;
103
104 for (i = 0; i < 1000; i++) {
105 snprintf(dir, sizeof(dir), "/tmp/ulepipe.%ld.%u", (long)getpid(), i);
106 if (mkdir(dir, 0700) == 0)
107 break;
108 if (errno != EEXIST)
109 goto fail;
110 }
111 if (i == 1000) {
112 errno = EEXIST;
113 goto fail;
114 }
115
116 client.sun_family = AF_UNIX;
117 snprintf(client.sun_path, sizeof(client.sun_path), "%s/socket", dir);
118 if (bind(fd, (struct sockaddr*)&client, sizeof(client)) == -1 ||
119 setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) == -1)
120 goto done;
121
122 addr.sun_family = AF_UNIX;
123 memcpy(addr.sun_path, path, strlen(path) + 1);
124
125 do
126 n = sendto(fd, data, size, 0, (struct sockaddr*)&addr, sizeof(addr));
127 while (n == -1 && errno == EINTR);
128
129 if (n == -1)
130 goto done;
131 if ((size_t)n != size) {
132 errno = EIO;
133 n = -1;
134 goto done;
135 }
136
137 iov.iov_base = data;
138 iov.iov_len = size;
139 msg.msg_iov = &iov;
140 msg.msg_iovlen = 1;
141
142 do
143 n = recvmsg(fd, &msg, 0);
144 while (n == -1 && errno == EINTR);
145
146 if (n != -1 && (msg.msg_flags & MSG_TRUNC)) {
147 errno = EMSGSIZE;
148 n = -1;
149 }
150
151done:
152 sverrno = errno;
153 close(fd);
154 unlink(client.sun_path);
155 rmdir(dir);
156 errno = sverrno;
157 return n;
158
159fail:
160 sverrno = errno;
161 close(fd);
162 errno = sverrno;
163 return -1;
164}
165
166ssize_t
167ulepipe_reply(const ulepipe* conn, const void* data, size_t size)
168{
169 ssize_t n = -1;
170
171 if (!conn || conn->fd < 0 || !conn->peer_len || (!data && size)) {
172 errno = EINVAL;
173 return -1;
174 }
175
176 do
177 n = sendto(conn->fd, data, size, 0,
178 (struct sockaddr*)&conn->peer, conn->peer_len);
179 while (n == -1 && errno == EINTR);
180
181 return n;
182}
183
184ssize_t
185ulepipe_receive(ulepipe* conn, void* data, size_t size)
186{
187 struct msghdr msg = { 0 };
188 struct iovec iov = { 0 };
189 ssize_t n = -1;
190
191 if (!conn || conn->fd < 0 || (!data && size)) {
192 errno = EINVAL;
193 return -1;
194 }
195
196 conn->peer_len = 0;
197 iov.iov_base = data;
198 iov.iov_len = size;
199 msg.msg_iov = &iov;
200 msg.msg_iovlen = 1;
201 msg.msg_name = &conn->peer;
202 msg.msg_namelen = sizeof(conn->peer);
203
204 do
205 n = recvmsg(conn->fd, &msg, 0);
206 while (n == -1 && errno == EINTR);
207
208 if (n != -1 && (msg.msg_flags & MSG_TRUNC)) {
209 errno = EMSGSIZE;
210 return -1;
211 }
212
213 if (n != -1)
214 conn->peer_len = msg.msg_namelen;
215
216 return n;
217}
218