1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2020 Netflix, Inc.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <sys/sysctl.h>
31 #include <sys/uio.h>
32
33 #include <netinet/in.h>
34 #include <netdb.h>
35
36 #include <err.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <pthread.h>
40 #include <stdbool.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45
46 static char buf[1024*1024];
47 static ssize_t readlen;
48 static volatile bool read_done = false;
49
50 static int
tcp_socketpair(int * sv)51 tcp_socketpair(int *sv)
52 {
53 struct sockaddr_in sin = {
54 .sin_len = sizeof(struct sockaddr_in),
55 .sin_family = AF_INET,
56 .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
57 };
58 int flags;
59 int ls;
60
61 ls = socket(PF_INET, SOCK_STREAM, 0);
62 if (ls < 0)
63 err(1, "socket ls");
64
65 if (setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, &(socklen_t){1},
66 sizeof(int)) < 0)
67 err(1, "SO_REUSEADDR");
68
69 if (bind(ls, (struct sockaddr *)&sin, sizeof(sin)) < 0)
70 err(1, "bind ls");
71
72 if (getsockname(ls, (struct sockaddr *)&sin,
73 &(socklen_t){ sizeof(sin) }) < 0)
74 err(1, "getsockname");
75
76 if (listen(ls, 5) < 0)
77 err(1, "listen ls");
78
79 sv[0] = socket(PF_INET, SOCK_STREAM, 0);
80 if (sv[0] < 0)
81 err(1, "socket cs");
82
83 flags = fcntl(sv[0], F_GETFL);
84 flags |= O_NONBLOCK;
85 if (fcntl(sv[0], F_SETFL, flags) == -1)
86 err(1, "fcntl +O_NONBLOCK");
87
88 if (connect(sv[0], (void *)&sin, sizeof(sin)) == -1 &&
89 errno != EINPROGRESS)
90 err(1, "connect cs");
91
92 sv[1] = accept(ls, NULL, 0);
93 if (sv[1] < 0)
94 err(1, "accept ls");
95
96 flags &= ~O_NONBLOCK;
97 if (fcntl(sv[0], F_SETFL, flags) == -1)
98 err(1, "fcntl -O_NONBLOCK");
99
100 close(ls);
101
102 return (0);
103 }
104
105 static int
tcp_client_socket(const char * host,const char * port)106 tcp_client_socket(const char *host, const char *port)
107 {
108 struct addrinfo hints, *res, *res0;
109 int error;
110 int s;
111
112 memset(&hints, 0, sizeof(hints));
113 hints.ai_family = AF_UNSPEC;
114 hints.ai_socktype = SOCK_STREAM;
115 if ((error = getaddrinfo(host, port, &hints, &res0)) != 0)
116 errx(1, "host %s port %s: %s.",
117 host, port, gai_strerror(error));
118 s = -1;
119 for (res = res0; res != NULL; res = res->ai_next) {
120 s = socket(res->ai_family, res->ai_socktype,
121 res->ai_protocol);
122 if (s < 0) {
123 warn("socket(pf:%d, type:%d, proto:%d)",
124 res->ai_family, res->ai_socktype,
125 res->ai_protocol);
126 continue;
127 }
128 if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
129 warn("connect(%s, %s)", host, port);
130 close(s);
131 s = -1;
132 continue;
133 } else
134 break;
135 }
136 if (s < 0)
137 exit(1);
138 freeaddrinfo(res0);
139
140 return s;
141 }
142
143 static void *
receiver(void * arg)144 receiver(void *arg)
145 {
146 int s = *(int *)arg;
147 ssize_t rv;
148
149 do {
150 rv = read(s, buf, sizeof(buf));
151 if (rv == -1)
152 err(2, "read receiver");
153 if (rv == 0)
154 break;
155 readlen -= rv;
156 } while (readlen != 0);
157
158 read_done = true;
159
160 return NULL;
161 }
162
163 static void
usage(void)164 usage(void)
165 {
166 errx(1, "usage: %s [-u] [-c host] [-p port] "
167 "<file> <start> <len> <flags>", getprogname());
168 }
169
170 int
main(int argc,char ** argv)171 main(int argc, char **argv)
172 {
173 pthread_t pt;
174 off_t start;
175 int ch, fd, ss[2], flags, error;
176 bool pf_unix = false;
177 bool tcp_client = false;
178 const char *host, *port;
179
180 while ((ch = getopt(argc, argv, "c:p:u")) != -1)
181 switch (ch) {
182 case 'c':
183 host = optarg;
184 tcp_client = true;
185 break;
186 case 'p':
187 port = optarg;
188 tcp_client = true;
189 break;
190 case 'u':
191 pf_unix = true;
192 break;
193 default:
194 usage();
195 }
196 argc -= optind;
197 argv += optind;
198
199 if (argc != 4)
200 usage();
201 if (tcp_client && (host == NULL || port == NULL))
202 errx(1, "Need to specify host and port.");
203
204 start = strtoull(argv[1], NULL, 0);
205 readlen = strtoull(argv[2], NULL, 0);
206 flags = strtoul(argv[3], NULL, 0);
207
208 fd = open(argv[0], O_RDONLY);
209 if (fd < 0)
210 err(1, "open");
211
212 if (pf_unix) {
213 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, ss) != 0)
214 err(1, "socketpair");
215 } else if (tcp_client)
216 ss[0] = tcp_client_socket(host, port);
217 else
218 tcp_socketpair(ss);
219
220 if (tcp_client)
221 read_done = true; /* The receiver is another process. */
222 else {
223 error = pthread_create(&pt, NULL, receiver, &ss[1]);
224 if (error)
225 errc(1, error, "pthread_create");
226 }
227
228 if (sendfile(fd, ss[0], start, readlen, NULL, NULL, flags) < 0)
229 err(3, "sendfile");
230
231 while (!read_done)
232 usleep(1000);
233
234 exit(0);
235 }
236