1 /* $OpenBSD: relay_udp.c,v 1.49 2018/08/06 17:31:31 benno Exp $ */ 2 3 /* 4 * Copyright (c) 2007 - 2013 Reyk Floeter <reyk@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/queue.h> 21 #include <sys/time.h> 22 #include <sys/socket.h> 23 #include <sys/tree.h> 24 25 #include <netinet/in.h> 26 #include <arpa/inet.h> 27 28 #include <signal.h> 29 #include <errno.h> 30 #include <fcntl.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <unistd.h> 34 #include <stdio.h> 35 #include <event.h> 36 #include <imsg.h> 37 38 #include "relayd.h" 39 40 extern volatile sig_atomic_t relay_sessions; 41 extern objid_t relay_conid; 42 43 static struct relayd *env = NULL; 44 struct shuffle relay_shuffle; 45 46 int relay_udp_socket(struct sockaddr_storage *, in_port_t, 47 struct protocol *); 48 void relay_udp_request(struct rsession *); 49 void relay_udp_timeout(int, short, void *); 50 51 void relay_dns_log(struct rsession *, u_int8_t *, size_t); 52 void *relay_dns_validate(struct rsession *, 53 struct relay *, struct sockaddr_storage *, 54 u_int8_t *, size_t); 55 int relay_dns_request(struct rsession *); 56 void relay_udp_response(int, short, void *); 57 void relay_dns_result(struct rsession *, u_int8_t *, size_t); 58 int relay_dns_cmp(struct rsession *, struct rsession *); 59 60 void 61 relay_udp_privinit(struct relay *rlay) 62 { 63 if (rlay->rl_conf.flags & F_TLS) 64 fatalx("tls over udp is not supported"); 65 rlay->rl_conf.flags |= F_UDP; 66 } 67 68 void 69 relay_udp_init(struct relayd *x_env, struct relay *rlay) 70 { 71 struct protocol *proto = rlay->rl_proto; 72 73 if (env == NULL) 74 env = x_env; 75 76 switch (proto->type) { 77 case RELAY_PROTO_DNS: 78 proto->validate = relay_dns_validate; 79 proto->request = relay_dns_request; 80 proto->cmp = relay_dns_cmp; 81 shuffle_init(&relay_shuffle); 82 break; 83 default: 84 fatalx("unsupported udp protocol"); 85 break; 86 } 87 } 88 89 int 90 relay_udp_bind(struct sockaddr_storage *ss, in_port_t port, 91 struct protocol *proto) 92 { 93 int s; 94 95 if ((s = relay_udp_socket(ss, port, proto)) == -1) 96 return (-1); 97 98 if (bind(s, (struct sockaddr *)ss, ss->ss_len) == -1) 99 goto bad; 100 101 return (s); 102 103 bad: 104 close(s); 105 return (-1); 106 } 107 108 int 109 relay_udp_socket(struct sockaddr_storage *ss, in_port_t port, 110 struct protocol *proto) 111 { 112 int s = -1, val; 113 114 if (relay_socket_af(ss, port) == -1) 115 goto bad; 116 117 if ((s = socket(ss->ss_family, SOCK_DGRAM | SOCK_NONBLOCK, 118 IPPROTO_UDP)) == -1) 119 goto bad; 120 121 /* 122 * Socket options 123 */ 124 if (proto->tcpflags & TCPFLAG_BUFSIZ) { 125 val = proto->tcpbufsiz; 126 if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, 127 &val, sizeof(val)) == -1) 128 goto bad; 129 val = proto->tcpbufsiz; 130 if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, 131 &val, sizeof(val)) == -1) 132 goto bad; 133 } 134 135 /* 136 * IP options 137 */ 138 if (proto->tcpflags & TCPFLAG_IPTTL) { 139 val = (int)proto->tcpipttl; 140 switch (ss->ss_family) { 141 case AF_INET: 142 if (setsockopt(s, IPPROTO_IP, IP_TTL, 143 &val, sizeof(val)) == -1) 144 goto bad; 145 break; 146 case AF_INET6: 147 if (setsockopt(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS, 148 &val, sizeof(val)) == -1) 149 goto bad; 150 break; 151 } 152 } 153 if (proto->tcpflags & TCPFLAG_IPMINTTL) { 154 val = (int)proto->tcpipminttl; 155 switch (ss->ss_family) { 156 case AF_INET: 157 if (setsockopt(s, IPPROTO_IP, IP_MINTTL, 158 &val, sizeof(val)) == -1) 159 goto bad; 160 break; 161 case AF_INET6: 162 if (setsockopt(s, IPPROTO_IPV6, IPV6_MINHOPCOUNT, 163 &val, sizeof(val)) == -1) 164 goto bad; 165 break; 166 } 167 } 168 169 return (s); 170 171 bad: 172 if (s != -1) 173 close(s); 174 return (-1); 175 } 176 177 void 178 relay_udp_response(int fd, short sig, void *arg) 179 { 180 struct rsession *con = arg; 181 struct relay *rlay = con->se_relay; 182 struct protocol *proto = rlay->rl_proto; 183 void *priv = NULL; 184 struct sockaddr_storage ss; 185 u_int8_t buf[IBUF_READ_SIZE]; 186 ssize_t len; 187 socklen_t slen; 188 189 if (sig == EV_TIMEOUT) { 190 relay_udp_timeout(fd, sig, arg); 191 return; 192 } 193 194 if (rlay->rl_conf.flags & F_DISABLE) 195 return; 196 197 slen = sizeof(ss); 198 if ((len = recvfrom(fd, buf, sizeof(buf), 0, 199 (struct sockaddr*)&ss, &slen)) < 1) 200 return; 201 202 /* Parse and validate the packet header */ 203 if (proto->validate != NULL && 204 (priv = (*proto->validate)(con, rlay, &ss, buf, len)) == NULL) 205 return; 206 207 relay_close(con, "unknown response", 1); 208 free(priv); 209 } 210 211 void 212 relay_udp_server(int fd, short sig, void *arg) 213 { 214 struct privsep *ps = env->sc_ps; 215 struct relay *rlay = arg; 216 struct protocol *proto = rlay->rl_proto; 217 struct rsession *con = NULL; 218 struct ctl_natlook *cnl = NULL; 219 socklen_t slen; 220 struct timeval tv; 221 struct sockaddr_storage ss; 222 u_int8_t buf[IBUF_READ_SIZE]; 223 void *priv = NULL; 224 ssize_t len; 225 226 event_add(&rlay->rl_ev, NULL); 227 228 if (rlay->rl_conf.flags & F_DISABLE) 229 return; 230 231 slen = sizeof(ss); 232 if ((len = recvfrom(fd, buf, sizeof(buf), 0, 233 (struct sockaddr*)&ss, &slen)) < 1) 234 return; 235 236 if (proto->validate != NULL && 237 (priv = (*proto->validate)(NULL, rlay, &ss, buf, len)) == NULL) 238 return; 239 240 if ((con = calloc(1, sizeof(*con))) == NULL) { 241 free(priv); 242 return; 243 } 244 245 /* 246 * Replace the DNS request Id with a random Id. 247 */ 248 con->se_priv = priv; 249 con->se_in.s = -1; 250 con->se_out.s = -1; 251 con->se_in.dst = &con->se_out; 252 con->se_out.dst = &con->se_in; 253 con->se_in.con = con; 254 con->se_out.con = con; 255 con->se_relay = rlay; 256 con->se_id = ++relay_conid; 257 con->se_in.dir = RELAY_DIR_REQUEST; 258 con->se_out.dir = RELAY_DIR_RESPONSE; 259 con->se_retry = rlay->rl_conf.dstretry; 260 con->se_out.port = rlay->rl_conf.dstport; 261 switch (ss.ss_family) { 262 case AF_INET: 263 con->se_in.port = ((struct sockaddr_in *)&ss)->sin_port; 264 break; 265 case AF_INET6: 266 con->se_in.port = ((struct sockaddr_in6 *)&ss)->sin6_port; 267 break; 268 } 269 bcopy(&ss, &con->se_in.ss, sizeof(con->se_in.ss)); 270 271 getmonotime(&con->se_tv_start); 272 bcopy(&con->se_tv_start, &con->se_tv_last, sizeof(con->se_tv_last)); 273 274 relay_sessions++; 275 SPLAY_INSERT(session_tree, &rlay->rl_sessions, con); 276 relay_session_publish(con); 277 278 /* Increment the per-relay session counter */ 279 rlay->rl_stats[ps->ps_instance].last++; 280 281 /* Pre-allocate output buffer */ 282 con->se_out.output = evbuffer_new(); 283 if (con->se_out.output == NULL) { 284 relay_close(con, "failed to allocate output buffer", 1); 285 return; 286 } 287 288 /* Pre-allocate log buffer */ 289 con->se_haslog = 0; 290 con->se_log = evbuffer_new(); 291 if (con->se_log == NULL) { 292 relay_close(con, "failed to allocate log buffer", 1); 293 return; 294 } 295 296 if (rlay->rl_conf.flags & F_NATLOOK) { 297 if ((cnl = calloc(1, sizeof(*cnl))) == NULL) { 298 relay_close(con, "failed to allocate natlookup", 1); 299 return; 300 } 301 } 302 303 /* Save the received data */ 304 if (evbuffer_add(con->se_out.output, buf, len) == -1) { 305 relay_close(con, "failed to store buffer", 1); 306 free(cnl); 307 return; 308 } 309 310 if (cnl != NULL) { 311 con->se_cnl = cnl; 312 bzero(cnl, sizeof(*cnl)); 313 cnl->in = -1; 314 cnl->id = con->se_id; 315 cnl->proc = ps->ps_instance; 316 cnl->proto = IPPROTO_UDP; 317 bcopy(&con->se_in.ss, &cnl->src, sizeof(cnl->src)); 318 bcopy(&rlay->rl_conf.ss, &cnl->dst, sizeof(cnl->dst)); 319 proc_compose(env->sc_ps, PROC_PFE, 320 IMSG_NATLOOK, cnl, sizeof(*cnl)); 321 322 /* Schedule timeout */ 323 evtimer_set(&con->se_ev, relay_natlook, con); 324 bcopy(&rlay->rl_conf.timeout, &tv, sizeof(tv)); 325 evtimer_add(&con->se_ev, &tv); 326 return; 327 } 328 329 relay_session(con); 330 } 331 332 void 333 relay_udp_timeout(int fd, short sig, void *arg) 334 { 335 struct rsession *con = arg; 336 337 if (sig != EV_TIMEOUT) 338 fatalx("invalid timeout event"); 339 340 relay_close(con, "udp timeout", 1); 341 } 342 343 /* 344 * Domain Name System support 345 */ 346 347 struct relay_dns_priv { 348 u_int16_t dp_inkey; 349 u_int16_t dp_outkey; 350 }; 351 352 struct relay_dnshdr { 353 u_int16_t dns_id; 354 355 u_int8_t dns_flags0; 356 #define DNS_F0_QR 0x80 /* response flag */ 357 #define DNS_F0_OPCODE 0x78 /* message type */ 358 #define DNS_F0_AA 0x04 /* authorative answer */ 359 #define DNS_F0_TC 0x02 /* truncated message */ 360 #define DNS_F0_RD 0x01 /* recursion desired */ 361 362 u_int8_t dns_flags1; 363 #define DNS_F1_RA 0x80 /* recursion available */ 364 #define DNS_F1_RES 0x40 /* reserved */ 365 #define DNS_F1_AD 0x20 /* authentic data */ 366 #define DNS_F1_CD 0x10 /* checking disabled */ 367 #define DNS_F1_RCODE 0x0f /* response code */ 368 369 u_int16_t dns_qdcount; 370 u_int16_t dns_ancount; 371 u_int16_t dns_nscount; 372 u_int16_t dns_arcount; 373 } __packed; 374 375 void 376 relay_dns_log(struct rsession *con, u_int8_t *buf, size_t len) 377 { 378 struct relay_dnshdr *hdr = (struct relay_dnshdr *)buf; 379 380 /* Validate the header length */ 381 if (len < sizeof(*hdr)) { 382 log_debug("%s: session %d: short dns packet", __func__, 383 con->se_id); 384 return; 385 } 386 387 log_debug("%s: session %d: %s id 0x%x " 388 "flags 0x%x:0x%x qd %u an %u ns %u ar %u", __func__, 389 con->se_id, 390 hdr->dns_flags0 & DNS_F0_QR ? "response" : "request", 391 ntohs(hdr->dns_id), 392 hdr->dns_flags0, 393 hdr->dns_flags1, 394 ntohs(hdr->dns_qdcount), 395 ntohs(hdr->dns_ancount), 396 ntohs(hdr->dns_nscount), 397 ntohs(hdr->dns_arcount)); 398 } 399 400 void * 401 relay_dns_validate(struct rsession *con, struct relay *rlay, 402 struct sockaddr_storage *ss, u_int8_t *buf, size_t len) 403 { 404 struct relay_dnshdr *hdr = (struct relay_dnshdr *)buf; 405 struct rsession lookup; 406 u_int16_t key; 407 struct relay_dns_priv *priv, lpriv; 408 409 /* Validate the header length */ 410 if (len < sizeof(*hdr)) 411 return (NULL); 412 413 key = ntohs(hdr->dns_id); 414 415 /* 416 * Check if the header has the response flag set, otherwise 417 * return 0 to tell the UDP server to create a new session. 418 */ 419 if ((hdr->dns_flags0 & DNS_F0_QR) == 0) { 420 priv = malloc(sizeof(struct relay_dns_priv)); 421 if (priv == NULL) 422 return (NULL); 423 priv->dp_inkey = shuffle_generate16(&relay_shuffle); 424 priv->dp_outkey = key; 425 return ((void *)priv); 426 } 427 428 /* 429 * Lookup if this response is for a known session and if the 430 * remote host matches the original destination of the request. 431 */ 432 if (con == NULL) { 433 lpriv.dp_inkey = key; 434 lookup.se_priv = &lpriv; 435 if ((con = SPLAY_FIND(session_tree, 436 &rlay->rl_sessions, &lookup)) != NULL && 437 con->se_priv != NULL && 438 relay_cmp_af(ss, &con->se_out.ss) == 0) 439 relay_dns_result(con, buf, len); 440 } else { 441 priv = con->se_priv; 442 if (priv == NULL || key != priv->dp_inkey) { 443 relay_close(con, "invalid response", 1); 444 return (NULL); 445 } 446 relay_dns_result(con, buf, len); 447 } 448 449 /* 450 * This is not a new session, ignore it in the UDP server. 451 */ 452 return (NULL); 453 } 454 455 int 456 relay_dns_request(struct rsession *con) 457 { 458 struct relay *rlay = con->se_relay; 459 struct relay_dns_priv *priv = con->se_priv; 460 u_int8_t *buf = EVBUFFER_DATA(con->se_out.output); 461 size_t len = EVBUFFER_LENGTH(con->se_out.output); 462 struct relay_dnshdr *hdr; 463 socklen_t slen; 464 465 if (buf == NULL || priv == NULL || len < 1) 466 return (-1); 467 if (log_getverbose() > 1) 468 relay_dns_log(con, buf, len); 469 470 getmonotime(&con->se_tv_start); 471 472 if (!TAILQ_EMPTY(&rlay->rl_tables)) { 473 if (relay_from_table(con) != 0) 474 return (-1); 475 } else if (con->se_out.ss.ss_family == AF_UNSPEC) { 476 bcopy(&rlay->rl_conf.dstss, &con->se_out.ss, 477 sizeof(con->se_out.ss)); 478 con->se_out.port = rlay->rl_conf.dstport; 479 } 480 481 if ((con->se_out.s = relay_udp_socket(&con->se_out.ss, 482 con->se_out.port, rlay->rl_proto)) == -1) 483 return (-1); 484 slen = con->se_out.ss.ss_len; 485 486 hdr = (struct relay_dnshdr *)buf; 487 hdr->dns_id = htons(priv->dp_inkey); 488 489 retry: 490 if (sendto(con->se_out.s, buf, len, 0, 491 (struct sockaddr *)&con->se_out.ss, slen) == -1) { 492 if (con->se_retry) { 493 con->se_retry--; 494 log_debug("%s: session %d: " 495 "forward failed: %s, %s", __func__, 496 con->se_id, strerror(errno), 497 con->se_retry ? "next retry" : "last retry"); 498 goto retry; 499 } 500 log_debug("%s: session %d: forward failed: %s", __func__, 501 con->se_id, strerror(errno)); 502 return (-1); 503 } 504 505 event_again(&con->se_ev, con->se_out.s, EV_TIMEOUT|EV_READ, 506 relay_udp_response, &con->se_tv_start, &rlay->rl_conf.timeout, con); 507 508 return (0); 509 } 510 511 void 512 relay_dns_result(struct rsession *con, u_int8_t *buf, size_t len) 513 { 514 struct relay *rlay = con->se_relay; 515 struct relay_dns_priv *priv = con->se_priv; 516 struct relay_dnshdr *hdr; 517 socklen_t slen; 518 519 if (priv == NULL) 520 fatalx("%s: response to invalid session", __func__); 521 522 if (log_getverbose() > 1) 523 relay_dns_log(con, buf, len); 524 525 /* 526 * Replace the random DNS request Id with the original Id 527 */ 528 hdr = (struct relay_dnshdr *)buf; 529 hdr->dns_id = htons(priv->dp_outkey); 530 531 slen = con->se_out.ss.ss_len; 532 if (sendto(rlay->rl_s, buf, len, 0, 533 (struct sockaddr *)&con->se_in.ss, slen) == -1) { 534 relay_close(con, "response failed", 1); 535 return; 536 } 537 538 relay_close(con, "session closed", 0); 539 } 540 541 int 542 relay_dns_cmp(struct rsession *a, struct rsession *b) 543 { 544 struct relay_dns_priv *ap = a->se_priv; 545 struct relay_dns_priv *bp = b->se_priv; 546 547 if (ap == NULL || bp == NULL) 548 fatalx("%s: invalid session", __func__); 549 550 return (memcmp(&ap->dp_inkey, &bp->dp_inkey, sizeof(u_int16_t))); 551 } 552