1/*- 2 * Copyright (c) 2009 Robert N. M. Watson 3 * All rights reserved. 4 * 5 * This software was developed at the University of Cambridge Computer 6 * Laboratory with support from a grant from Google, Inc. 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 AUTHOR 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 AUTHOR 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/cdefs.h> 31__FBSDID("$FreeBSD$"); 32 33#include <sys/socket.h> 34#include <sys/wait.h> 35#include <sys/un.h> 36 37#include <err.h> 38#include <errno.h> 39#include <limits.h> 40#include <stdio.h> 41#include <signal.h> 42#include <string.h> 43#include <unistd.h> 44 45#define min(x, y) (x < y ? x : y) 46 47#define BUFLEN 32768 48 49#define SEQPACKET_RCVBUF (131072-16) 50#define SEQPACKET_SNDBUF (131072-16) 51 52#define FAILERR(str) err(-1, "%s: %s", __func__, str) 53#define FAILNERR(str, n) err(-1, "%s %d: %s", __func__, n, str) 54#define FAILNMERR(str, n, m) err(-1, "%s %d %d: %s", __func__, n, m, str) 55#define FAILERRX(str) errx(-1, "%s: %s", __func__, str) 56#define FAILNERRX(str, n) errx(-1, "%s %d: %s", __func__, n, str) 57#define FAILNMERRX(str, n, m) errx(-1, "%s %d %d: %s", __func__, n, m, str) 58 59static int ann = 0; 60 61#define ANN() (ann ? warnx("%s: start", __func__) : 0) 62#define ANNN(n) (ann ? warnx("%s %d: start", __func__, (n)) : 0) 63#define ANNNM(n, m) (ann ? warnx("%s %d %d: start", __func__, (n), (m)) : 0) 64 65#define OK() warnx("%s: ok", __func__) 66#define OKN(n) warnx("%s %d: ok", __func__, (n)) 67#define OKNM(n, m) warnx("%s %d %d: ok", __func__, (n), (m)) 68 69#ifdef SO_NOSIGPIPE 70#define NEW_SOCKET(s) do { \ 71 int i; \ 72 \ 73 (s) = socket(PF_LOCAL, SOCK_SEQPACKET, 0); \ 74 if ((s) < 0) \ 75 FAILERR("socket"); \ 76 \ 77 i = 1; \ 78 if (setsockopt((s), SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof(i)) < 0) \ 79 FAILERR("setsockopt SO_NOSIGPIPE"); \ 80 \ 81 i = SEQPACKET_RCVBUF; \ 82 if (setsockopt((s), SOL_SOCKET, SO_RCVBUF, &i, sizeof(i)) < 0) \ 83 FAILERR("setsockopt SO_RCVBUF"); \ 84 \ 85 i = SEQPACKET_SNDBUF; \ 86 if (setsockopt((s), SOL_SOCKET, SO_SNDBUF, &i, sizeof(i)) < 0) \ 87 FAILERR("setsockopt SO_SNDBUF"); \ 88} while (0) 89#else 90#define NEW_SOCKET(s) do { \ 91 int i; \ 92 \ 93 (s) = socket(PF_LOCAL, SOCK_SEQPACKET, 0); \ 94 if ((s) < 0) \ 95 FAILERR("socket"); \ 96 \ 97 i = SEQPACKET_RCVBUF; \ 98 if (setsockopt((s), SOL_SOCKET, SO_RCVBUF, &i, sizeof(i)) < 0) \ 99 FAILERR("setsockopt SO_RCVBUF"); \ 100 \ 101 i = SEQPACKET_SNDBUF; \ 102 if (setsockopt((s), SOL_SOCKET, SO_SNDBUF, &i, sizeof(i)) < 0) \ 103 FAILERR("setsockopt SO_SNDBUF"); \ 104} while (0) 105#endif 106 107static void 108server(int s_listen) 109{ 110 char buffer[BUFLEN]; 111 ssize_t ssize_recv, ssize_send; 112 socklen_t socklen; 113 int i, s_accept; 114 115 while (1) { 116 s_accept = accept(s_listen, NULL, 0); 117 if (s_accept >= 0) { 118 i = SEQPACKET_RCVBUF; 119 if (setsockopt(s_accept, SOL_SOCKET, SO_RCVBUF, &i, 120 sizeof(i)) < 0) { 121 warn("server: setsockopt SO_RCVBUF"); 122 close(s_accept); 123 continue; 124 } 125 126 if (getsockopt(s_accept, SOL_SOCKET, SO_RCVBUF, &i, 127 &socklen) < 0) { 128 warn("server: getsockopt SO_RCVBUF"); 129 close(s_accept); 130 continue; 131 } 132 if (i != SEQPACKET_RCVBUF) { 133 warnx("server: getsockopt SO_RCVBUF wrong %d", 134 i); 135 close(s_accept); 136 continue; 137 } 138 139 socklen = sizeof(i); 140 if (getsockopt(s_accept, SOL_SOCKET, SO_SNDBUF, &i, 141 &socklen) < 0) { 142 warn("server: getsockopt SO_SNDBUF"); 143 close(s_accept); 144 continue; 145 } 146 if (i != SEQPACKET_SNDBUF) { 147 warnx("server: getsockopt SO_SNDBUF wrong %d", 148 i); 149 close(s_accept); 150 continue; 151 } 152 153 do { 154 ssize_recv = recv(s_accept, buffer, 155 sizeof(buffer), 0); 156 if (ssize_recv == 0) 157 break; 158 if (ssize_recv < 0) { 159 warn("server: recv"); 160 break; 161 } 162 ssize_send = send(s_accept, buffer, 163 ssize_recv, 0); 164 if (ssize_send == 0) 165 break; 166 if (ssize_send < 0) { 167 warn("server: send"); 168 break; 169 } 170 if (ssize_send != ssize_recv) 171 warnx("server: recv %d sent %d", 172 ssize_recv, ssize_send); 173 } while (1); 174 close(s_accept); 175 } else 176 warn("server: accept"); 177 } 178} 179 180static void 181test_connect(struct sockaddr_un *sun) 182{ 183 int s; 184 185 ANN(); 186 NEW_SOCKET(s); 187 if (connect(s, (struct sockaddr *)sun, sizeof(*sun)) < 0) 188 FAILERR("connect"); 189 (void)close(s); 190 OK(); 191} 192 193static void 194test_connect_send(struct sockaddr_un *sun) 195{ 196 ssize_t ssize; 197 char ch; 198 int s; 199 200 ANN(); 201 NEW_SOCKET(s); 202 if (connect(s, (struct sockaddr *)sun, sizeof(*sun)) < 0) 203 FAILERR("connect"); 204 ssize = send(s, &ch, sizeof(ch), 0); 205 if (ssize < 0) 206 FAILERR("send"); 207 if (ssize != sizeof(ch)) 208 FAILERRX("send wrong size"); 209 (void)close(s); 210 OK(); 211} 212 213static void 214test_connect_shutdown_send(struct sockaddr_un *sun) 215{ 216 ssize_t ssize; 217 char ch; 218 int s; 219 220 ANN(); 221 NEW_SOCKET(s); 222 if (connect(s, (struct sockaddr *)sun, sizeof(*sun)) < 0) 223 FAILERR("connect"); 224 if (shutdown(s, SHUT_RDWR) < 0) 225 FAILERR("shutdown SHUT_RDWR"); 226 ssize = send(s, &ch, sizeof(ch), 0); 227 if (ssize >= 0) 228 FAILERRX("send"); 229 if (errno != EPIPE) 230 FAILERR("send unexpected error"); 231 (void)close(s); 232 OK(); 233} 234 235static void 236test_connect_send_recv(struct sockaddr_un *sun, size_t size) 237{ 238 char buf[size + 4]; /* Detect extra bytes. */ 239 size_t truncsize; 240 ssize_t ssize; 241 int s; 242 243 ANNN(size); 244 NEW_SOCKET(s); 245 if (connect(s, (struct sockaddr *)sun, sizeof(*sun)) < 0) 246 FAILNERR("connect", size); 247 ssize = send(s, buf, size, 0); 248 if (ssize < 0 && size >= SEQPACKET_RCVBUF) 249 goto out; 250 if (ssize < 0) 251 FAILNERR("send", size); 252 if (ssize == 0) 253 FAILNERR("send eof", size); 254 if (ssize != size) 255 FAILNERRX("send size", size); 256 257 truncsize = min(size, BUFLEN); 258 ssize = recv(s, buf, sizeof(buf), 0); 259 if (ssize < 0) 260 FAILNERR("recv", size); 261 if (ssize == 0) 262 FAILNERRX("recv eof", size); 263 if (ssize < truncsize) 264 FAILNERRX("recv too few bytes", size); 265 if (ssize > truncsize) 266 FAILNERRX("recv too many bytes", size); 267out: 268 (void)close(s); 269 OKN(size); 270} 271 272static void 273test_connect_send_recv_count(struct sockaddr_un *sun, int count, size_t size) 274{ 275 char buf[size + 4]; /* Detect extra bytes and coalescing. */ 276 size_t truncsize; 277 ssize_t ssize; 278 int i, s; 279 280 ANNNM(size, count); 281 NEW_SOCKET(s); 282 if (connect(s, (struct sockaddr *)sun, sizeof(*sun)) < 0) 283 FAILNMERR("connect", size, count); 284 for (i = 0; i < count; i++) { 285 usleep(5000); 286 ssize = send(s, buf, size, 0); 287 if (ssize < 0 && size >= SEQPACKET_RCVBUF) 288 goto out; 289 if (ssize < 0) 290 FAILNMERR("send", size, count); 291 if (ssize == 0) 292 FAILNMERRX("send eof", size, count); 293 if (ssize != size) 294 FAILNMERRX("send size", size, count); 295 } 296 297 truncsize = min(size, BUFLEN); 298 for (i = 0; i < count; i++) { 299 ssize = recv(s, buf, sizeof(buf), 0); 300 if (ssize < 0) 301 FAILNMERR("recv", size, count); 302 if (ssize == 0) 303 FAILNMERRX("recv eof", size, count); 304 if (ssize < truncsize) 305 FAILNMERRX("recv too few bytes", size, count); 306 if (ssize > truncsize) 307 FAILNMERRX("recv too many bytes", size, count); 308 } 309out: 310 (void)close(s); 311 OKNM(size, count); 312} 313 314static void 315test_sendto(struct sockaddr_un *sun) 316{ 317 ssize_t ssize; 318 char ch; 319 int s; 320 321 ANN(); 322 NEW_SOCKET(s); 323 ssize = sendto(s, &ch, sizeof(ch), 0, (struct sockaddr *)sun, 324 sizeof(*sun)); 325 if (ssize < 0) 326 FAILERR("sendto"); 327 (void)close(s); 328 OK(); 329} 330 331static void 332client(struct sockaddr_un *sun) 333{ 334 size_t sizes[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 335 4096, 8192, 16384, 32768, 65536 /*, 131072 */}; 336 int c, i; 337 338 test_connect(sun); 339 test_connect_send(sun); 340 test_connect_shutdown_send(sun); 341 342 /* 343 * Try a range of sizes and packet counts. 344 */ 345 for (i = 0; i < sizeof(sizes) / sizeof(sizes[0]); i++) 346 test_connect_send_recv(sun, sizes[i]); 347 for (c = 1; c <= 8; c++) { 348 for (i = 0; i < sizeof(sizes) / sizeof(sizes[0]); i++) 349 test_connect_send_recv_count(sun, c, sizes[i]); 350 } 351 test_sendto(sun); 352 printf("client done\n"); 353} 354 355int 356main(int argc, char *argv[]) 357{ 358 struct sockaddr_un sun; 359 char path[PATH_MAX]; 360 pid_t pid_client, pid_server; 361 int i, s_listen; 362 363 snprintf(path, sizeof(path), "/tmp/lds_exercise.XXXXXXXXX"); 364 if (mktemp(path) == NULL) 365 FAILERR("mktemp"); 366 367 s_listen = socket(PF_LOCAL, SOCK_SEQPACKET, 0); 368 if (s_listen < 0) { 369 (void)unlink(path); 370 FAILERR("socket"); 371 } 372 373 i = SEQPACKET_RCVBUF; 374 if (setsockopt(s_listen, SOL_SOCKET, SO_RCVBUF, &i, sizeof(i)) < 0) { 375 (void)unlink(path); 376 FAILERR("setsockopt SO_RCVBUF"); 377 } 378 379 i = SEQPACKET_SNDBUF; 380 if (setsockopt(s_listen, SOL_SOCKET, SO_SNDBUF, &i, sizeof(i)) < 0) { 381 (void)unlink(path); 382 FAILERR("setsockopt SO_SNDBUF"); 383 } 384 385 i = 1; 386 if (setsockopt(s_listen, SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof(i)) 387 < 0) { 388 (void)unlink(path); 389 FAILERR("setsockopt SO_NOSIGPIPE"); 390 } 391 392 bzero(&sun, sizeof(sun)); 393 sun.sun_len = sizeof(sun); 394 sun.sun_family = AF_LOCAL; 395 strlcpy(sun.sun_path, path, sizeof(sun.sun_path)); 396 397 if (bind(s_listen, (struct sockaddr *)&sun, sizeof(sun)) < 0) { 398 (void)unlink(path); 399 FAILERR("bind"); 400 } 401 402 if (listen(s_listen, -1) < 0) { 403 (void)unlink(path); 404 FAILERR("listen"); 405 } 406 407 pid_server = fork(); 408 if (pid_server < 0) { 409 (void)unlink(path); 410 FAILERR("fork"); 411 } 412 if (pid_server == 0) { 413 server(s_listen); 414 return (0); 415 } 416 417 pid_client = fork(); 418 if (pid_client < 0) { 419 (void)kill(pid_server, SIGKILL); 420 (void)unlink(path); 421 FAILERR("fork"); 422 } 423 if (pid_client == 0) { 424 client(&sun); 425 return (0); 426 } 427 428 /* 429 * When the client is done, kill the server and clean up. 430 */ 431 (void)waitpid(pid_client, NULL, 0); 432 (void)kill(pid_server, SIGKILL); 433 (void)unlink(path); 434 return (0); 435} 436