1 /* $OpenBSD: relay.c,v 1.2 2014/01/08 23:32:17 bluhm Exp $ */ 2 /* 3 * Copyright (c) 2013 Alexander Bluhm <bluhm@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 /* 19 * Accept tcp or udp from client and connect to server. 20 * Then copy or splice data from client to server. 21 */ 22 23 #include <sys/types.h> 24 #include <sys/socket.h> 25 #include <sys/time.h> 26 27 #include <netinet/in.h> 28 #include <netinet/tcp.h> 29 30 #include <errno.h> 31 #include <err.h> 32 #include <fcntl.h> 33 #include <netdb.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <unistd.h> 38 39 #define BUFSIZE (1<<16) 40 41 __dead void usage(void); 42 void relay_copy(int, int); 43 void relay_splice(int, int); 44 int socket_listen(int *, struct addrinfo *, const char *, 45 const char *); 46 int listen_select(const int *, int); 47 int socket_accept(int); 48 int socket_connect(struct addrinfo *, const char *, const char *); 49 50 __dead void 51 usage(void) 52 { 53 fprintf(stderr, 54 "usage: relay copy|splice [-46tu] [-b bindaddress] listenport " 55 "hostname port\n" 56 " copy [-46tu] [-b bindaddress] listenport hostname port\n" 57 " splice [-46tu] [-b bindaddress] listenport hostname port\n" 58 " -4 IPv4 only\n" 59 " -6 IPv6 only\n" 60 " -b bindaddress bind listen socket to address\n" 61 " -t TCP (default)\n" 62 " -u UDP\n" 63 ); 64 exit(1); 65 } 66 67 void 68 relay_copy(int fdin, int fdout) 69 { 70 char buf[BUFSIZE]; 71 off_t len; 72 size_t off; 73 ssize_t nr, nw; 74 75 printf("copy...\n"); 76 len = 0; 77 while (1) { 78 nr = read(fdin, buf, sizeof(buf)); 79 if (nr == -1) 80 err(1, "read"); 81 if (nr == 0) 82 break; 83 len += nr; 84 off = 0; 85 do { 86 nw = write(fdout, buf + off, nr); 87 if (nw == -1) 88 err(1, "write"); 89 off += nw; 90 nr -= nw; 91 } while (nr); 92 } 93 printf("len %lld\n", len); 94 } 95 96 void 97 relay_splice(int fdin, int fdout) 98 { 99 fd_set fdset; 100 socklen_t optlen; 101 off_t len; 102 int error; 103 104 printf("splice...\n"); 105 if (setsockopt(fdin, SOL_SOCKET, SO_SPLICE, &fdout, sizeof(int)) == -1) 106 err(1, "setsockopt splice"); 107 FD_ZERO(&fdset); 108 FD_SET(fdin, &fdset); 109 if (select(fdin+1, &fdset, NULL, NULL, NULL) == -1) 110 err(1, "select"); 111 optlen = sizeof(error); 112 if (getsockopt(fdin, SOL_SOCKET, SO_ERROR, &error, &optlen) == -1) 113 err(1, "getsockopt error"); 114 if (error) 115 printf("error %s\n", strerror(error)); 116 optlen = sizeof(len); 117 if (getsockopt(fdin, SOL_SOCKET, SO_SPLICE, &len, &optlen) == -1) 118 err(1, "getsockopt splice"); 119 printf("len %lld\n", len); 120 } 121 122 int 123 socket_listen(int *ls, struct addrinfo *hints, const char *listenaddr, 124 const char *listenport) 125 { 126 char host[NI_MAXHOST], serv[NI_MAXSERV]; 127 struct sockaddr_storage sa; 128 socklen_t salen; 129 struct addrinfo *res, *res0; 130 const char *cause = NULL; 131 int optval, error, save_errno, nls; 132 133 hints->ai_flags = AI_PASSIVE; 134 error = getaddrinfo(listenaddr, listenport, hints, &res0); 135 if (error) 136 errx(1, "getaddrinfo %s %s: %s", listenaddr == NULL ? "*" : 137 listenaddr, listenport, gai_strerror(error)); 138 for (res = res0, nls = 0; res && nls < FD_SETSIZE; res = res->ai_next) { 139 ls[nls] = socket(res->ai_family, res->ai_socktype, 140 res->ai_protocol); 141 if (ls[nls] == -1) { 142 cause = "listen socket"; 143 continue; 144 } 145 optval = 100000; 146 if (setsockopt(ls[nls], SOL_SOCKET, SO_RCVBUF, 147 &optval, sizeof(optval)) == -1) 148 err(1, "setsockopt rcvbuf"); 149 optval = 1; 150 if (setsockopt(ls[nls], SOL_SOCKET, SO_REUSEADDR, 151 &optval, sizeof(optval)) == -1) 152 err(1, "setsockopt reuseaddr"); 153 if (bind(ls[nls], res->ai_addr, res->ai_addrlen) == -1) { 154 cause = "bind"; 155 save_errno = errno; 156 close(ls[nls]); 157 errno = save_errno; 158 continue; 159 } 160 if (hints->ai_socktype == SOCK_STREAM) { 161 if (listen(ls[nls], 5) == -1) 162 err(1, "listen"); 163 } 164 salen = sizeof(sa); 165 if (getsockname(ls[nls], (struct sockaddr *)&sa, &salen) == -1) 166 err(1, "listen getsockname"); 167 error = getnameinfo((struct sockaddr *)&sa, salen, 168 host, sizeof(host), serv, sizeof(serv), 169 NI_NUMERICHOST|NI_NUMERICSERV); 170 if (error) 171 errx(1, "listen getnameinfo: %s", gai_strerror(error)); 172 printf("listen %s %s\n", host, serv); 173 nls++; 174 } 175 if (nls == 0) 176 err(1, "%s", cause); 177 freeaddrinfo(res0); 178 179 return nls; 180 } 181 182 int 183 listen_select(const int *ls, int nls) 184 { 185 fd_set fdset; 186 int i, mfd; 187 188 FD_ZERO(&fdset); 189 mfd = 0; 190 for (i = 0; i < nls; i++) { 191 FD_SET(ls[i], &fdset); 192 if (ls[i] > mfd) 193 mfd = ls[i]; 194 } 195 if (select(mfd+1, &fdset, NULL, NULL, NULL) == -1) 196 err(1, "select"); 197 for (i = 0; i < nls; i++) { 198 if (FD_ISSET(ls[i], &fdset)) 199 break; 200 } 201 if (i == nls) 202 errx(1, "select: no fd set"); 203 return ls[i]; 204 } 205 206 int 207 socket_accept(int ls) 208 { 209 char host[NI_MAXHOST], serv[NI_MAXSERV]; 210 struct sockaddr_storage sa; 211 socklen_t salen; 212 int error, as; 213 214 salen = sizeof(sa); 215 as = accept(ls, (struct sockaddr *)&sa, &salen); 216 if (as == -1) 217 err(1, "accept"); 218 error = getnameinfo((struct sockaddr *)&sa, salen, 219 host, sizeof(host), serv, sizeof(serv), 220 NI_NUMERICHOST|NI_NUMERICSERV); 221 if (error) 222 errx(1, "accept getnameinfo: %s", gai_strerror(error)); 223 printf("accept %s %s\n", host, serv); 224 225 return as; 226 } 227 228 int 229 socket_connect(struct addrinfo *hints, const char *hostname, const char *port) 230 { 231 char host[NI_MAXHOST], serv[NI_MAXSERV]; 232 struct sockaddr_storage sa; 233 socklen_t salen; 234 struct addrinfo *res, *res0; 235 const char *cause = NULL; 236 int optval, error, save_errno, cs; 237 238 hints->ai_flags = 0; 239 error = getaddrinfo(hostname, port, hints, &res0); 240 if (error) 241 errx(1, "getaddrinfo %s %s: %s", hostname, port, 242 gai_strerror(error)); 243 cs = -1; 244 for (res = res0; res; res = res->ai_next) { 245 cs = socket(res->ai_family, res->ai_socktype, 246 res->ai_protocol); 247 if (cs == -1) { 248 cause = "connect socket"; 249 continue; 250 } 251 optval = 100000; 252 if (setsockopt(cs, SOL_SOCKET, SO_SNDBUF, 253 &optval, sizeof(optval)) == -1) 254 err(1, "setsockopt sndbuf"); 255 if (connect(cs, res->ai_addr, res->ai_addrlen) == -1) { 256 cause = "connect"; 257 save_errno = errno; 258 close(cs); 259 errno = save_errno; 260 cs = -1; 261 continue; 262 } 263 break; 264 } 265 if (cs == -1) 266 err(1, "%s", cause); 267 salen = sizeof(sa); 268 if (getpeername(cs, (struct sockaddr *)&sa, &salen) == -1) 269 err(1, "connect getpeername"); 270 error = getnameinfo((struct sockaddr *)&sa, salen, 271 host, sizeof(host), serv, sizeof(serv), 272 NI_NUMERICHOST|NI_NUMERICSERV); 273 if (error) 274 errx(1, "connect getnameinfo: %s", gai_strerror(error)); 275 printf("connect %s %s\n", host, serv); 276 freeaddrinfo(res0); 277 278 return cs; 279 } 280 281 int 282 main(int argc, char *argv[]) 283 { 284 struct addrinfo hints; 285 int ch, ls[FD_SETSIZE], nls, as, cs, optval; 286 const char *listenaddr, *listenport, *hostname, *port; 287 const char *relayname; 288 void (*relayfunc)(int, int); 289 290 relayname = strrchr(argv[0], '/'); 291 relayname = relayname ? relayname + 1 : argv[0]; 292 if (strcmp(relayname, "copy") == 0) 293 relayfunc = relay_copy; 294 else if (strcmp(relayname, "splice") == 0) 295 relayfunc = relay_splice; 296 else { 297 argc--; 298 argv++; 299 if (argv[0] == NULL) 300 usage(); 301 relayname = argv[0]; 302 if (strcmp(relayname, "copy") == 0) 303 relayfunc = relay_copy; 304 else if (strcmp(relayname, "splice") == 0) 305 relayfunc = relay_splice; 306 else 307 usage(); 308 } 309 310 memset(&hints, 0, sizeof(hints)); 311 hints.ai_family = PF_UNSPEC; 312 hints.ai_socktype = SOCK_STREAM; 313 listenaddr = NULL; 314 while ((ch = getopt(argc, argv, "46b:tu")) != -1) { 315 switch (ch) { 316 case '4': 317 hints.ai_family = PF_INET; 318 break; 319 case '6': 320 hints.ai_family = PF_INET6; 321 break; 322 case 'b': 323 listenaddr = optarg; 324 break; 325 case 't': 326 hints.ai_socktype = SOCK_STREAM; 327 break; 328 case 'u': 329 hints.ai_socktype = SOCK_DGRAM; 330 break; 331 default: 332 usage(); 333 } 334 } 335 argc -= optind; 336 argv += optind; 337 if (argc != 3) 338 usage(); 339 listenport = argv[0]; 340 hostname = argv[1]; 341 port = argv[2]; 342 343 nls = socket_listen(ls, &hints, listenaddr, listenport); 344 345 while (1) { 346 if (hints.ai_socktype == SOCK_STREAM) { 347 as = socket_accept(listen_select(ls, nls)); 348 cs = socket_connect(&hints, hostname, port); 349 optval = 1; 350 if (setsockopt(cs, IPPROTO_TCP, TCP_NODELAY, 351 &optval, sizeof(optval)) == -1) 352 err(1, "setsockopt nodelay"); 353 } else { 354 cs = socket_connect(&hints, hostname, port); 355 as = listen_select(ls, nls); 356 } 357 358 relayfunc(as, cs); 359 360 if (close(cs) == -1) 361 err(1, "connect close"); 362 if (hints.ai_socktype == SOCK_STREAM) { 363 if (close(as) == -1) 364 err(1, "accept close"); 365 } 366 printf("close\n"); 367 } 368 } 369