1 /*-
2 * Copyright (c) 2009-2010 The FreeBSD Foundation
3 * All rights reserved.
4 *
5 * This software was developed by Pawel Jakub Dawidek under sponsorship from
6 * the FreeBSD Foundation.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 #include <sys/types.h>
31 #include <sys/socket.h>
32
33 #include <errno.h>
34 #include <stdbool.h>
35 #include <stdint.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <unistd.h>
39
40 #include "pjdlog.h"
41 #include "proto_impl.h"
42
43 #define SP_CTX_MAGIC 0x50c3741
44 struct sp_ctx {
45 int sp_magic;
46 int sp_fd[2];
47 int sp_side;
48 #define SP_SIDE_UNDEF 0
49 #define SP_SIDE_CLIENT 1
50 #define SP_SIDE_SERVER 2
51 };
52
53 static void sp_close(void *ctx);
54
55 static int
sp_connect(const char * srcaddr,const char * dstaddr,int timeout,void ** ctxp)56 sp_connect(const char *srcaddr, const char *dstaddr, int timeout, void **ctxp)
57 {
58 struct sp_ctx *spctx;
59 int error;
60
61 PJDLOG_ASSERT(dstaddr != NULL);
62 PJDLOG_ASSERT(timeout >= -1);
63
64 if (strcmp(dstaddr, "socketpair://") != 0)
65 return (-1);
66
67 PJDLOG_ASSERT(srcaddr == NULL);
68
69 spctx = malloc(sizeof(*spctx));
70 if (spctx == NULL)
71 return (errno);
72
73 if (socketpair(PF_UNIX, SOCK_STREAM, 0, spctx->sp_fd) == -1) {
74 error = errno;
75 free(spctx);
76 return (error);
77 }
78
79 spctx->sp_side = SP_SIDE_UNDEF;
80 spctx->sp_magic = SP_CTX_MAGIC;
81 *ctxp = spctx;
82
83 return (0);
84 }
85
86 static int
sp_wrap(int fd,bool client,void ** ctxp)87 sp_wrap(int fd, bool client, void **ctxp)
88 {
89 struct sp_ctx *spctx;
90
91 PJDLOG_ASSERT(fd >= 0);
92
93 spctx = malloc(sizeof(*spctx));
94 if (spctx == NULL)
95 return (errno);
96
97 if (client) {
98 spctx->sp_side = SP_SIDE_CLIENT;
99 spctx->sp_fd[0] = fd;
100 spctx->sp_fd[1] = -1;
101 } else {
102 spctx->sp_side = SP_SIDE_SERVER;
103 spctx->sp_fd[0] = -1;
104 spctx->sp_fd[1] = fd;
105 }
106 spctx->sp_magic = SP_CTX_MAGIC;
107 *ctxp = spctx;
108
109 return (0);
110 }
111
112 static int
sp_send(void * ctx,const unsigned char * data,size_t size,int fd)113 sp_send(void *ctx, const unsigned char *data, size_t size, int fd)
114 {
115 struct sp_ctx *spctx = ctx;
116 int sock;
117
118 PJDLOG_ASSERT(spctx != NULL);
119 PJDLOG_ASSERT(spctx->sp_magic == SP_CTX_MAGIC);
120
121 switch (spctx->sp_side) {
122 case SP_SIDE_UNDEF:
123 /*
124 * If the first operation done by the caller is proto_send(),
125 * we assume this is the client.
126 */
127 /* FALLTHROUGH */
128 spctx->sp_side = SP_SIDE_CLIENT;
129 /* Close other end. */
130 close(spctx->sp_fd[1]);
131 spctx->sp_fd[1] = -1;
132 case SP_SIDE_CLIENT:
133 PJDLOG_ASSERT(spctx->sp_fd[0] >= 0);
134 sock = spctx->sp_fd[0];
135 break;
136 case SP_SIDE_SERVER:
137 PJDLOG_ASSERT(spctx->sp_fd[1] >= 0);
138 sock = spctx->sp_fd[1];
139 break;
140 default:
141 PJDLOG_ABORT("Invalid socket side (%d).", spctx->sp_side);
142 }
143
144 /* Someone is just trying to decide about side. */
145 if (data == NULL)
146 return (0);
147
148 return (proto_common_send(sock, data, size, fd));
149 }
150
151 static int
sp_recv(void * ctx,unsigned char * data,size_t size,int * fdp)152 sp_recv(void *ctx, unsigned char *data, size_t size, int *fdp)
153 {
154 struct sp_ctx *spctx = ctx;
155 int sock;
156
157 PJDLOG_ASSERT(spctx != NULL);
158 PJDLOG_ASSERT(spctx->sp_magic == SP_CTX_MAGIC);
159
160 switch (spctx->sp_side) {
161 case SP_SIDE_UNDEF:
162 /*
163 * If the first operation done by the caller is proto_recv(),
164 * we assume this is the server.
165 */
166 /* FALLTHROUGH */
167 spctx->sp_side = SP_SIDE_SERVER;
168 /* Close other end. */
169 close(spctx->sp_fd[0]);
170 spctx->sp_fd[0] = -1;
171 case SP_SIDE_SERVER:
172 PJDLOG_ASSERT(spctx->sp_fd[1] >= 0);
173 sock = spctx->sp_fd[1];
174 break;
175 case SP_SIDE_CLIENT:
176 PJDLOG_ASSERT(spctx->sp_fd[0] >= 0);
177 sock = spctx->sp_fd[0];
178 break;
179 default:
180 PJDLOG_ABORT("Invalid socket side (%d).", spctx->sp_side);
181 }
182
183 /* Someone is just trying to decide about side. */
184 if (data == NULL)
185 return (0);
186
187 return (proto_common_recv(sock, data, size, fdp));
188 }
189
190 static int
sp_descriptor(const void * ctx)191 sp_descriptor(const void *ctx)
192 {
193 const struct sp_ctx *spctx = ctx;
194
195 PJDLOG_ASSERT(spctx != NULL);
196 PJDLOG_ASSERT(spctx->sp_magic == SP_CTX_MAGIC);
197 PJDLOG_ASSERT(spctx->sp_side == SP_SIDE_CLIENT ||
198 spctx->sp_side == SP_SIDE_SERVER);
199
200 switch (spctx->sp_side) {
201 case SP_SIDE_CLIENT:
202 PJDLOG_ASSERT(spctx->sp_fd[0] >= 0);
203 return (spctx->sp_fd[0]);
204 case SP_SIDE_SERVER:
205 PJDLOG_ASSERT(spctx->sp_fd[1] >= 0);
206 return (spctx->sp_fd[1]);
207 }
208
209 PJDLOG_ABORT("Invalid socket side (%d).", spctx->sp_side);
210 }
211
212 static void
sp_close(void * ctx)213 sp_close(void *ctx)
214 {
215 struct sp_ctx *spctx = ctx;
216
217 PJDLOG_ASSERT(spctx != NULL);
218 PJDLOG_ASSERT(spctx->sp_magic == SP_CTX_MAGIC);
219
220 switch (spctx->sp_side) {
221 case SP_SIDE_UNDEF:
222 PJDLOG_ASSERT(spctx->sp_fd[0] >= 0);
223 close(spctx->sp_fd[0]);
224 spctx->sp_fd[0] = -1;
225 PJDLOG_ASSERT(spctx->sp_fd[1] >= 0);
226 close(spctx->sp_fd[1]);
227 spctx->sp_fd[1] = -1;
228 break;
229 case SP_SIDE_CLIENT:
230 PJDLOG_ASSERT(spctx->sp_fd[0] >= 0);
231 close(spctx->sp_fd[0]);
232 spctx->sp_fd[0] = -1;
233 PJDLOG_ASSERT(spctx->sp_fd[1] == -1);
234 break;
235 case SP_SIDE_SERVER:
236 PJDLOG_ASSERT(spctx->sp_fd[1] >= 0);
237 close(spctx->sp_fd[1]);
238 spctx->sp_fd[1] = -1;
239 PJDLOG_ASSERT(spctx->sp_fd[0] == -1);
240 break;
241 default:
242 PJDLOG_ABORT("Invalid socket side (%d).", spctx->sp_side);
243 }
244
245 spctx->sp_magic = 0;
246 free(spctx);
247 }
248
249 static struct proto sp_proto = {
250 .prt_name = "socketpair",
251 .prt_connect = sp_connect,
252 .prt_wrap = sp_wrap,
253 .prt_send = sp_send,
254 .prt_recv = sp_recv,
255 .prt_descriptor = sp_descriptor,
256 .prt_close = sp_close
257 };
258
259 static __constructor void
sp_ctor(void)260 sp_ctor(void)
261 {
262
263 proto_register(&sp_proto, false);
264 }
265